mirror of
https://github.com/formbricks/formbricks.git
synced 2026-05-06 02:46:46 -05:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8374eea770 | |||
| d3ccc623e0 | |||
| 60bd5cbeff | |||
| b6a3a15379 | |||
| c68f214eff | |||
| c90ee84483 | |||
| dc1ee72594 | |||
| 924132287e | |||
| e6f347aa07 | |||
| 367bc23dd4 | |||
| a1a11b2bb8 | |||
| 0653c6a59f | |||
| b6d793e109 | |||
| 8619916682 |
@@ -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 (
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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({
|
||||
|
||||
+31
-16
@@ -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
|
||||
@@ -1238,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
|
||||
@@ -1335,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
|
||||
@@ -1347,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
|
||||
@@ -1371,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
|
||||
@@ -1390,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
|
||||
@@ -1526,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
|
||||
@@ -1538,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
|
||||
@@ -1565,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
|
||||
@@ -1654,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
|
||||
@@ -1685,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
|
||||
@@ -1693,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
|
||||
@@ -1768,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
|
||||
@@ -2112,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
|
||||
@@ -2123,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
|
||||
@@ -2181,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
|
||||
@@ -2793,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
|
||||
|
||||
@@ -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
|
||||
|
||||
+31
-16
@@ -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",
|
||||
@@ -1309,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",
|
||||
@@ -1406,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.",
|
||||
@@ -1418,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",
|
||||
@@ -1442,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",
|
||||
@@ -1461,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.",
|
||||
@@ -1598,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",
|
||||
@@ -1609,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.",
|
||||
@@ -1636,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",
|
||||
@@ -1727,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",
|
||||
@@ -1758,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",
|
||||
@@ -1766,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",
|
||||
@@ -1843,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)",
|
||||
@@ -2227,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",
|
||||
@@ -2237,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",
|
||||
@@ -2298,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",
|
||||
@@ -2948,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.",
|
||||
|
||||
+32
-17
@@ -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",
|
||||
@@ -1309,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",
|
||||
@@ -1406,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.",
|
||||
@@ -1418,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",
|
||||
@@ -1442,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",
|
||||
@@ -1461,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.",
|
||||
@@ -1597,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",
|
||||
@@ -1609,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.",
|
||||
@@ -1636,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",
|
||||
@@ -1727,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",
|
||||
@@ -1758,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",
|
||||
@@ -1766,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",
|
||||
@@ -1843,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)",
|
||||
@@ -2227,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",
|
||||
@@ -2237,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",
|
||||
@@ -2298,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",
|
||||
@@ -2948,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.",
|
||||
|
||||
+31
-16
@@ -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",
|
||||
@@ -1309,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",
|
||||
@@ -1406,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.",
|
||||
@@ -1418,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",
|
||||
@@ -1442,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",
|
||||
@@ -1461,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.",
|
||||
@@ -1598,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",
|
||||
@@ -1609,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.",
|
||||
@@ -1636,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",
|
||||
@@ -1727,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",
|
||||
@@ -1758,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",
|
||||
@@ -1766,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",
|
||||
@@ -1843,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)",
|
||||
@@ -2227,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",
|
||||
@@ -2237,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",
|
||||
@@ -2298,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",
|
||||
@@ -2948,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.",
|
||||
|
||||
+31
-16
@@ -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",
|
||||
@@ -1309,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.",
|
||||
@@ -1406,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.",
|
||||
@@ -1418,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",
|
||||
@@ -1442,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",
|
||||
@@ -1461,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.",
|
||||
@@ -1598,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",
|
||||
@@ -1609,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.",
|
||||
@@ -1636,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",
|
||||
@@ -1727,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",
|
||||
@@ -1758,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",
|
||||
@@ -1766,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",
|
||||
@@ -1843,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)",
|
||||
@@ -2227,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",
|
||||
@@ -2237,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",
|
||||
@@ -2298,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",
|
||||
@@ -2948,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.",
|
||||
|
||||
+31
-16
@@ -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",
|
||||
@@ -1309,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",
|
||||
@@ -1406,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.",
|
||||
@@ -1418,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",
|
||||
@@ -1442,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",
|
||||
@@ -1461,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.",
|
||||
@@ -1598,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",
|
||||
@@ -1609,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.",
|
||||
@@ -1636,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",
|
||||
@@ -1727,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",
|
||||
@@ -1758,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",
|
||||
@@ -1766,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",
|
||||
@@ -1843,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)",
|
||||
@@ -2227,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",
|
||||
@@ -2237,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",
|
||||
@@ -2298,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",
|
||||
@@ -2948,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.",
|
||||
|
||||
+31
-16
@@ -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": "フォーム",
|
||||
@@ -1309,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を追加",
|
||||
@@ -1406,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": "背景を色、画像、またはアニメーションに変更します。",
|
||||
@@ -1418,6 +1426,7 @@
|
||||
"choose_where_to_run_the_survey": "フォームを実行する場所を選択してください。",
|
||||
"city": "市区町村",
|
||||
"close_survey_on_response_limit": "回答数の上限でフォームを閉じる",
|
||||
"code": "コード",
|
||||
"color": "色",
|
||||
"column_used_in_logic_error": "この列は質問 {questionIndex} のロジックで使用されています。まず、ロジックから削除してください。",
|
||||
"columns": "列",
|
||||
@@ -1442,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": "選択肢を削除",
|
||||
@@ -1461,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を使用してスパム回答をフィルタリングします。",
|
||||
@@ -1598,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インスタンスに必要",
|
||||
@@ -1609,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": "まだ変数がありません。以下で最初のものを追加してください。",
|
||||
@@ -1636,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": "進捗を保存しました",
|
||||
@@ -1727,6 +1739,7 @@
|
||||
"seven_points": "7点",
|
||||
"show_block_settings": "ブロック設定を表示",
|
||||
"show_button": "ボタンを表示",
|
||||
"show_in_order": "順番に表示",
|
||||
"show_language_switch": "言語切り替えを表示",
|
||||
"show_multiple_times": "限られた回数表示する",
|
||||
"show_only_once": "一度だけ表示",
|
||||
@@ -1758,7 +1771,6 @@
|
||||
"survey_preview": "アンケートプレビュー 👀",
|
||||
"survey_styling": "フォームのスタイル",
|
||||
"survey_trigger": "フォームのトリガー",
|
||||
"switch_multi_language_on_to_get_started": "多言語機能をオンにして開始 👉",
|
||||
"target_block_not_found": "対象ブロックが見つかりません",
|
||||
"targeted": "ターゲット",
|
||||
"ten_points": "10点",
|
||||
@@ -1766,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を入力",
|
||||
@@ -1843,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": "クールダウン期間(アンケート全体)",
|
||||
@@ -2227,7 +2242,6 @@
|
||||
"duplicate_language_or_language_id": "重複する言語または言語ID",
|
||||
"edit_languages": "言語を編集",
|
||||
"identifier": "識別子(ISO)",
|
||||
"incomplete_translations": "未完了の翻訳",
|
||||
"language": "言語",
|
||||
"language_deleted_successfully": "言語を正常に削除しました",
|
||||
"languages_updated_successfully": "言語を正常に更新しました",
|
||||
@@ -2237,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": "背景色を追加",
|
||||
@@ -2298,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": "入力フィールド",
|
||||
@@ -2948,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": "ユーザーが製品に統合を追加するのがどれだけ簡単かを評価する。盲点を見つける。",
|
||||
|
||||
+32
-17
@@ -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",
|
||||
@@ -1309,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",
|
||||
@@ -1406,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.",
|
||||
@@ -1418,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",
|
||||
@@ -1442,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",
|
||||
@@ -1461,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.",
|
||||
@@ -1597,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",
|
||||
@@ -1609,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.",
|
||||
@@ -1636,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",
|
||||
@@ -1727,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",
|
||||
@@ -1758,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",
|
||||
@@ -1766,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",
|
||||
@@ -1843,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)",
|
||||
@@ -2227,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",
|
||||
@@ -2237,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",
|
||||
@@ -2298,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",
|
||||
@@ -2948,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.",
|
||||
|
||||
+32
-17
@@ -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",
|
||||
@@ -1309,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",
|
||||
@@ -1406,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.",
|
||||
@@ -1418,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",
|
||||
@@ -1442,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",
|
||||
@@ -1461,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.",
|
||||
@@ -1597,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",
|
||||
@@ -1609,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.",
|
||||
@@ -1636,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",
|
||||
@@ -1727,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",
|
||||
@@ -1758,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",
|
||||
@@ -1766,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",
|
||||
@@ -1843,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)",
|
||||
@@ -2227,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",
|
||||
@@ -2237,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",
|
||||
@@ -2298,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 Ró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 Ró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 Ró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",
|
||||
@@ -2948,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.",
|
||||
|
||||
+32
-17
@@ -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",
|
||||
@@ -1309,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",
|
||||
@@ -1406,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",
|
||||
@@ -1418,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",
|
||||
@@ -1442,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",
|
||||
@@ -1461,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.",
|
||||
@@ -1597,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",
|
||||
@@ -1609,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.",
|
||||
@@ -1636,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",
|
||||
@@ -1727,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",
|
||||
@@ -1758,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",
|
||||
@@ -1766,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",
|
||||
@@ -1843,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)",
|
||||
@@ -2227,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",
|
||||
@@ -2237,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",
|
||||
@@ -2298,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",
|
||||
@@ -2948,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.",
|
||||
|
||||
+32
-17
@@ -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",
|
||||
@@ -1309,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",
|
||||
@@ -1406,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.",
|
||||
@@ -1418,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",
|
||||
@@ -1442,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",
|
||||
@@ -1461,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.",
|
||||
@@ -1597,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",
|
||||
@@ -1609,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.",
|
||||
@@ -1636,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",
|
||||
@@ -1727,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ă",
|
||||
@@ -1758,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",
|
||||
@@ -1766,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",
|
||||
@@ -1843,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)",
|
||||
@@ -2227,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",
|
||||
@@ -2237,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",
|
||||
@@ -2298,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": "Redimensionează 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",
|
||||
@@ -2948,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": "Vă 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.",
|
||||
|
||||
+31
-16
@@ -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": "Опрос",
|
||||
@@ -1309,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-код",
|
||||
@@ -1406,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": "Изменить фон на цвет, изображение или анимацию.",
|
||||
@@ -1418,6 +1426,7 @@
|
||||
"choose_where_to_run_the_survey": "Выберите, где запускать опрос.",
|
||||
"city": "Город",
|
||||
"close_survey_on_response_limit": "Закрыть опрос при достижении лимита ответов",
|
||||
"code": "Код",
|
||||
"color": "Цвет",
|
||||
"column_used_in_logic_error": "Этот столбец используется в логике вопроса {questionIndex}. Пожалуйста, сначала удалите его из логики.",
|
||||
"columns": "Столбцы",
|
||||
@@ -1442,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": "Удалить вариант",
|
||||
@@ -1461,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, чтобы отфильтровывать спам-ответы.",
|
||||
@@ -1598,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",
|
||||
@@ -1609,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": "Пока нет переменных. Добавьте первую ниже.",
|
||||
@@ -1636,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": "Прогресс сохранён",
|
||||
@@ -1727,6 +1739,7 @@
|
||||
"seven_points": "7 баллов",
|
||||
"show_block_settings": "Показать настройки блока",
|
||||
"show_button": "Показать кнопку",
|
||||
"show_in_order": "Показать по порядку",
|
||||
"show_language_switch": "Показать переключатель языка",
|
||||
"show_multiple_times": "Показать ограниченное количество раз",
|
||||
"show_only_once": "Показать только один раз",
|
||||
@@ -1758,7 +1771,6 @@
|
||||
"survey_preview": "Предпросмотр опроса 👀",
|
||||
"survey_styling": "Оформление формы",
|
||||
"survey_trigger": "Триггер опроса",
|
||||
"switch_multi_language_on_to_get_started": "Включите многоязычный режим, чтобы начать 👉",
|
||||
"target_block_not_found": "Целевой блок не найден",
|
||||
"targeted": "Нацелен",
|
||||
"ten_points": "10 баллов",
|
||||
@@ -1766,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 поля",
|
||||
@@ -1843,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": "Период ожидания (между опросами)",
|
||||
@@ -2227,7 +2242,6 @@
|
||||
"duplicate_language_or_language_id": "Дублирующийся язык или идентификатор языка",
|
||||
"edit_languages": "Редактировать языки",
|
||||
"identifier": "Идентификатор (ISO)",
|
||||
"incomplete_translations": "Неполные переводы",
|
||||
"language": "Язык",
|
||||
"language_deleted_successfully": "Язык успешно удалён",
|
||||
"languages_updated_successfully": "Языки успешно обновлены",
|
||||
@@ -2237,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": "Добавить цвет фона",
|
||||
@@ -2298,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": "Поля ввода",
|
||||
@@ -2948,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": "Оцените, насколько легко пользователи могут добавлять интеграции в ваш продукт. Найдите слабые места.",
|
||||
|
||||
+31
-16
@@ -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",
|
||||
@@ -1309,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",
|
||||
@@ -1406,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.",
|
||||
@@ -1418,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",
|
||||
@@ -1442,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",
|
||||
@@ -1461,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.",
|
||||
@@ -1598,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",
|
||||
@@ -1609,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.",
|
||||
@@ -1636,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",
|
||||
@@ -1727,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",
|
||||
@@ -1758,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",
|
||||
@@ -1766,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",
|
||||
@@ -1843,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)",
|
||||
@@ -2227,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",
|
||||
@@ -2237,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",
|
||||
@@ -2298,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",
|
||||
@@ -2948,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.",
|
||||
|
||||
@@ -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": "调查",
|
||||
@@ -1309,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",
|
||||
@@ -1406,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": "将 背景 更改为 颜色 、 图像 或 动画。",
|
||||
@@ -1418,6 +1426,7 @@
|
||||
"choose_where_to_run_the_survey": "选择 调查 运行 的 位置 。",
|
||||
"city": "城市",
|
||||
"close_survey_on_response_limit": "在响应限制时关闭 调查",
|
||||
"code": "代码",
|
||||
"color": "颜色",
|
||||
"column_used_in_logic_error": "\"这个 列 在 问题 {questionIndex} 的 逻辑 中 使用。请 先 从 逻辑 中 删除 它。\"",
|
||||
"columns": "列",
|
||||
@@ -1442,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": "删除 选择",
|
||||
@@ -1461,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 来 过滤 掉 垃圾 响应 。",
|
||||
@@ -1597,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 实例",
|
||||
@@ -1609,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": "还没有变量。 在下面添加第一个。",
|
||||
@@ -1636,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": "进度已保存",
|
||||
@@ -1727,6 +1739,7 @@
|
||||
"seven_points": "7 分",
|
||||
"show_block_settings": "显示区块设置",
|
||||
"show_button": "显示 按钮",
|
||||
"show_in_order": "按顺序显示",
|
||||
"show_language_switch": "显示 语言 切换",
|
||||
"show_multiple_times": "显示有限次数",
|
||||
"show_only_once": "仅 显示 一次",
|
||||
@@ -1758,7 +1771,6 @@
|
||||
"survey_preview": "问卷预览 👀",
|
||||
"survey_styling": "表单 样式",
|
||||
"survey_trigger": "调查 触发",
|
||||
"switch_multi_language_on_to_get_started": "开启多语言以开始使用 👉",
|
||||
"target_block_not_found": "未找到目标区块",
|
||||
"targeted": "定位",
|
||||
"ten_points": "10 分",
|
||||
@@ -1766,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",
|
||||
@@ -1843,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": "冷却期(跨问卷)",
|
||||
@@ -2227,7 +2242,6 @@
|
||||
"duplicate_language_or_language_id": "语言或语言 ID 重复",
|
||||
"edit_languages": "编辑语言",
|
||||
"identifier": "标识符(ISO)",
|
||||
"incomplete_translations": "翻译不完整",
|
||||
"language": "语言",
|
||||
"language_deleted_successfully": "语言删除成功",
|
||||
"languages_updated_successfully": "语言更新成功",
|
||||
@@ -2237,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": "添加背景色",
|
||||
@@ -2298,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": "输入项",
|
||||
@@ -2948,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": "评估用户 添加 集成 到 产品 的 便捷程度 。 找到 盲点 。",
|
||||
|
||||
@@ -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": "問卷",
|
||||
@@ -1309,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 碼",
|
||||
@@ -1406,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": "將背景變更為顏色、圖片或動畫。",
|
||||
@@ -1418,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": "欄位",
|
||||
@@ -1442,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": "刪除選項",
|
||||
@@ -1461,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 過濾垃圾回應。",
|
||||
@@ -1598,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 執行個體時需要",
|
||||
@@ -1609,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": "尚無變數。在下方新增第一個變數。",
|
||||
@@ -1636,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": "進度已儲存",
|
||||
@@ -1727,6 +1739,7 @@
|
||||
"seven_points": "7 分",
|
||||
"show_block_settings": "顯示區塊設定",
|
||||
"show_button": "顯示按鈕",
|
||||
"show_in_order": "依序顯示",
|
||||
"show_language_switch": "顯示語言切換",
|
||||
"show_multiple_times": "顯示有限次數",
|
||||
"show_only_once": "僅顯示一次",
|
||||
@@ -1758,7 +1771,6 @@
|
||||
"survey_preview": "問卷預覽 👀",
|
||||
"survey_styling": "表單樣式設定",
|
||||
"survey_trigger": "問卷觸發器",
|
||||
"switch_multi_language_on_to_get_started": "請開啟多語言功能以開始使用 👉",
|
||||
"target_block_not_found": "找不到目標區塊",
|
||||
"targeted": "目標",
|
||||
"ten_points": "10 分",
|
||||
@@ -1766,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",
|
||||
@@ -1843,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": "冷卻期(跨問卷)",
|
||||
@@ -2227,7 +2242,6 @@
|
||||
"duplicate_language_or_language_id": "語言或語言 ID 重複",
|
||||
"edit_languages": "編輯語言",
|
||||
"identifier": "識別碼(ISO)",
|
||||
"incomplete_translations": "翻譯不完整",
|
||||
"language": "語言",
|
||||
"language_deleted_successfully": "語言已成功刪除",
|
||||
"languages_updated_successfully": "語言已成功更新",
|
||||
@@ -2237,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": "新增背景顏色",
|
||||
@@ -2298,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": "輸入欄位",
|
||||
@@ -2948,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": "評估使用者將整合新增至您的產品的容易程度。找出盲點。",
|
||||
|
||||
+5
-8
@@ -98,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" },
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
-100
@@ -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>
|
||||
);
|
||||
};
|
||||
+6
-2
@@ -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>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -22,8 +22,6 @@ interface PictureSelectionFormProps {
|
||||
element: TSurveyPictureSelectionElement;
|
||||
elementIdx: number;
|
||||
updateElement: (elementIdx: number, updatedAttributes: Partial<TSurveyElement>) => void;
|
||||
selectedLanguageCode: string;
|
||||
setSelectedLanguageCode: (language: string) => void;
|
||||
isInvalid: boolean;
|
||||
locale: TUserLocale;
|
||||
isStorageConfigured: boolean;
|
||||
@@ -35,8 +33,6 @@ export const PictureSelectionForm = ({
|
||||
element,
|
||||
elementIdx,
|
||||
updateElement,
|
||||
selectedLanguageCode,
|
||||
setSelectedLanguageCode,
|
||||
isInvalid,
|
||||
locale,
|
||||
isStorageConfigured = true,
|
||||
@@ -87,8 +83,6 @@ export const PictureSelectionForm = ({
|
||||
elementIdx={elementIdx}
|
||||
isInvalid={isInvalid}
|
||||
updateElement={updateElement}
|
||||
selectedLanguageCode={selectedLanguageCode}
|
||||
setSelectedLanguageCode={setSelectedLanguageCode}
|
||||
locale={locale}
|
||||
isStorageConfigured={isStorageConfigured}
|
||||
isExternalUrlsAllowed={isExternalUrlsAllowed}
|
||||
@@ -106,8 +100,6 @@ export const PictureSelectionForm = ({
|
||||
elementIdx={elementIdx}
|
||||
isInvalid={isInvalid}
|
||||
updateElement={updateElement}
|
||||
selectedLanguageCode={selectedLanguageCode}
|
||||
setSelectedLanguageCode={setSelectedLanguageCode}
|
||||
locale={locale}
|
||||
isStorageConfigured={isStorageConfigured}
|
||||
isExternalUrlsAllowed={isExternalUrlsAllowed}
|
||||
|
||||
@@ -28,7 +28,6 @@ interface RankingElementFormProps {
|
||||
elementIdx: number;
|
||||
updateElement: (elementIdx: number, updatedAttributes: Partial<TSurveyElement>) => void;
|
||||
selectedLanguageCode: string;
|
||||
setSelectedLanguageCode: (language: string) => void;
|
||||
isInvalid: boolean;
|
||||
locale: TUserLocale;
|
||||
isStorageConfigured: boolean;
|
||||
@@ -42,7 +41,6 @@ export const RankingElementForm = ({
|
||||
isInvalid,
|
||||
localSurvey,
|
||||
selectedLanguageCode,
|
||||
setSelectedLanguageCode,
|
||||
locale,
|
||||
isStorageConfigured = true,
|
||||
isExternalUrlsAllowed,
|
||||
@@ -150,8 +148,6 @@ export const RankingElementForm = ({
|
||||
elementIdx={elementIdx}
|
||||
isInvalid={isInvalid}
|
||||
updateElement={updateElement}
|
||||
selectedLanguageCode={selectedLanguageCode}
|
||||
setSelectedLanguageCode={setSelectedLanguageCode}
|
||||
locale={locale}
|
||||
isStorageConfigured={isStorageConfigured}
|
||||
autoFocus={!element.headline?.default || element.headline.default.trim() === ""}
|
||||
@@ -170,8 +166,6 @@ export const RankingElementForm = ({
|
||||
elementIdx={elementIdx}
|
||||
isInvalid={isInvalid}
|
||||
updateElement={updateElement}
|
||||
selectedLanguageCode={selectedLanguageCode}
|
||||
setSelectedLanguageCode={setSelectedLanguageCode}
|
||||
locale={locale}
|
||||
isStorageConfigured={isStorageConfigured}
|
||||
autoFocus={!element.subheader?.default || element.subheader.default.trim() === ""}
|
||||
@@ -232,8 +226,6 @@ export const RankingElementForm = ({
|
||||
addChoice={addChoice}
|
||||
isInvalid={isInvalid}
|
||||
localSurvey={localSurvey}
|
||||
selectedLanguageCode={selectedLanguageCode}
|
||||
setSelectedLanguageCode={setSelectedLanguageCode}
|
||||
surveyLanguages={surveyLanguages}
|
||||
element={element}
|
||||
updateElement={updateElement}
|
||||
|
||||
@@ -19,8 +19,6 @@ interface RatingElementFormProps {
|
||||
elementIdx: number;
|
||||
updateElement: (elementIdx: number, updatedAttributes: Partial<TSurveyElement>) => void;
|
||||
lastElement: boolean;
|
||||
selectedLanguageCode: string;
|
||||
setSelectedLanguageCode: (language: string) => void;
|
||||
isInvalid: boolean;
|
||||
locale: TUserLocale;
|
||||
isStorageConfigured: boolean;
|
||||
@@ -33,8 +31,6 @@ export const RatingElementForm = ({
|
||||
updateElement,
|
||||
isInvalid,
|
||||
localSurvey,
|
||||
selectedLanguageCode,
|
||||
setSelectedLanguageCode,
|
||||
locale,
|
||||
isStorageConfigured = true,
|
||||
isExternalUrlsAllowed,
|
||||
@@ -53,8 +49,6 @@ export const RatingElementForm = ({
|
||||
elementIdx={elementIdx}
|
||||
isInvalid={isInvalid}
|
||||
updateElement={updateElement}
|
||||
selectedLanguageCode={selectedLanguageCode}
|
||||
setSelectedLanguageCode={setSelectedLanguageCode}
|
||||
locale={locale}
|
||||
isStorageConfigured={isStorageConfigured}
|
||||
autoFocus={!element.headline?.default || element.headline.default.trim() === ""}
|
||||
@@ -73,8 +67,6 @@ export const RatingElementForm = ({
|
||||
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 RatingElementForm = ({
|
||||
elementIdx={elementIdx}
|
||||
isInvalid={isInvalid}
|
||||
updateElement={updateElement}
|
||||
selectedLanguageCode={selectedLanguageCode}
|
||||
setSelectedLanguageCode={setSelectedLanguageCode}
|
||||
locale={locale}
|
||||
isStorageConfigured={isStorageConfigured}
|
||||
/>
|
||||
@@ -170,8 +160,6 @@ export const RatingElementForm = ({
|
||||
elementIdx={elementIdx}
|
||||
isInvalid={isInvalid}
|
||||
updateElement={updateElement}
|
||||
selectedLanguageCode={selectedLanguageCode}
|
||||
setSelectedLanguageCode={setSelectedLanguageCode}
|
||||
locale={locale}
|
||||
isStorageConfigured={isStorageConfigured}
|
||||
/>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { Project } from "@prisma/client";
|
||||
import { RotateCcwIcon, SparklesIcon } from "lucide-react";
|
||||
import { RotateCcwIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { UseFormReturn, useForm } from "react-hook-form";
|
||||
@@ -21,7 +21,6 @@ import { AlertDialog } from "@/modules/ui/components/alert-dialog";
|
||||
import { BackgroundStylingCard } from "@/modules/ui/components/background-styling-card";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { CardStylingSettings } from "@/modules/ui/components/card-styling-settings";
|
||||
import { ColorPicker } from "@/modules/ui/components/color-picker";
|
||||
import {
|
||||
FormControl,
|
||||
FormDescription,
|
||||
@@ -204,12 +203,12 @@ export const StylingView = ({
|
||||
<form onSubmit={(e) => e.preventDefault()}>
|
||||
<div className="mt-12 space-y-3 p-5">
|
||||
{!isCxMode && (
|
||||
<div className="flex items-center gap-4 py-4">
|
||||
<div className="flex items-center gap-4 rounded-lg border border-slate-300 bg-white p-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="overwriteThemeStyling"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex items-center gap-2 space-y-0">
|
||||
<FormItem className="flex items-center gap-4 space-y-0">
|
||||
<FormControl>
|
||||
<Switch
|
||||
id="overwrite-theme-styling"
|
||||
@@ -219,10 +218,12 @@ export const StylingView = ({
|
||||
</FormControl>
|
||||
|
||||
<div>
|
||||
<FormLabel htmlFor="overwrite-theme-styling" className="text-base font-semibold text-slate-900">
|
||||
<FormLabel
|
||||
htmlFor="overwrite-theme-styling"
|
||||
className="text-base font-semibold text-slate-900">
|
||||
{t("environments.surveys.edit.add_custom_styles")}
|
||||
</FormLabel>
|
||||
<FormDescription className="text-sm text-slate-800">
|
||||
<FormDescription className="text-sm text-slate-500">
|
||||
{t("environments.surveys.edit.override_theme_with_individual_styles_for_this_survey")}
|
||||
</FormDescription>
|
||||
</div>
|
||||
@@ -232,43 +233,12 @@ export const StylingView = ({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{overwriteThemeStyling && (
|
||||
<div className="grid grid-cols-2 items-end gap-4 rounded-lg border border-slate-300 bg-white p-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="brandColor.light"
|
||||
render={({ field }) => (
|
||||
<FormItem className="space-y-1">
|
||||
<FormLabel>{t("environments.surveys.edit.brand_color")}</FormLabel>
|
||||
<FormDescription>
|
||||
{t("environments.surveys.edit.brand_color_description")}
|
||||
</FormDescription>
|
||||
<FormControl>
|
||||
<ColorPicker
|
||||
color={field.value ?? STYLE_DEFAULTS.brandColor?.light ?? COLOR_DEFAULTS.brandColor}
|
||||
onChange={(color) => field.onChange(color)}
|
||||
containerClass="w-full"
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
variant="default"
|
||||
className="h-10 justify-center gap-1"
|
||||
onClick={() => setConfirmSuggestColorsOpen(true)}>
|
||||
<SparklesIcon className="mr-2 h-4 w-4" />
|
||||
{t("environments.workspace.look.suggest_colors")}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<FormStylingSettings
|
||||
open={formStylingOpen}
|
||||
setOpen={setFormStylingOpen}
|
||||
disabled={!overwriteThemeStyling}
|
||||
form={form as UseFormReturn<TProjectStyling | TSurveyStyling>}
|
||||
onSuggestColorsClick={() => setConfirmSuggestColorsOpen(true)}
|
||||
/>
|
||||
|
||||
<CardStylingSettings
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { MailIcon, PaintbrushIcon, Rows3Icon, SettingsIcon } from "lucide-react";
|
||||
import {
|
||||
AlertTriangleIcon,
|
||||
Languages,
|
||||
MailIcon,
|
||||
PaintbrushIcon,
|
||||
Rows3Icon,
|
||||
SettingsIcon,
|
||||
} from "lucide-react";
|
||||
import { type JSX, useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TSurveyEditorTabs } from "@formbricks/types/surveys/types";
|
||||
@@ -10,6 +17,7 @@ interface Tab {
|
||||
id: TSurveyEditorTabs;
|
||||
label: string;
|
||||
icon: JSX.Element;
|
||||
alert?: boolean;
|
||||
}
|
||||
|
||||
interface SurveyEditorTabsProps {
|
||||
@@ -17,6 +25,7 @@ interface SurveyEditorTabsProps {
|
||||
setActiveId: React.Dispatch<React.SetStateAction<TSurveyEditorTabs>>;
|
||||
isStylingTabVisible?: boolean;
|
||||
isCxMode: boolean;
|
||||
hasLanguageErrors?: boolean;
|
||||
}
|
||||
|
||||
export const SurveyEditorTabs = ({
|
||||
@@ -24,6 +33,7 @@ export const SurveyEditorTabs = ({
|
||||
setActiveId,
|
||||
isStylingTabVisible,
|
||||
isCxMode,
|
||||
hasLanguageErrors,
|
||||
}: SurveyEditorTabsProps) => {
|
||||
const { t } = useTranslation();
|
||||
const tabsComputed = useMemo(() => {
|
||||
@@ -38,6 +48,12 @@ export const SurveyEditorTabs = ({
|
||||
label: t("common.styling"),
|
||||
icon: <PaintbrushIcon className="h-5 w-5" />,
|
||||
},
|
||||
{
|
||||
id: "language",
|
||||
label: t("common.language"),
|
||||
icon: <Languages className="h-5 w-5" />,
|
||||
alert: hasLanguageErrors,
|
||||
},
|
||||
{
|
||||
id: "settings",
|
||||
label: t("common.settings"),
|
||||
@@ -54,7 +70,7 @@ export const SurveyEditorTabs = ({
|
||||
return tabs;
|
||||
}
|
||||
return tabs.filter((tab) => tab.id !== "styling");
|
||||
}, [isStylingTabVisible, t]);
|
||||
}, [isStylingTabVisible, t, hasLanguageErrors]);
|
||||
|
||||
// Hide settings tab in CX mode
|
||||
let tabsToDisplay = isCxMode ? tabsComputed.filter((tab) => tab.id !== "settings") : tabsComputed;
|
||||
@@ -76,6 +92,7 @@ export const SurveyEditorTabs = ({
|
||||
aria-current={tab.id === activeId ? "page" : undefined}>
|
||||
{tab.icon && <div className="mr-2 h-5 w-5">{tab.icon}</div>}
|
||||
{tab.label}
|
||||
{tab.alert && <AlertTriangleIcon className="ml-1.5 h-4 w-4 text-amber-500" />}
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
@@ -20,8 +20,9 @@ import { SurveyEditorTabs } from "@/modules/survey/editor/components/survey-edit
|
||||
import { SurveyMenuBar } from "@/modules/survey/editor/components/survey-menu-bar";
|
||||
import { TFollowUpEmailToUser } from "@/modules/survey/editor/types/survey-follow-up";
|
||||
import { FollowUpsView } from "@/modules/survey/follow-ups/components/follow-ups-view";
|
||||
import { LanguageView } from "@/modules/survey/multi-language-surveys/components/language-view";
|
||||
import { PreviewSurvey } from "@/modules/ui/components/preview-survey";
|
||||
import { refetchProjectAction } from "../actions";
|
||||
import { getProjectLanguagesAction, refetchProjectAction } from "../actions";
|
||||
|
||||
interface SurveyEditorProps {
|
||||
survey: TSurvey;
|
||||
@@ -84,24 +85,32 @@ export const SurveyEditor = ({
|
||||
const [activeElementId, setActiveElementId] = useState<string | null>(null);
|
||||
const [localSurvey, setLocalSurvey] = useState<TSurvey | null>(() => structuredClone(survey));
|
||||
const [invalidElements, setInvalidElements] = useState<string[] | null>([]);
|
||||
const [hasIncompleteTranslations, setHasIncompleteTranslations] = useState(false);
|
||||
|
||||
const [selectedLanguageCode, setSelectedLanguageCode] = useState<string>("default");
|
||||
const surveyEditorRef = useRef(null);
|
||||
const [localProject, setLocalProject] = useState<Project>(project);
|
||||
const [localProjectLanguages, setLocalProjectLanguages] = useState<Language[]>(projectLanguages);
|
||||
|
||||
const [styling, setStyling] = useState<TSurveyStyling | null>(localSurvey?.styling ?? null);
|
||||
const [localStylingChanges, setLocalStylingChanges] = useState<TSurveyStyling | null>(null);
|
||||
|
||||
const fetchLatestProject = useCallback(async () => {
|
||||
const refetchProjectResponse = await refetchProjectAction({ projectId: localProject.id });
|
||||
const fetchLatestProjectData = useCallback(async () => {
|
||||
const [refetchProjectResponse, refetchLanguagesResponse] = await Promise.all([
|
||||
refetchProjectAction({ projectId: localProject.id }),
|
||||
getProjectLanguagesAction({ projectId: localProject.id }),
|
||||
]);
|
||||
if (refetchProjectResponse?.data) {
|
||||
setLocalProject(refetchProjectResponse.data);
|
||||
}
|
||||
if (refetchLanguagesResponse?.data) {
|
||||
setLocalProjectLanguages(refetchLanguagesResponse.data);
|
||||
}
|
||||
}, [localProject.id]);
|
||||
|
||||
const [isCautionDialogOpen, setIsCautionDialogOpen] = useState(false);
|
||||
|
||||
useDocumentVisibility(fetchLatestProject);
|
||||
useDocumentVisibility(fetchLatestProjectData);
|
||||
|
||||
useEffect(() => {
|
||||
if (survey) {
|
||||
@@ -190,6 +199,7 @@ export const SurveyEditor = ({
|
||||
setActiveId={setActiveView}
|
||||
isCxMode={isCxMode}
|
||||
isStylingTabVisible={!!project.styling.allowStyleOverwrite}
|
||||
hasLanguageErrors={hasIncompleteTranslations}
|
||||
/>
|
||||
|
||||
{activeView === "elements" && (
|
||||
@@ -199,11 +209,9 @@ export const SurveyEditor = ({
|
||||
activeElementId={activeElementId}
|
||||
setActiveElementId={setActiveElementId}
|
||||
project={localProject}
|
||||
projectLanguages={projectLanguages}
|
||||
invalidElements={invalidElements}
|
||||
setInvalidElements={setInvalidElements}
|
||||
selectedLanguageCode={selectedLanguageCode || "default"}
|
||||
setSelectedLanguageCode={setSelectedLanguageCode}
|
||||
isFormbricksCloud={isFormbricksCloud}
|
||||
isCxMode={isCxMode}
|
||||
locale={locale}
|
||||
@@ -232,6 +240,16 @@ export const SurveyEditor = ({
|
||||
/>
|
||||
)}
|
||||
|
||||
{activeView === "language" && (
|
||||
<LanguageView
|
||||
localSurvey={localSurvey}
|
||||
setLocalSurvey={setLocalSurveyNonNull}
|
||||
projectLanguages={localProjectLanguages}
|
||||
locale={locale}
|
||||
setHasIncompleteTranslations={setHasIncompleteTranslations}
|
||||
/>
|
||||
)}
|
||||
|
||||
{activeView === "settings" && (
|
||||
<SettingsView
|
||||
environment={environment}
|
||||
@@ -274,6 +292,8 @@ export const SurveyEditor = ({
|
||||
environment={environment}
|
||||
previewType={localSurvey.type === "app" ? "modal" : "fullwidth"}
|
||||
languageCode={selectedLanguageCode}
|
||||
setLanguageCode={setSelectedLanguageCode}
|
||||
locale={locale}
|
||||
isSpamProtectionAllowed={isSpamProtectionAllowed}
|
||||
publicDomain={publicDomain}
|
||||
/>
|
||||
|
||||
@@ -245,6 +245,7 @@ export const SurveyMenuBar = ({
|
||||
const messageSplit = firstError.message.split("-fLang-")[0];
|
||||
|
||||
toast.error(`${messageSplit} ${invalidLanguageLabels.join(", ")}`);
|
||||
setActiveId("language");
|
||||
} else {
|
||||
toast.error(firstError.message, {
|
||||
className: "w-fit !max-w-md",
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Language } from "@prisma/client";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { getLanguageLabel } from "@formbricks/i18n-utils/src/utils";
|
||||
import { DefaultTag } from "@/modules/ui/components/default-tag";
|
||||
import { Label } from "@/modules/ui/components/label";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/modules/ui/components/select";
|
||||
import type { ConfirmationModalProps } from "./multi-language-card";
|
||||
|
||||
interface DefaultLanguageSelectProps {
|
||||
defaultLanguage?: Language;
|
||||
handleDefaultLanguageChange: (languageCode: string) => void;
|
||||
projectLanguages: Language[];
|
||||
setConfirmationModalInfo: (confirmationModal: ConfirmationModalProps) => void;
|
||||
locale: string;
|
||||
}
|
||||
|
||||
export function DefaultLanguageSelect({
|
||||
defaultLanguage,
|
||||
handleDefaultLanguageChange,
|
||||
projectLanguages,
|
||||
setConfirmationModalInfo,
|
||||
locale,
|
||||
}: DefaultLanguageSelectProps) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<Label>{t("environments.surveys.edit.1_choose_the_default_language_for_this_survey")}</Label>
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="w-48">
|
||||
<Select
|
||||
defaultValue={`${defaultLanguage?.code}`}
|
||||
disabled={Boolean(defaultLanguage)}
|
||||
onValueChange={(languageCode) => {
|
||||
setConfirmationModalInfo({
|
||||
open: true,
|
||||
title:
|
||||
t("environments.surveys.edit.confirm_default_language") +
|
||||
": " +
|
||||
getLanguageLabel(languageCode, locale),
|
||||
body: t(
|
||||
"environments.surveys.edit.once_set_the_default_language_for_this_survey_can_only_be_changed_by_disabling_the_multi_language_option_and_deleting_all_translations"
|
||||
),
|
||||
buttonText: t("common.confirm"),
|
||||
onConfirm: () => {
|
||||
handleDefaultLanguageChange(languageCode);
|
||||
},
|
||||
buttonVariant: "default",
|
||||
});
|
||||
}}
|
||||
value={`${defaultLanguage?.code}`}>
|
||||
<SelectTrigger className="w-full max-w-full truncate px-4 text-xs text-slate-800">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{projectLanguages.map((language) => (
|
||||
<SelectItem
|
||||
className="px-0.5 py-1 text-sm text-slate-800"
|
||||
key={language.id}
|
||||
value={language.code}>
|
||||
{`${getLanguageLabel(language.code, locale)} (${language.code})`}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<DefaultTag />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
import { ChevronDown } from "lucide-react";
|
||||
import { useRef, useState } from "react";
|
||||
import { getLanguageLabel } from "@formbricks/i18n-utils/src/utils";
|
||||
import type { TSurveyLanguage } from "@formbricks/types/surveys/types";
|
||||
import { useClickOutside } from "@/lib/utils/hooks/useClickOutside";
|
||||
|
||||
interface LanguageIndicatorProps {
|
||||
selectedLanguageCode: string;
|
||||
surveyLanguages: TSurveyLanguage[];
|
||||
setSelectedLanguageCode: (languageCode: string) => void;
|
||||
setFirstRender?: (firstRender: boolean) => void;
|
||||
locale: string;
|
||||
}
|
||||
export function LanguageIndicator({
|
||||
surveyLanguages,
|
||||
selectedLanguageCode,
|
||||
setSelectedLanguageCode,
|
||||
setFirstRender,
|
||||
locale,
|
||||
}: LanguageIndicatorProps) {
|
||||
const [showLanguageDropdown, setShowLanguageDropdown] = useState(false);
|
||||
const toggleDropdown = () => {
|
||||
setShowLanguageDropdown((prev) => !prev);
|
||||
};
|
||||
const languageDropdownRef = useRef(null);
|
||||
|
||||
const changeLanguage = (language: TSurveyLanguage) => {
|
||||
setSelectedLanguageCode(language.default ? "default" : language.language.code);
|
||||
if (setFirstRender) {
|
||||
//for lexical editor
|
||||
setFirstRender(true);
|
||||
}
|
||||
setShowLanguageDropdown(false);
|
||||
};
|
||||
|
||||
const languageToBeDisplayed = surveyLanguages.find((language) => {
|
||||
return selectedLanguageCode === "default"
|
||||
? language.default
|
||||
: language.language.code === selectedLanguageCode;
|
||||
});
|
||||
|
||||
useClickOutside(languageDropdownRef, () => {
|
||||
setShowLanguageDropdown(false);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="absolute right-2 top-2">
|
||||
<button
|
||||
aria-expanded={showLanguageDropdown}
|
||||
aria-haspopup="true"
|
||||
className="relative z-20 flex max-w-20 items-center justify-center rounded-md bg-slate-900 p-1 px-2 text-xs text-white hover:bg-slate-700"
|
||||
onClick={toggleDropdown}
|
||||
tabIndex={-1}
|
||||
type="button">
|
||||
<span className="max-w-full truncate">
|
||||
{languageToBeDisplayed ? getLanguageLabel(languageToBeDisplayed.language.code, locale) : ""}
|
||||
</span>
|
||||
<ChevronDown className="ml-1 h-4 w-4 flex-shrink-0" />
|
||||
</button>
|
||||
{showLanguageDropdown ? (
|
||||
<div
|
||||
className="absolute right-0 z-30 mt-1 max-h-64 w-48 space-y-2 overflow-auto rounded-md bg-slate-900 p-1 text-xs text-white"
|
||||
ref={languageDropdownRef}>
|
||||
{surveyLanguages.map(
|
||||
(language) =>
|
||||
language.language.code !== languageToBeDisplayed?.language.code &&
|
||||
language.enabled && (
|
||||
<button
|
||||
className="flex w-full rounded-sm p-1 text-left hover:bg-slate-700"
|
||||
key={language.language.id}
|
||||
onClick={() => {
|
||||
changeLanguage(language);
|
||||
}}
|
||||
type="button">
|
||||
<span className="min-w-0 flex-1 truncate">
|
||||
{getLanguageLabel(language.language.code, locale)}
|
||||
</span>
|
||||
</button>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Language } from "@prisma/client";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { getLanguageLabel } from "@formbricks/i18n-utils/src/utils";
|
||||
import type { TUserLocale } from "@formbricks/types/user";
|
||||
import { Label } from "@/modules/ui/components/label";
|
||||
import { Switch } from "@/modules/ui/components/switch";
|
||||
|
||||
interface LanguageToggleProps {
|
||||
language: Language;
|
||||
isChecked: boolean;
|
||||
onToggle: () => void;
|
||||
onEdit: () => void;
|
||||
locale: TUserLocale;
|
||||
}
|
||||
|
||||
export function LanguageToggle({ language, isChecked, onToggle, onEdit, locale }: LanguageToggleProps) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="flex flex-col space-y-4">
|
||||
<div className="flex max-w-96 items-center space-x-4">
|
||||
<Switch
|
||||
checked={isChecked}
|
||||
id={`${language.code}-toggle`}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onToggle();
|
||||
}}
|
||||
/>
|
||||
<Label className="truncate font-medium text-slate-800" htmlFor={`${language.code}-toggle`}>
|
||||
{getLanguageLabel(language.code, locale)}
|
||||
</Label>
|
||||
{isChecked ? (
|
||||
<button
|
||||
className="truncate text-xs text-slate-600 underline hover:text-slate-800"
|
||||
onClick={onEdit}
|
||||
type="button">
|
||||
{t("environments.surveys.edit.edit_translations", {
|
||||
lang: getLanguageLabel(language.code, locale),
|
||||
})}
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,504 @@
|
||||
"use client";
|
||||
|
||||
import { Language } from "@prisma/client";
|
||||
import { ArrowUpRight, EllipsisVerticalIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { getLanguageLabel } from "@formbricks/i18n-utils/src/utils";
|
||||
import { TSurvey, TSurveyLanguage } from "@formbricks/types/surveys/types";
|
||||
import { TUserLocale } from "@formbricks/types/user";
|
||||
import { cn } from "@/lib/cn";
|
||||
import { addMultiLanguageLabels, extractLanguageCodes, getEnabledLanguages } from "@/lib/i18n/utils";
|
||||
import { AdvancedOptionToggle } from "@/modules/ui/components/advanced-option-toggle";
|
||||
import { Badge } from "@/modules/ui/components/badge";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { ConfirmationModal } from "@/modules/ui/components/confirmation-modal";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/modules/ui/components/dropdown-menu";
|
||||
import { Label } from "@/modules/ui/components/label";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/modules/ui/components/select";
|
||||
import { Switch } from "@/modules/ui/components/switch";
|
||||
import {
|
||||
computeTranslationProgress,
|
||||
extractTranslatableStrings,
|
||||
getProgressColor,
|
||||
getProgressTextColor,
|
||||
removeLanguageKeysFromSurvey,
|
||||
} from "../lib/utils";
|
||||
import { ManageTranslationsModal } from "./manage-translations-modal";
|
||||
|
||||
interface LanguageViewProps {
|
||||
localSurvey: TSurvey;
|
||||
setLocalSurvey: (survey: TSurvey) => void;
|
||||
projectLanguages: Language[];
|
||||
locale: TUserLocale;
|
||||
setHasIncompleteTranslations: (has: boolean) => void;
|
||||
}
|
||||
|
||||
interface ConfirmationModalInfo {
|
||||
title: string;
|
||||
open: boolean;
|
||||
body: string;
|
||||
buttonText: string;
|
||||
buttonVariant?: "default" | "destructive";
|
||||
onConfirm: () => void;
|
||||
}
|
||||
|
||||
export const LanguageView = ({
|
||||
localSurvey,
|
||||
setLocalSurvey,
|
||||
projectLanguages,
|
||||
locale,
|
||||
setHasIncompleteTranslations,
|
||||
}: LanguageViewProps) => {
|
||||
const { t } = useTranslation();
|
||||
const environmentId = localSurvey.environmentId;
|
||||
|
||||
const [isMultiLanguageActivated, setIsMultiLanguageActivated] = useState(localSurvey.languages.length > 0);
|
||||
const [confirmationModalInfo, setConfirmationModalInfo] = useState<ConfirmationModalInfo>({
|
||||
title: "",
|
||||
open: false,
|
||||
body: "",
|
||||
buttonText: "",
|
||||
onConfirm: () => {},
|
||||
});
|
||||
const [translationModalOpen, setTranslationModalOpen] = useState(false);
|
||||
const [activeLanguageCode, setActiveLanguageCode] = useState<string>("");
|
||||
|
||||
const defaultLanguage = useMemo(
|
||||
() => localSurvey.languages.find((l) => l.default)?.language,
|
||||
[localSurvey.languages]
|
||||
);
|
||||
|
||||
const translatableStrings = useMemo(() => extractTranslatableStrings(localSurvey, t), [localSurvey, t]);
|
||||
|
||||
const enabledLanguages = getEnabledLanguages(localSurvey.languages);
|
||||
|
||||
// Sync multi-language state
|
||||
useEffect(() => {
|
||||
if (localSurvey.languages.length === 0) {
|
||||
setIsMultiLanguageActivated(false);
|
||||
}
|
||||
}, [localSurvey.languages]);
|
||||
|
||||
// Track incomplete translations
|
||||
useEffect(() => {
|
||||
if (!isMultiLanguageActivated || localSurvey.languages.length <= 1) {
|
||||
setHasIncompleteTranslations(false);
|
||||
return;
|
||||
}
|
||||
const nonDefaultLangs = localSurvey.languages.filter((l) => !l.default && l.enabled);
|
||||
const hasIncomplete = nonDefaultLangs.some((lang) => {
|
||||
const progress = computeTranslationProgress(translatableStrings, lang.language.code);
|
||||
return progress.percentage < 100;
|
||||
});
|
||||
setHasIncompleteTranslations(hasIncomplete);
|
||||
}, [localSurvey.languages, translatableStrings, isMultiLanguageActivated, setHasIncompleteTranslations]);
|
||||
|
||||
const updateSurveyTranslations = (survey: TSurvey, updatedLanguages: TSurveyLanguage[]) => {
|
||||
const translatedSurveyResult = addMultiLanguageLabels(survey, extractLanguageCodes(updatedLanguages));
|
||||
const updatedSurvey = { ...translatedSurveyResult, languages: updatedLanguages };
|
||||
setLocalSurvey(updatedSurvey as TSurvey);
|
||||
};
|
||||
|
||||
const handleActivationSwitch = () => {
|
||||
if (isMultiLanguageActivated) {
|
||||
if (localSurvey.languages.length > 0) {
|
||||
setConfirmationModalInfo({
|
||||
open: true,
|
||||
title: t("environments.surveys.edit.remove_translations"),
|
||||
body: t("environments.surveys.edit.this_action_will_remove_all_the_translations_from_this_survey"),
|
||||
buttonText: t("environments.surveys.edit.remove_translations"),
|
||||
buttonVariant: "destructive",
|
||||
onConfirm: () => {
|
||||
updateSurveyTranslations(localSurvey, []);
|
||||
setIsMultiLanguageActivated(false);
|
||||
setConfirmationModalInfo((prev) => ({ ...prev, open: false }));
|
||||
},
|
||||
});
|
||||
} else {
|
||||
setIsMultiLanguageActivated(false);
|
||||
}
|
||||
} else {
|
||||
setIsMultiLanguageActivated(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDefaultLanguageChange = (languageCode: string) => {
|
||||
const language = projectLanguages.find((lang) => lang.code === languageCode);
|
||||
if (!language) return;
|
||||
|
||||
let languageExists = false;
|
||||
const newLanguages =
|
||||
localSurvey.languages.map((lang) => {
|
||||
if (lang.language.code === language.code) {
|
||||
languageExists = true;
|
||||
return { ...lang, default: true };
|
||||
}
|
||||
return { ...lang, default: false };
|
||||
}) ?? [];
|
||||
|
||||
if (!languageExists) {
|
||||
newLanguages.push({ enabled: true, default: true, language });
|
||||
}
|
||||
|
||||
setConfirmationModalInfo((prev) => ({ ...prev, open: false }));
|
||||
setLocalSurvey({ ...localSurvey, languages: newLanguages });
|
||||
};
|
||||
|
||||
const handleToggleLanguage = (code: string) => {
|
||||
const surveyLang = localSurvey.languages.find((l) => l.language.code === code);
|
||||
|
||||
if (surveyLang) {
|
||||
if (surveyLang.enabled) {
|
||||
// Disabling
|
||||
const progress = computeTranslationProgress(translatableStrings, code);
|
||||
if (progress.translated > 0) {
|
||||
// Has translations — just disable, keep translations in survey object
|
||||
const updatedLanguages = localSurvey.languages.map((l) =>
|
||||
l.language.code === code ? { ...l, enabled: false } : l
|
||||
);
|
||||
setLocalSurvey({ ...localSurvey, languages: updatedLanguages });
|
||||
} else {
|
||||
// No translations — remove from survey object and clean up i18n keys
|
||||
const cleanedSurvey = removeLanguageKeysFromSurvey(localSurvey, code);
|
||||
const updatedLanguages = localSurvey.languages.filter((l) => l.language.code !== code);
|
||||
setLocalSurvey({ ...cleanedSurvey, languages: updatedLanguages });
|
||||
}
|
||||
} else {
|
||||
// Re-enabling — ensure i18n keys exist for any new translatable strings
|
||||
const updatedLanguages = localSurvey.languages.map((l) =>
|
||||
l.language.code === code ? { ...l, enabled: true } : l
|
||||
);
|
||||
updateSurveyTranslations(localSurvey, updatedLanguages);
|
||||
}
|
||||
} else {
|
||||
// Language not in survey — add it with enabled: true
|
||||
const language = projectLanguages.find((l) => l.code === code);
|
||||
if (language) {
|
||||
const updatedLanguages: TSurveyLanguage[] = [
|
||||
...localSurvey.languages,
|
||||
{ enabled: true, default: false, language },
|
||||
];
|
||||
updateSurveyTranslations(localSurvey, updatedLanguages);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveTranslations = (code: string) => {
|
||||
const label = getLanguageLabel(code, locale) ?? code;
|
||||
setConfirmationModalInfo({
|
||||
open: true,
|
||||
title: `${t("environments.surveys.edit.remove_translations")}: ${label}`,
|
||||
body: t("environments.surveys.edit.this_will_remove_the_language_and_all_its_translations"),
|
||||
buttonText: t("environments.surveys.edit.remove_translations"),
|
||||
buttonVariant: "destructive",
|
||||
onConfirm: () => {
|
||||
const cleanedSurvey = removeLanguageKeysFromSurvey(localSurvey, code);
|
||||
const updatedLanguages = localSurvey.languages.filter((l) => l.language.code !== code);
|
||||
setLocalSurvey({ ...cleanedSurvey, languages: updatedLanguages });
|
||||
setConfirmationModalInfo((prev) => ({ ...prev, open: false }));
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleChangeDefault = () => {
|
||||
setConfirmationModalInfo({
|
||||
open: true,
|
||||
title: t("environments.surveys.edit.remove_translations"),
|
||||
body: t("environments.surveys.edit.this_action_will_remove_all_the_translations_from_this_survey"),
|
||||
buttonText: t("environments.surveys.edit.remove_translations"),
|
||||
buttonVariant: "destructive",
|
||||
onConfirm: () => {
|
||||
updateSurveyTranslations(localSurvey, []);
|
||||
setIsMultiLanguageActivated(false);
|
||||
setConfirmationModalInfo((prev) => ({ ...prev, open: false }));
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleLanguageSwitchToggle = () => {
|
||||
setLocalSurvey({ ...localSurvey, showLanguageSwitch: !localSurvey.showLanguageSwitch });
|
||||
};
|
||||
|
||||
const openTranslationModal = (code: string) => {
|
||||
setActiveLanguageCode(code);
|
||||
setTranslationModalOpen(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mt-12 space-y-3 p-5">
|
||||
{/* Activation toggle — only show when workspace has languages */}
|
||||
{projectLanguages.length > 0 && (
|
||||
<div className="flex items-center gap-4 rounded-lg border border-slate-300 bg-white p-4">
|
||||
<Switch
|
||||
checked={isMultiLanguageActivated}
|
||||
onCheckedChange={handleActivationSwitch}
|
||||
id="activate-translations-toggle"
|
||||
/>
|
||||
<div>
|
||||
<Label htmlFor="activate-translations-toggle" className="text-base font-semibold text-slate-900">
|
||||
{t("environments.surveys.edit.activate_translations")}
|
||||
</Label>
|
||||
<p className="text-sm text-slate-500">
|
||||
{t("environments.surveys.edit.present_your_survey_in_multiple_languages")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isMultiLanguageActivated && (
|
||||
<div className="space-y-6 rounded-lg border border-slate-300 bg-white p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="text-base font-semibold text-slate-900">{t("common.survey_languages")}</h3>
|
||||
</div>
|
||||
|
||||
{/* Default language select — only show when no default is set yet */}
|
||||
{projectLanguages.length > 0 && !defaultLanguage && (
|
||||
<div className="space-y-2">
|
||||
<Label>{t("environments.surveys.edit.default_language")}</Label>
|
||||
<div className="w-56">
|
||||
<Select
|
||||
onValueChange={(code) => {
|
||||
setConfirmationModalInfo({
|
||||
open: true,
|
||||
title:
|
||||
t("environments.surveys.edit.confirm_default_language") +
|
||||
": " +
|
||||
getLanguageLabel(code, locale),
|
||||
body: t(
|
||||
"environments.surveys.edit.once_set_the_default_language_for_this_survey_can_only_be_changed_by_disabling_the_multi_language_option_and_deleting_all_translations"
|
||||
),
|
||||
buttonText: t("common.confirm"),
|
||||
buttonVariant: "default",
|
||||
onConfirm: () => handleDefaultLanguageChange(code),
|
||||
});
|
||||
}}>
|
||||
<SelectTrigger className="w-full text-sm">
|
||||
<SelectValue placeholder={t("common.select_language")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{projectLanguages.map((lang) => (
|
||||
<SelectItem key={lang.id} value={lang.code}>
|
||||
{getLanguageLabel(lang.code, locale)} ({lang.code})
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Languages table — show all workspace languages */}
|
||||
{defaultLanguage && projectLanguages.length > 0 && (
|
||||
<div className="overflow-hidden rounded-lg border border-slate-300">
|
||||
<table className="w-full text-sm">
|
||||
<thead className="bg-slate-50">
|
||||
<tr>
|
||||
<th className="px-4 py-2.5 text-left font-medium text-slate-600">
|
||||
{t("common.language")}
|
||||
</th>
|
||||
<th className="px-4 py-2.5 text-left font-medium text-slate-600">
|
||||
{t("environments.surveys.edit.code")}
|
||||
</th>
|
||||
<th className="px-4 py-2.5 text-left font-medium text-slate-600">
|
||||
{t("environments.surveys.edit.translated")}
|
||||
</th>
|
||||
<th className="px-4 py-2.5 text-left font-medium text-slate-600">
|
||||
{t("environments.surveys.edit.visible")}
|
||||
</th>
|
||||
<th className="w-12 px-4 py-2.5" />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{/* Default language row */}
|
||||
<tr className="border-t border-slate-200">
|
||||
<td className="px-4 py-3 font-medium text-slate-800">
|
||||
{getLanguageLabel(defaultLanguage.code, locale)}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-slate-600">{defaultLanguage.code}</td>
|
||||
<td className="px-4 py-3">
|
||||
<Badge type="gray" size="normal" text={t("common.default")} />
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<Switch checked disabled />
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<button type="button" className="rounded p-1 hover:bg-slate-100">
|
||||
<EllipsisVerticalIcon className="h-4 w-4 text-slate-500" />
|
||||
</button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem
|
||||
className="text-red-600 focus:text-red-600"
|
||||
onClick={handleChangeDefault}>
|
||||
{t("environments.surveys.edit.change_default")}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{/* Non-default language rows — all workspace languages except default */}
|
||||
{projectLanguages
|
||||
.filter((pl) => pl.code !== defaultLanguage.code)
|
||||
.map((pl) => {
|
||||
const surveyLang = localSurvey.languages.find((sl) => sl.language.code === pl.code);
|
||||
const inSurvey = !!surveyLang;
|
||||
const enabled = surveyLang?.enabled ?? false;
|
||||
const progress = inSurvey
|
||||
? computeTranslationProgress(translatableStrings, pl.code)
|
||||
: null;
|
||||
|
||||
return (
|
||||
<tr
|
||||
key={pl.code}
|
||||
className={cn(
|
||||
"border-t border-slate-200",
|
||||
inSurvey && "cursor-pointer hover:bg-slate-50"
|
||||
)}
|
||||
onClick={() => {
|
||||
if (inSurvey) {
|
||||
openTranslationModal(pl.code);
|
||||
}
|
||||
}}>
|
||||
<td className="px-4 py-3 font-medium text-slate-800">
|
||||
{getLanguageLabel(pl.code, locale)}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-slate-600">{pl.code}</td>
|
||||
<td className="px-4 py-3">
|
||||
{inSurvey && progress && (
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="h-2 w-24 overflow-hidden rounded-full bg-slate-200">
|
||||
<div
|
||||
className={cn(
|
||||
"h-full rounded-full transition-all",
|
||||
getProgressColor(progress.percentage)
|
||||
)}
|
||||
style={{ width: `${progress.percentage}%` }}
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
className={cn(
|
||||
"text-xs font-medium",
|
||||
getProgressTextColor(progress.percentage)
|
||||
)}>
|
||||
{progress.translated}/{progress.total}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-4 py-3" onClick={(e) => e.stopPropagation()}>
|
||||
<Switch checked={enabled} onCheckedChange={() => handleToggleLanguage(pl.code)} />
|
||||
</td>
|
||||
<td className="px-4 py-3" onClick={(e) => e.stopPropagation()}>
|
||||
{inSurvey && (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<button type="button" className="rounded p-1 hover:bg-slate-100">
|
||||
<EllipsisVerticalIcon className="h-4 w-4 text-slate-500" />
|
||||
</button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={() => openTranslationModal(pl.code)}>
|
||||
{t("environments.surveys.edit.manage_translations")}
|
||||
</DropdownMenuItem>
|
||||
{progress && progress.translated > 0 && (
|
||||
<DropdownMenuItem
|
||||
className="text-red-600 focus:text-red-600"
|
||||
onClick={() => handleRemoveTranslations(pl.code)}>
|
||||
{t("environments.surveys.edit.remove_translations")}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Manage workspace languages link */}
|
||||
<Button asChild size="sm" variant="secondary">
|
||||
<Link href={`/environments/${environmentId}/workspace/languages`} target="_blank">
|
||||
{t("environments.surveys.edit.manage_languages")}
|
||||
<ArrowUpRight className="ml-1 h-4 w-4" />
|
||||
</Link>
|
||||
</Button>
|
||||
|
||||
{/* Show language switcher toggle */}
|
||||
<AdvancedOptionToggle
|
||||
customContainerClass="px-0 pt-0"
|
||||
htmlId="languageSwitch"
|
||||
disabled={enabledLanguages.length <= 1}
|
||||
isChecked={!!localSurvey.showLanguageSwitch}
|
||||
onToggle={handleLanguageSwitchToggle}
|
||||
title={t("environments.surveys.edit.show_language_switch")}
|
||||
description={t(
|
||||
"environments.surveys.edit.enable_participants_to_switch_the_survey_language_at_any_point_during_the_survey"
|
||||
)}
|
||||
childBorder={true}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{projectLanguages.length === 0 && (
|
||||
<div className="rounded-lg border border-slate-300 bg-white p-6 text-center">
|
||||
<p className="text-sm text-slate-500">
|
||||
{t("environments.surveys.edit.no_languages_found_add_first_one_to_get_started")}
|
||||
</p>
|
||||
<Button asChild size="sm" variant="secondary" className="mt-3">
|
||||
<Link href={`/environments/${environmentId}/workspace/languages`} target="_blank">
|
||||
{t("environments.surveys.edit.manage_languages")}
|
||||
<ArrowUpRight className="ml-1 h-4 w-4" />
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Confirmation modal */}
|
||||
<ConfirmationModal
|
||||
buttonText={confirmationModalInfo.buttonText}
|
||||
buttonVariant={confirmationModalInfo.buttonVariant}
|
||||
onConfirm={confirmationModalInfo.onConfirm}
|
||||
open={confirmationModalInfo.open}
|
||||
setOpen={(value) => {
|
||||
const open = typeof value === "function" ? value(confirmationModalInfo.open) : value;
|
||||
setConfirmationModalInfo((prev) => ({ ...prev, open }));
|
||||
}}
|
||||
body={confirmationModalInfo.body}
|
||||
title={confirmationModalInfo.title}
|
||||
/>
|
||||
|
||||
{/* Translations modal */}
|
||||
<ManageTranslationsModal
|
||||
open={translationModalOpen}
|
||||
setOpen={setTranslationModalOpen}
|
||||
localSurvey={localSurvey}
|
||||
setLocalSurvey={setLocalSurvey}
|
||||
languageCode={activeLanguageCode}
|
||||
languageName={getLanguageLabel(activeLanguageCode, locale) ?? activeLanguageCode}
|
||||
defaultLanguageName={
|
||||
defaultLanguage ? (getLanguageLabel(defaultLanguage.code, locale) ?? defaultLanguage.code) : ""
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -2,17 +2,12 @@
|
||||
|
||||
import { useMemo, useTransition } from "react";
|
||||
import type { Dispatch, SetStateAction } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { TI18nString } from "@formbricks/types/i18n";
|
||||
import type { TSurvey, TSurveyLanguage } from "@formbricks/types/surveys/types";
|
||||
import { getTextContent, isValidHTML } from "@formbricks/types/surveys/validation";
|
||||
import { TUserLocale } from "@formbricks/types/user";
|
||||
import type { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { isValidHTML } from "@formbricks/types/surveys/validation";
|
||||
import { md } from "@/lib/markdownIt";
|
||||
import { recallToHeadline } from "@/lib/utils/recall";
|
||||
import { isLabelValidForAllLanguages } from "@/modules/survey/editor/lib/validation";
|
||||
import { getElementsFromBlocks } from "@/modules/survey/lib/client-utils";
|
||||
import { Editor } from "@/modules/ui/components/editor";
|
||||
import { LanguageIndicator } from "./language-indicator";
|
||||
|
||||
interface LocalizedEditorProps {
|
||||
id: string;
|
||||
@@ -20,59 +15,35 @@ interface LocalizedEditorProps {
|
||||
localSurvey: TSurvey;
|
||||
isInvalid: boolean;
|
||||
updateElement: any;
|
||||
selectedLanguageCode: string;
|
||||
setSelectedLanguageCode: (languageCode: string) => void;
|
||||
selectedLanguageCode?: string;
|
||||
setSelectedLanguageCode?: (languageCode: string) => void;
|
||||
elementIdx: number;
|
||||
firstRender: boolean;
|
||||
setFirstRender?: Dispatch<SetStateAction<boolean>>;
|
||||
locale: TUserLocale;
|
||||
locale: string;
|
||||
elementId: string;
|
||||
isCard?: boolean; // Flag to indicate if this is a welcome/ending card
|
||||
isCard?: boolean;
|
||||
autoFocus?: boolean;
|
||||
isExternalUrlsAllowed?: boolean;
|
||||
suppressUpdates?: () => boolean; // Function to check if updates should be suppressed (e.g., during deletion)
|
||||
suppressUpdates?: () => boolean;
|
||||
}
|
||||
|
||||
const checkIfValueIsIncomplete = (
|
||||
id: string,
|
||||
isInvalid: boolean,
|
||||
surveyLanguageCodes: TSurveyLanguage[],
|
||||
value?: TI18nString
|
||||
) => {
|
||||
const labelIds = ["subheader", "headline", "html"];
|
||||
if (value === undefined) return false;
|
||||
const isDefaultIncomplete = labelIds.includes(id)
|
||||
? getTextContent(value.default ?? "").trim() !== ""
|
||||
: false;
|
||||
return isInvalid && !isLabelValidForAllLanguages(value, surveyLanguageCodes) && isDefaultIncomplete;
|
||||
};
|
||||
|
||||
export function LocalizedEditor({
|
||||
id,
|
||||
value,
|
||||
localSurvey,
|
||||
isInvalid,
|
||||
updateElement,
|
||||
selectedLanguageCode,
|
||||
setSelectedLanguageCode,
|
||||
selectedLanguageCode = "default",
|
||||
elementIdx,
|
||||
firstRender,
|
||||
setFirstRender,
|
||||
locale,
|
||||
elementId,
|
||||
isCard,
|
||||
autoFocus,
|
||||
isExternalUrlsAllowed,
|
||||
suppressUpdates,
|
||||
autoFocus,
|
||||
}: Readonly<LocalizedEditorProps>) {
|
||||
// Derive elements from blocks for migrated surveys
|
||||
const elements = useMemo(() => getElementsFromBlocks(localSurvey.blocks), [localSurvey.blocks]);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const isInComplete = useMemo(
|
||||
() => checkIfValueIsIncomplete(id, isInvalid, localSurvey.languages, value),
|
||||
[id, isInvalid, localSurvey.languages, value]
|
||||
);
|
||||
|
||||
const [, startTransition] = useTransition();
|
||||
|
||||
@@ -88,10 +59,7 @@ export function LocalizedEditor({
|
||||
const text = value ? (value[selectedLanguageCode] ?? "") : "";
|
||||
let html = md.render(text);
|
||||
|
||||
// For backwards compatibility: wrap plain text headlines in <strong> tags
|
||||
// This ensures old surveys maintain semibold styling when converted to HTML
|
||||
if (id === "headline" && text && !isValidHTML(text)) {
|
||||
// Use [\s\S]*? to match any character including newlines
|
||||
html = html.replaceAll(/<p>([\s\S]*?)<\/p>/g, "<p><strong>$1</strong></p>");
|
||||
}
|
||||
|
||||
@@ -100,8 +68,6 @@ export function LocalizedEditor({
|
||||
key={`${elementId}-${id}-${selectedLanguageCode}`}
|
||||
setFirstRender={setFirstRender}
|
||||
setText={(v: string) => {
|
||||
// Early exit if updates are suppressed (e.g., during deletion)
|
||||
// This prevents race conditions where setText fires with stale props before React updates state
|
||||
if (suppressUpdates?.()) {
|
||||
return;
|
||||
}
|
||||
@@ -114,21 +80,17 @@ export function LocalizedEditor({
|
||||
const currentElement = elements[elementIdx];
|
||||
|
||||
startTransition(() => {
|
||||
// if this is a card, we wanna check if the card exists in the localSurvey
|
||||
if (isCard) {
|
||||
const isWelcomeCard = elementIdx === -1;
|
||||
const isEndingCard = elementIdx >= elements.length;
|
||||
|
||||
// For ending cards, check if the field exists before updating
|
||||
if (isEndingCard) {
|
||||
const ending = localSurvey.endings.find((ending) => ending.id === elementId);
|
||||
// If the field doesn't exist on the ending card, don't create it
|
||||
if ((ending as Record<string, unknown>)?.[id] === undefined) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// For welcome cards, check if it exists
|
||||
if (isWelcomeCard && !localSurvey.welcomeCard) {
|
||||
return;
|
||||
}
|
||||
@@ -141,7 +103,6 @@ export function LocalizedEditor({
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the field exists on the element (not just if it's not undefined)
|
||||
if (
|
||||
currentElement &&
|
||||
id in currentElement &&
|
||||
@@ -160,32 +121,6 @@ export function LocalizedEditor({
|
||||
selectedLanguageCode={selectedLanguageCode}
|
||||
isExternalUrlsAllowed={isExternalUrlsAllowed}
|
||||
/>
|
||||
{localSurvey.languages.length > 1 && (
|
||||
<div>
|
||||
<LanguageIndicator
|
||||
selectedLanguageCode={selectedLanguageCode}
|
||||
setFirstRender={setFirstRender}
|
||||
setSelectedLanguageCode={setSelectedLanguageCode}
|
||||
surveyLanguages={localSurvey.languages}
|
||||
locale={locale}
|
||||
/>
|
||||
|
||||
{value && selectedLanguageCode !== "default" && value.default ? (
|
||||
<div className="mt-1 flex text-xs text-gray-500">
|
||||
<strong>{t("environments.workspace.languages.translate")}:</strong>
|
||||
<span className="ml-1">
|
||||
{getTextContent(recallToHeadline(value, localSurvey, false, "default").default ?? "")}
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isInComplete ? (
|
||||
<div className="mt-1 text-xs text-red-400">
|
||||
{t("environments.workspace.languages.incomplete_translations")}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
"use client";
|
||||
|
||||
import { ChevronDownIcon } from "lucide-react";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { getTextContent } from "@formbricks/types/surveys/validation";
|
||||
import { cn } from "@/lib/cn";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogBody,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/modules/ui/components/dialog";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/modules/ui/components/dropdown-menu";
|
||||
import { type TranslatableString } from "../lib/types";
|
||||
import {
|
||||
extractTranslatableStrings,
|
||||
getProgressColor,
|
||||
getProgressTextColor,
|
||||
setTranslationAtPathMutable,
|
||||
} from "../lib/utils";
|
||||
import { TranslationRow } from "./translation-row";
|
||||
|
||||
interface ManageTranslationsModalProps {
|
||||
open: boolean;
|
||||
setOpen: (open: boolean) => void;
|
||||
localSurvey: TSurvey;
|
||||
setLocalSurvey: (survey: TSurvey) => void;
|
||||
languageCode: string;
|
||||
languageName: string;
|
||||
defaultLanguageName: string;
|
||||
}
|
||||
|
||||
export const ManageTranslationsModal = ({
|
||||
open,
|
||||
setOpen,
|
||||
localSurvey,
|
||||
setLocalSurvey,
|
||||
languageCode,
|
||||
languageName,
|
||||
defaultLanguageName,
|
||||
}: ManageTranslationsModalProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const strings = useMemo(() => extractTranslatableStrings(localSurvey, t), [localSurvey, t]);
|
||||
|
||||
const [draftTranslations, setDraftTranslations] = useState<Record<string, string>>({});
|
||||
const [missingFirst, setMissingFirst] = useState(false);
|
||||
|
||||
// Initialize drafts when modal opens
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
const drafts: Record<string, string> = {};
|
||||
for (const s of strings) {
|
||||
drafts[s.path] = s.value[languageCode] ?? "";
|
||||
}
|
||||
setDraftTranslations(drafts);
|
||||
}
|
||||
}, [open, strings, languageCode]);
|
||||
|
||||
const isDraftEmpty = useCallback(
|
||||
(s: TranslatableString) => {
|
||||
const val = draftTranslations[s.path];
|
||||
if (val === undefined || val === "") return true;
|
||||
// Rich text editors output HTML like "<p></p>" when empty — strip tags before checking
|
||||
const text = s.isRichText ? getTextContent(val) : val;
|
||||
return !text.trim();
|
||||
},
|
||||
[draftTranslations]
|
||||
);
|
||||
|
||||
const progress = useMemo(() => {
|
||||
const total = strings.length;
|
||||
if (total === 0) return { translated: 0, total: 0, percentage: 100 };
|
||||
const translated = strings.filter((s) => !isDraftEmpty(s)).length;
|
||||
const percentage = Math.round((translated / total) * 100);
|
||||
return { translated, total, percentage };
|
||||
}, [isDraftEmpty, strings]);
|
||||
|
||||
// Snapshot the sort order so rows don't jump while the user is typing.
|
||||
// Re-sort only when the sort mode is toggled or the underlying strings change.
|
||||
const displayStrings = useMemo(() => {
|
||||
if (!missingFirst) return strings;
|
||||
return [...strings].sort((a, b) => {
|
||||
const aEmpty = isDraftEmpty(a);
|
||||
const bEmpty = isDraftEmpty(b);
|
||||
if (aEmpty && !bEmpty) return -1;
|
||||
if (!aEmpty && bEmpty) return 1;
|
||||
return 0;
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [strings, missingFirst]);
|
||||
|
||||
// Merge draft translations into localSurvey so that the recall dropdown
|
||||
// can see in-progress translations (e.g. translated headlines).
|
||||
const mergedSurvey = useMemo(() => {
|
||||
const clone = structuredClone(localSurvey);
|
||||
for (const s of strings) {
|
||||
const val = draftTranslations[s.path] ?? "";
|
||||
if (val) {
|
||||
setTranslationAtPathMutable(clone, s.path, languageCode, val);
|
||||
}
|
||||
}
|
||||
return clone;
|
||||
}, [localSurvey, strings, draftTranslations, languageCode]);
|
||||
|
||||
const handleDraftChange = useCallback((path: string, value: string) => {
|
||||
setDraftTranslations((prev) => ({ ...prev, [path]: value }));
|
||||
}, []);
|
||||
|
||||
const handleSave = () => {
|
||||
const updatedSurvey = structuredClone(localSurvey);
|
||||
for (const s of strings) {
|
||||
const val = draftTranslations[s.path] ?? "";
|
||||
setTranslationAtPathMutable(updatedSurvey, s.path, languageCode, val);
|
||||
}
|
||||
setLocalSurvey(updatedSurvey);
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
const progressColor = getProgressColor(progress.percentage);
|
||||
const progressTextColor = getProgressTextColor(progress.percentage);
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogContent width="wide" className="max-h-[85dvh]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t("environments.surveys.edit.manage_translations")}</DialogTitle>
|
||||
<div className="mt-2 flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="h-1.5 w-20 overflow-hidden rounded-full bg-slate-200">
|
||||
<div
|
||||
className={cn("h-full rounded-full transition-all", progressColor)}
|
||||
style={{ width: `${progress.percentage}%` }}
|
||||
/>
|
||||
</div>
|
||||
<span className={cn("text-sm font-medium", progressTextColor)}>
|
||||
{progress.translated}/{progress.total}
|
||||
</span>
|
||||
</div>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="secondary" size="sm" className="text-xs">
|
||||
{missingFirst
|
||||
? t("environments.surveys.edit.missing_first")
|
||||
: t("environments.surveys.edit.show_in_order")}
|
||||
<ChevronDownIcon className="ml-1 h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="text-xs">
|
||||
<DropdownMenuItem
|
||||
className={cn(!missingFirst && "font-semibold", "text-xs")}
|
||||
onSelect={() => setMissingFirst(false)}>
|
||||
{t("environments.surveys.edit.show_in_order")}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className={cn(missingFirst && "font-semibold", "text-xs")}
|
||||
onSelect={() => setMissingFirst(true)}>
|
||||
{t("environments.surveys.edit.missing_first")}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</DialogHeader>
|
||||
|
||||
<DialogBody>
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b text-left text-slate-500">
|
||||
<th className="w-16 py-2 pr-2 font-medium">{t("common.id")}</th>
|
||||
<th className="w-1/3 py-2 pr-2 font-medium">{defaultLanguageName}</th>
|
||||
<th className="py-2 font-medium">{languageName}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{displayStrings.map((s) => (
|
||||
<TranslationRow
|
||||
key={s.path}
|
||||
s={s}
|
||||
value={draftTranslations[s.path] ?? ""}
|
||||
onChange={handleDraftChange}
|
||||
localSurvey={mergedSurvey}
|
||||
languageCode={languageCode}
|
||||
/>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</DialogBody>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="secondary" size="sm" onClick={handleCancel}>
|
||||
{t("common.cancel")}
|
||||
</Button>
|
||||
<Button size="sm" onClick={handleSave}>
|
||||
{t("common.save")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -1,300 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import { Language } from "@prisma/client";
|
||||
import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
import { ArrowUpRight, Languages } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import type { FC } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { TSurvey, TSurveyLanguage } from "@formbricks/types/surveys/types";
|
||||
import { TUserLocale } from "@formbricks/types/user";
|
||||
import { cn } from "@/lib/cn";
|
||||
import { addMultiLanguageLabels, extractLanguageCodes, getEnabledLanguages } from "@/lib/i18n/utils";
|
||||
import { AdvancedOptionToggle } from "@/modules/ui/components/advanced-option-toggle";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { ConfirmationModal } from "@/modules/ui/components/confirmation-modal";
|
||||
import { Label } from "@/modules/ui/components/label";
|
||||
import { Switch } from "@/modules/ui/components/switch";
|
||||
import { DefaultLanguageSelect } from "./default-language-select";
|
||||
import { SecondaryLanguageSelect } from "./secondary-language-select";
|
||||
|
||||
interface MultiLanguageCardProps {
|
||||
localSurvey: TSurvey;
|
||||
projectLanguages: Language[];
|
||||
setLocalSurvey: (survey: TSurvey) => void;
|
||||
activeElementId: string | null;
|
||||
setActiveElementId: (elementId: string | null) => void;
|
||||
setSelectedLanguageCode: (language: string) => void;
|
||||
locale: TUserLocale;
|
||||
}
|
||||
|
||||
export interface ConfirmationModalProps {
|
||||
body: string;
|
||||
open: boolean;
|
||||
title: string;
|
||||
buttonText: string;
|
||||
buttonVariant?: "default" | "destructive";
|
||||
onConfirm: () => void;
|
||||
}
|
||||
|
||||
export const MultiLanguageCard: FC<MultiLanguageCardProps> = ({
|
||||
activeElementId,
|
||||
localSurvey,
|
||||
setActiveElementId,
|
||||
setLocalSurvey,
|
||||
projectLanguages,
|
||||
setSelectedLanguageCode,
|
||||
locale,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const environmentId = localSurvey.environmentId;
|
||||
const open = activeElementId === "multiLanguage";
|
||||
const [isMultiLanguageActivated, setIsMultiLanguageActivated] = useState(localSurvey.languages.length > 0);
|
||||
const [confirmationModalInfo, setConfirmationModalInfo] = useState<ConfirmationModalProps>({
|
||||
title: "",
|
||||
open: false,
|
||||
body: "",
|
||||
buttonText: "",
|
||||
onConfirm: () => {},
|
||||
});
|
||||
|
||||
const defaultLanguage = useMemo(
|
||||
() => localSurvey.languages.find((language) => language.default)?.language,
|
||||
[localSurvey.languages]
|
||||
);
|
||||
|
||||
const setOpen = (open: boolean) => {
|
||||
if (open) {
|
||||
setActiveElementId("multiLanguage");
|
||||
} else {
|
||||
setActiveElementId(null);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (localSurvey.languages.length === 0) {
|
||||
setIsMultiLanguageActivated(false);
|
||||
}
|
||||
}, [localSurvey.languages]);
|
||||
|
||||
const updateSurveyTranslations = (survey: TSurvey, updatedLanguages: TSurveyLanguage[]) => {
|
||||
const translatedSurveyResult = addMultiLanguageLabels(survey, extractLanguageCodes(updatedLanguages));
|
||||
|
||||
const updatedSurvey = { ...translatedSurveyResult, languages: updatedLanguages };
|
||||
setLocalSurvey(updatedSurvey as TSurvey);
|
||||
};
|
||||
|
||||
const updateSurveyLanguages = (language: Language) => {
|
||||
let updatedLanguages = localSurvey.languages;
|
||||
const languageIndex = localSurvey.languages.findIndex(
|
||||
(surveyLanguage) => surveyLanguage.language.code === language.code
|
||||
);
|
||||
if (languageIndex >= 0) {
|
||||
// Toggle the 'enabled' property of the existing language
|
||||
updatedLanguages = updatedLanguages.map((surveyLanguage, index) =>
|
||||
index === languageIndex ? { ...surveyLanguage, enabled: !surveyLanguage.enabled } : surveyLanguage
|
||||
);
|
||||
} else {
|
||||
// Add the new language
|
||||
updatedLanguages = [
|
||||
...updatedLanguages,
|
||||
{
|
||||
enabled: true,
|
||||
default: false,
|
||||
language,
|
||||
},
|
||||
];
|
||||
}
|
||||
updateSurveyTranslations(localSurvey, updatedLanguages);
|
||||
};
|
||||
|
||||
const updateSurvey = (data: { languages: TSurveyLanguage[] }) => {
|
||||
setLocalSurvey({ ...localSurvey, ...data });
|
||||
};
|
||||
|
||||
const handleDefaultLanguageChange = (languageCode: string) => {
|
||||
const language = projectLanguages.find((lang) => lang.code === languageCode);
|
||||
if (language) {
|
||||
let languageExists = false;
|
||||
|
||||
// Update all languages and check if the new default language already exists
|
||||
const newLanguages =
|
||||
localSurvey.languages.map((lang) => {
|
||||
if (lang.language.code === language.code) {
|
||||
languageExists = true;
|
||||
return { ...lang, default: true };
|
||||
}
|
||||
return { ...lang, default: false };
|
||||
}) ?? [];
|
||||
|
||||
if (!languageExists) {
|
||||
// If the language doesn't exist, add it as the default
|
||||
newLanguages.push({
|
||||
enabled: true,
|
||||
default: true,
|
||||
language,
|
||||
});
|
||||
}
|
||||
|
||||
setConfirmationModalInfo({ ...confirmationModalInfo, open: false });
|
||||
updateSurvey({ languages: newLanguages });
|
||||
}
|
||||
};
|
||||
|
||||
const handleActivationSwitchLogic = () => {
|
||||
if (isMultiLanguageActivated) {
|
||||
if (localSurvey.languages.length > 0) {
|
||||
setConfirmationModalInfo({
|
||||
open: true,
|
||||
title: t("environments.surveys.edit.remove_translations"),
|
||||
body: t("environments.surveys.edit.this_action_will_remove_all_the_translations_from_this_survey"),
|
||||
buttonText: t("environments.surveys.edit.remove_translations"),
|
||||
buttonVariant: "destructive",
|
||||
onConfirm: () => {
|
||||
updateSurveyTranslations(localSurvey, []);
|
||||
setIsMultiLanguageActivated(false);
|
||||
setConfirmationModalInfo({ ...confirmationModalInfo, open: false });
|
||||
},
|
||||
});
|
||||
} else {
|
||||
setIsMultiLanguageActivated(false);
|
||||
}
|
||||
} else {
|
||||
setIsMultiLanguageActivated(true);
|
||||
if (!open) {
|
||||
setOpen(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleLanguageSwitchToggle = () => {
|
||||
setLocalSurvey({ ...localSurvey, showLanguageSwitch: !localSurvey.showLanguageSwitch });
|
||||
};
|
||||
|
||||
const [parent] = useAutoAnimate();
|
||||
|
||||
const enabledLanguages = getEnabledLanguages(localSurvey.languages);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
open ? "shadow-lg" : "shadow-md",
|
||||
"group z-10 flex flex-row rounded-lg bg-white text-slate-900"
|
||||
)}>
|
||||
<div
|
||||
className={cn(
|
||||
open ? "bg-slate-50" : "bg-white group-hover:bg-slate-50",
|
||||
"flex w-10 items-center justify-center rounded-l-lg border-b border-l border-t group-aria-expanded:rounded-bl-none"
|
||||
)}>
|
||||
<p>
|
||||
<Languages className="h-6 w-6 rounded-full bg-indigo-500 p-1 text-white" />
|
||||
</p>
|
||||
</div>
|
||||
<Collapsible.Root
|
||||
className="flex-1 rounded-r-lg border border-slate-200 transition-all duration-300 ease-in-out"
|
||||
onOpenChange={setOpen}
|
||||
open={open}>
|
||||
<Collapsible.CollapsibleTrigger
|
||||
asChild
|
||||
className="flex cursor-pointer justify-between rounded-r-lg p-4 hover:bg-slate-50">
|
||||
<div>
|
||||
<div className="inline-flex">
|
||||
<div>
|
||||
<p className="text-sm font-semibold">{t("common.multiple_languages")}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<Label htmlFor="multi-lang-toggle">
|
||||
{isMultiLanguageActivated ? t("common.on") : t("common.off")}
|
||||
</Label>
|
||||
|
||||
<Switch
|
||||
checked={isMultiLanguageActivated}
|
||||
disabled={projectLanguages.length === 0}
|
||||
id="multi-lang-toggle"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleActivationSwitchLogic();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Collapsible.CollapsibleTrigger>
|
||||
<Collapsible.CollapsibleContent className={`flex flex-col px-4 ${open && "pb-6"}`} ref={parent}>
|
||||
<div className="space-y-6 pt-3">
|
||||
{projectLanguages.length === 0 && (
|
||||
<div className="mb-4 text-sm italic text-slate-500">
|
||||
{t("environments.surveys.edit.no_languages_found_add_first_one_to_get_started")}
|
||||
</div>
|
||||
)}
|
||||
{projectLanguages.length > 0 && (
|
||||
<div className="space-y-6">
|
||||
{isMultiLanguageActivated ? (
|
||||
<div className="space-y-6">
|
||||
<DefaultLanguageSelect
|
||||
defaultLanguage={defaultLanguage}
|
||||
handleDefaultLanguageChange={handleDefaultLanguageChange}
|
||||
projectLanguages={projectLanguages}
|
||||
setConfirmationModalInfo={setConfirmationModalInfo}
|
||||
locale={locale}
|
||||
/>
|
||||
{defaultLanguage && projectLanguages.length > 1 ? (
|
||||
<SecondaryLanguageSelect
|
||||
defaultLanguage={defaultLanguage}
|
||||
localSurvey={localSurvey}
|
||||
projectLanguages={projectLanguages}
|
||||
setActiveElementId={setActiveElementId}
|
||||
setSelectedLanguageCode={setSelectedLanguageCode}
|
||||
updateSurveyLanguages={updateSurveyLanguages}
|
||||
locale={locale}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-sm italic text-slate-500">
|
||||
{t("environments.surveys.edit.switch_multi_language_on_to_get_started")}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Button asChild size="sm" variant="secondary">
|
||||
<Link href={`/environments/${environmentId}/workspace/languages`} target="_blank">
|
||||
{t("environments.surveys.edit.manage_languages")}
|
||||
<ArrowUpRight />
|
||||
</Link>
|
||||
</Button>
|
||||
{isMultiLanguageActivated && (
|
||||
<AdvancedOptionToggle
|
||||
customContainerClass="px-0 pt-0"
|
||||
htmlId="languageSwitch"
|
||||
disabled={enabledLanguages.length <= 1}
|
||||
isChecked={!!localSurvey.showLanguageSwitch}
|
||||
onToggle={handleLanguageSwitchToggle}
|
||||
title={t("environments.surveys.edit.show_language_switch")}
|
||||
description={t(
|
||||
"environments.surveys.edit.enable_participants_to_switch_the_survey_language_at_any_point_during_the_survey"
|
||||
)}
|
||||
childBorder={true}></AdvancedOptionToggle>
|
||||
)}
|
||||
|
||||
<ConfirmationModal
|
||||
buttonText={confirmationModalInfo.buttonText}
|
||||
buttonVariant={confirmationModalInfo.buttonVariant}
|
||||
onConfirm={confirmationModalInfo.onConfirm}
|
||||
open={confirmationModalInfo.open}
|
||||
setOpen={() => {
|
||||
setConfirmationModalInfo((prev) => ({ ...prev, open: !prev.open }));
|
||||
}}
|
||||
body={confirmationModalInfo.body}
|
||||
title={confirmationModalInfo.title}
|
||||
/>
|
||||
</div>
|
||||
</Collapsible.CollapsibleContent>
|
||||
</Collapsible.Root>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { md } from "@/lib/markdownIt";
|
||||
import { Editor } from "@/modules/ui/components/editor";
|
||||
|
||||
interface RichTextTranslationInputProps {
|
||||
path: string;
|
||||
value: string;
|
||||
onChange: (path: string, value: string) => void;
|
||||
localSurvey: TSurvey;
|
||||
languageCode: string;
|
||||
elementId: string;
|
||||
}
|
||||
|
||||
export const RichTextTranslationInput = ({
|
||||
path,
|
||||
value,
|
||||
onChange,
|
||||
localSurvey,
|
||||
languageCode,
|
||||
elementId,
|
||||
}: RichTextTranslationInputProps) => {
|
||||
const [firstRender, setFirstRender] = useState(true);
|
||||
|
||||
return (
|
||||
<div className="rounded-md">
|
||||
<Editor
|
||||
key={path}
|
||||
disableLists
|
||||
excludedToolbarItems={["blockType"]}
|
||||
firstRender={firstRender}
|
||||
setFirstRender={setFirstRender}
|
||||
getText={() => md.render(value)}
|
||||
setText={(v: string) => onChange(path, v)}
|
||||
localSurvey={localSurvey}
|
||||
elementId={elementId}
|
||||
selectedLanguageCode={languageCode}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Language } from "@prisma/client";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { TUserLocale } from "@formbricks/types/user";
|
||||
import { getElementsFromBlocks } from "@/modules/survey/lib/client-utils";
|
||||
import { LanguageToggle } from "./language-toggle";
|
||||
|
||||
interface SecondaryLanguageSelectProps {
|
||||
projectLanguages: Language[];
|
||||
defaultLanguage: Language;
|
||||
setSelectedLanguageCode: (languageCode: string) => void;
|
||||
setActiveElementId: (elementId: string) => void;
|
||||
localSurvey: TSurvey;
|
||||
updateSurveyLanguages: (language: Language) => void;
|
||||
locale: TUserLocale;
|
||||
}
|
||||
|
||||
export function SecondaryLanguageSelect({
|
||||
projectLanguages,
|
||||
defaultLanguage,
|
||||
setSelectedLanguageCode,
|
||||
setActiveElementId,
|
||||
localSurvey,
|
||||
updateSurveyLanguages,
|
||||
locale,
|
||||
}: SecondaryLanguageSelectProps) {
|
||||
const { t } = useTranslation();
|
||||
const isLanguageToggled = (language: Language) => {
|
||||
return localSurvey.languages.some(
|
||||
(surveyLanguage) => surveyLanguage.language.code === language.code && surveyLanguage.enabled
|
||||
);
|
||||
};
|
||||
|
||||
const elements = getElementsFromBlocks(localSurvey.blocks);
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm font-medium text-slate-800">
|
||||
{t("environments.surveys.edit.2_activate_translation_for_specific_languages")}
|
||||
</p>{" "}
|
||||
{projectLanguages
|
||||
.filter((lang) => lang.id !== defaultLanguage.id)
|
||||
.map((language) => (
|
||||
<LanguageToggle
|
||||
isChecked={isLanguageToggled(language)}
|
||||
key={language.id}
|
||||
language={language}
|
||||
onEdit={() => {
|
||||
setSelectedLanguageCode(language.code);
|
||||
setActiveElementId(elements[0]?.id);
|
||||
}}
|
||||
onToggle={() => {
|
||||
updateSurveyLanguages(language);
|
||||
}}
|
||||
locale={locale}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
"use client";
|
||||
|
||||
import DOMPurify from "isomorphic-dompurify";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { Input } from "@/modules/ui/components/input";
|
||||
import { type TranslatableString } from "../lib/types";
|
||||
import { RichTextTranslationInput } from "./rich-text-translation-input";
|
||||
|
||||
interface TranslationRowProps {
|
||||
s: TranslatableString;
|
||||
value: string;
|
||||
onChange: (path: string, value: string) => void;
|
||||
localSurvey: TSurvey;
|
||||
languageCode: string;
|
||||
}
|
||||
|
||||
const DefaultTextCell = ({
|
||||
text,
|
||||
isRichText,
|
||||
noTextLabel,
|
||||
}: {
|
||||
text: string;
|
||||
isRichText: boolean;
|
||||
noTextLabel: string;
|
||||
}) => {
|
||||
if (!text) {
|
||||
return <div className="text-sm italic text-slate-400">{noTextLabel}</div>;
|
||||
}
|
||||
|
||||
if (isRichText) {
|
||||
return (
|
||||
<div
|
||||
className="text-sm text-slate-700"
|
||||
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(text) }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <div className="text-sm text-slate-700">{text}</div>;
|
||||
};
|
||||
|
||||
export const TranslationRow = ({ s, value, onChange, localSurvey, languageCode }: TranslationRowProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const defaultText = s.value.default || "";
|
||||
|
||||
return (
|
||||
<tr className="border-b last:border-b-0" data-testid={`translation-row-${s.path}`}>
|
||||
<td className="py-2 pr-2 align-top">
|
||||
<span className="inline-block rounded bg-slate-100 px-1.5 py-0.5 text-xs font-medium text-slate-600">
|
||||
{s.displayId}
|
||||
</span>
|
||||
<div className="mt-0.5 text-[10px] text-slate-400">{s.fieldLabel}</div>
|
||||
</td>
|
||||
<td className="py-2 pr-2 align-top">
|
||||
<DefaultTextCell
|
||||
text={defaultText}
|
||||
isRichText={s.isRichText}
|
||||
noTextLabel={t("common.no_text_found")}
|
||||
/>
|
||||
</td>
|
||||
<td className="py-2 align-top">
|
||||
{s.isRichText ? (
|
||||
<RichTextTranslationInput
|
||||
path={s.path}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
localSurvey={localSurvey}
|
||||
languageCode={languageCode}
|
||||
elementId={s.elementId}
|
||||
/>
|
||||
) : (
|
||||
<Input
|
||||
dir="auto"
|
||||
className="text-sm"
|
||||
value={value}
|
||||
onChange={(e) => onChange(s.path, e.target.value)}
|
||||
placeholder=""
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
import { TI18nString } from "@formbricks/types/i18n";
|
||||
|
||||
export interface TranslatableString {
|
||||
path: string;
|
||||
displayId: string;
|
||||
fieldLabel: string;
|
||||
value: TI18nString;
|
||||
isRichText: boolean;
|
||||
elementId: string;
|
||||
}
|
||||
|
||||
export interface TranslationProgress {
|
||||
translated: number;
|
||||
total: number;
|
||||
percentage: number;
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
import { type TFunction } from "i18next";
|
||||
import { TSurveyElementTypeEnum } from "@formbricks/types/surveys/constants";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { getTextContent } from "@formbricks/types/surveys/validation";
|
||||
import { isI18nObject } from "@/lib/i18n/utils";
|
||||
import type { TranslatableString, TranslationProgress } from "./types";
|
||||
|
||||
const RICH_TEXT_FIELDS = new Set(["headline", "subheader", "html"]);
|
||||
|
||||
const pushIfI18n = (
|
||||
result: TranslatableString[],
|
||||
obj: unknown,
|
||||
field: string,
|
||||
path: string,
|
||||
displayId: string,
|
||||
fieldLabel: string,
|
||||
elementId: string
|
||||
) => {
|
||||
const val = (obj as Record<string, unknown>)?.[field];
|
||||
if (val && isI18nObject(val)) {
|
||||
const defaultText = val.default ?? "";
|
||||
const isRichText = RICH_TEXT_FIELDS.has(field);
|
||||
const hasContent = isRichText ? getTextContent(defaultText).trim() !== "" : defaultText.trim() !== "";
|
||||
if (!hasContent) return;
|
||||
|
||||
result.push({
|
||||
path: `${path}.${field}`,
|
||||
displayId,
|
||||
fieldLabel,
|
||||
value: val,
|
||||
isRichText,
|
||||
elementId,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const extractTranslatableStrings = (survey: TSurvey, t: TFunction): TranslatableString[] => {
|
||||
const result: TranslatableString[] = [];
|
||||
|
||||
// Welcome card
|
||||
if (survey.welcomeCard.enabled) {
|
||||
const base = "welcomeCard";
|
||||
const did = "W";
|
||||
const eid = "start";
|
||||
pushIfI18n(result, survey.welcomeCard, "headline", base, did, t("common.headline"), eid);
|
||||
pushIfI18n(result, survey.welcomeCard, "subheader", base, did, t("common.subheader"), eid);
|
||||
pushIfI18n(
|
||||
result,
|
||||
survey.welcomeCard,
|
||||
"buttonLabel",
|
||||
base,
|
||||
did,
|
||||
t("environments.surveys.edit.button_label"),
|
||||
eid
|
||||
);
|
||||
}
|
||||
|
||||
// Blocks → elements
|
||||
survey.blocks.forEach((block, blockIdx) => {
|
||||
// Block-level fields
|
||||
pushIfI18n(
|
||||
result,
|
||||
block,
|
||||
"buttonLabel",
|
||||
`blocks.${blockIdx}`,
|
||||
`${blockIdx + 1}`,
|
||||
t("environments.surveys.edit.button_label"),
|
||||
block.id
|
||||
);
|
||||
pushIfI18n(
|
||||
result,
|
||||
block,
|
||||
"backButtonLabel",
|
||||
`blocks.${blockIdx}`,
|
||||
`${blockIdx + 1}`,
|
||||
t("environments.surveys.edit.back_button_label"),
|
||||
block.id
|
||||
);
|
||||
|
||||
block.elements.forEach((element, elementIdx) => {
|
||||
const base = `blocks.${blockIdx}.elements.${elementIdx}`;
|
||||
const did = `${blockIdx + 1}.${elementIdx + 1}`;
|
||||
const eid = element.id;
|
||||
|
||||
// Common fields
|
||||
pushIfI18n(result, element, "headline", base, did, t("common.headline"), eid);
|
||||
pushIfI18n(result, element, "subheader", base, did, t("common.subheader"), eid);
|
||||
|
||||
// Type-specific fields
|
||||
switch (element.type) {
|
||||
case TSurveyElementTypeEnum.OpenText:
|
||||
pushIfI18n(result, element, "placeholder", base, did, t("common.placeholder"), eid);
|
||||
break;
|
||||
case TSurveyElementTypeEnum.Consent:
|
||||
pushIfI18n(result, element, "label", base, did, t("common.label"), eid);
|
||||
break;
|
||||
case TSurveyElementTypeEnum.MultipleChoiceSingle:
|
||||
case TSurveyElementTypeEnum.MultipleChoiceMulti: {
|
||||
element.choices?.forEach((choice, ci) => {
|
||||
if (isI18nObject(choice.label) && (choice.label.default ?? "").trim()) {
|
||||
result.push({
|
||||
path: `${base}.choices.${ci}.label`,
|
||||
displayId: did,
|
||||
fieldLabel: t("common.choice_n", { n: ci + 1 }),
|
||||
value: choice.label,
|
||||
isRichText: false,
|
||||
elementId: eid,
|
||||
});
|
||||
}
|
||||
});
|
||||
pushIfI18n(
|
||||
result,
|
||||
element,
|
||||
"otherOptionPlaceholder",
|
||||
base,
|
||||
did,
|
||||
t("common.other_placeholder"),
|
||||
eid
|
||||
);
|
||||
break;
|
||||
}
|
||||
case TSurveyElementTypeEnum.NPS:
|
||||
case TSurveyElementTypeEnum.Rating:
|
||||
pushIfI18n(
|
||||
result,
|
||||
element,
|
||||
"lowerLabel",
|
||||
base,
|
||||
did,
|
||||
t("environments.surveys.edit.lower_label"),
|
||||
eid
|
||||
);
|
||||
pushIfI18n(
|
||||
result,
|
||||
element,
|
||||
"upperLabel",
|
||||
base,
|
||||
did,
|
||||
t("environments.surveys.edit.upper_label"),
|
||||
eid
|
||||
);
|
||||
break;
|
||||
case TSurveyElementTypeEnum.CTA:
|
||||
pushIfI18n(
|
||||
result,
|
||||
element,
|
||||
"ctaButtonLabel",
|
||||
base,
|
||||
did,
|
||||
t("environments.surveys.edit.cta_button_label"),
|
||||
eid
|
||||
);
|
||||
break;
|
||||
case TSurveyElementTypeEnum.Date:
|
||||
pushIfI18n(result, element, "html", base, did, t("common.html"), eid);
|
||||
break;
|
||||
case TSurveyElementTypeEnum.Matrix: {
|
||||
element.rows?.forEach((row, ri) => {
|
||||
if (isI18nObject(row.label) && (row.label.default ?? "").trim()) {
|
||||
result.push({
|
||||
path: `${base}.rows.${ri}.label`,
|
||||
displayId: did,
|
||||
fieldLabel: t("common.row_n", { n: ri + 1 }),
|
||||
value: row.label,
|
||||
isRichText: false,
|
||||
elementId: eid,
|
||||
});
|
||||
}
|
||||
});
|
||||
element.columns?.forEach((col, ci) => {
|
||||
if (isI18nObject(col.label) && (col.label.default ?? "").trim()) {
|
||||
result.push({
|
||||
path: `${base}.columns.${ci}.label`,
|
||||
displayId: did,
|
||||
fieldLabel: t("common.column_n", { n: ci + 1 }),
|
||||
value: col.label,
|
||||
isRichText: false,
|
||||
elementId: eid,
|
||||
});
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case TSurveyElementTypeEnum.Address: {
|
||||
const addrFields = ["addressLine1", "addressLine2", "city", "state", "zip", "country"] as const;
|
||||
addrFields.forEach((f) => {
|
||||
const sub = element[f];
|
||||
if (sub?.placeholder && isI18nObject(sub.placeholder) && (sub.placeholder.default ?? "").trim()) {
|
||||
result.push({
|
||||
path: `${base}.${f}.placeholder`,
|
||||
displayId: did,
|
||||
fieldLabel: t("common.field_placeholder", { field: f }),
|
||||
value: sub.placeholder,
|
||||
isRichText: false,
|
||||
elementId: eid,
|
||||
});
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case TSurveyElementTypeEnum.ContactInfo: {
|
||||
const contactFields = ["firstName", "lastName", "email", "phone", "company"] as const;
|
||||
contactFields.forEach((f) => {
|
||||
const sub = element[f];
|
||||
if (sub?.placeholder && isI18nObject(sub.placeholder) && (sub.placeholder.default ?? "").trim()) {
|
||||
result.push({
|
||||
path: `${base}.${f}.placeholder`,
|
||||
displayId: did,
|
||||
fieldLabel: t("common.field_placeholder", { field: f }),
|
||||
value: sub.placeholder,
|
||||
isRichText: false,
|
||||
elementId: eid,
|
||||
});
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case TSurveyElementTypeEnum.Ranking: {
|
||||
element.choices?.forEach((choice, ci) => {
|
||||
if (isI18nObject(choice.label) && (choice.label.default ?? "").trim()) {
|
||||
result.push({
|
||||
path: `${base}.choices.${ci}.label`,
|
||||
displayId: did,
|
||||
fieldLabel: t("common.choice_n", { n: ci + 1 }),
|
||||
isRichText: false,
|
||||
value: choice.label,
|
||||
elementId: eid,
|
||||
});
|
||||
}
|
||||
});
|
||||
pushIfI18n(
|
||||
result,
|
||||
element,
|
||||
"otherOptionPlaceholder",
|
||||
base,
|
||||
did,
|
||||
t("common.other_placeholder"),
|
||||
eid
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Endings
|
||||
survey.endings.forEach((ending, endingIdx) => {
|
||||
if (ending.type === "endScreen") {
|
||||
const base = `endings.${endingIdx}`;
|
||||
const did = `E${endingIdx + 1}`;
|
||||
const eid = ending.id;
|
||||
pushIfI18n(result, ending, "headline", base, did, t("common.headline"), eid);
|
||||
pushIfI18n(result, ending, "subheader", base, did, t("common.subheader"), eid);
|
||||
pushIfI18n(result, ending, "buttonLabel", base, did, t("environments.surveys.edit.button_label"), eid);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const computeTranslationProgress = (
|
||||
strings: TranslatableString[],
|
||||
languageCode: string
|
||||
): TranslationProgress => {
|
||||
const total = strings.length;
|
||||
if (total === 0) return { translated: 0, total: 0, percentage: 100 };
|
||||
const translated = strings.filter((s) => {
|
||||
const val = s.value[languageCode];
|
||||
if (val === undefined || val === "") return false;
|
||||
const text = s.isRichText ? getTextContent(val) : val;
|
||||
return text.trim() !== "";
|
||||
}).length;
|
||||
const percentage = Math.round((translated / total) * 100);
|
||||
return { translated, total, percentage };
|
||||
};
|
||||
|
||||
export const getProgressColor = (percentage: number): string => {
|
||||
if (percentage < 10) return "bg-red-500";
|
||||
if (percentage < 25) return "bg-orange-700";
|
||||
if (percentage <= 45) return "bg-orange-500";
|
||||
if (percentage <= 75) return "bg-green-400";
|
||||
return "bg-green-600";
|
||||
};
|
||||
|
||||
export const getProgressTextColor = (percentage: number): string => {
|
||||
if (percentage < 10) return "text-red-600";
|
||||
if (percentage < 25) return "text-orange-700";
|
||||
if (percentage <= 45) return "text-orange-500";
|
||||
if (percentage <= 75) return "text-green-500";
|
||||
return "text-green-600";
|
||||
};
|
||||
|
||||
export const removeLanguageKeysFromSurvey = (survey: TSurvey, languageCode: string): TSurvey => {
|
||||
const clone = structuredClone(survey);
|
||||
|
||||
function processObject(obj: unknown) {
|
||||
if (Array.isArray(obj)) {
|
||||
obj.forEach(processObject);
|
||||
} else if (obj && typeof obj === "object") {
|
||||
const record = obj as Record<string, unknown>;
|
||||
for (const key in record) {
|
||||
if (record.hasOwnProperty(key)) {
|
||||
if (key === "default" && typeof record[key] === "string") {
|
||||
delete record[languageCode];
|
||||
return;
|
||||
} else {
|
||||
processObject(record[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processObject(clone);
|
||||
return clone;
|
||||
};
|
||||
|
||||
type Traversable = Record<string, unknown> | unknown[];
|
||||
|
||||
const isTraversable = (val: unknown): val is Traversable => val !== null && typeof val === "object";
|
||||
|
||||
/**
|
||||
* Mutates the given survey in-place, setting a translation value at the
|
||||
* specified path. Use this inside a loop after cloning once upfront.
|
||||
*/
|
||||
export const setTranslationAtPathMutable = (
|
||||
survey: TSurvey,
|
||||
path: string,
|
||||
languageCode: string,
|
||||
value: string
|
||||
): void => {
|
||||
const parts = path.split(".");
|
||||
if (parts.length === 0) return;
|
||||
|
||||
let current: Traversable = survey;
|
||||
|
||||
for (let i = 0; i < parts.length - 1; i++) {
|
||||
const part = parts[i];
|
||||
const next: unknown = Array.isArray(current) ? current[Number(part)] : current[part];
|
||||
if (!isTraversable(next)) return;
|
||||
current = next;
|
||||
}
|
||||
|
||||
const lastPart = parts.at(-1);
|
||||
if (!lastPart || Array.isArray(current)) return;
|
||||
|
||||
const target = current[lastPart];
|
||||
if (isTraversable(target) && !Array.isArray(target) && "default" in target) {
|
||||
(target as Record<string, string>)[languageCode] = value;
|
||||
}
|
||||
};
|
||||
@@ -129,6 +129,11 @@ export class RecallNode extends DecoratorNode<ReactNode> {
|
||||
writable.__fallbackValue = fallbackValue;
|
||||
}
|
||||
|
||||
setRecallItemLabel(label: string): void {
|
||||
const writable = this.getWritable();
|
||||
writable.__recallItem = { ...writable.__recallItem, label };
|
||||
}
|
||||
|
||||
getTextContent(): string {
|
||||
return `#recall:${this.__recallItem.id}/fallback:${this.__fallbackValue}#`;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { logger } from "@formbricks/logger";
|
||||
import { TSurvey, TSurveyRecallItem } from "@formbricks/types/surveys/types";
|
||||
import { getFallbackValues, getRecallItems } from "@/lib/utils/recall";
|
||||
import { getFallbackValues, getRecallItemLabel, getRecallItems } from "@/lib/utils/recall";
|
||||
import { RecallItemSelect } from "@/modules/survey/components/element-form-input/components/recall-item-select";
|
||||
import { $createRecallNode, RecallNode } from "./recall-node";
|
||||
|
||||
@@ -306,6 +306,63 @@ export const RecallPlugin = ({
|
||||
});
|
||||
}, [editor, convertTextToRecallNodes]);
|
||||
|
||||
// Sync recall node labels when the survey data changes (e.g. headline edited in translations modal)
|
||||
useEffect(() => {
|
||||
// Check if any labels actually need updating before calling editor.update().
|
||||
// This avoids unnecessary updates that steal focus and trigger cascading re-renders.
|
||||
let needsUpdate = false;
|
||||
editor.getEditorState().read(() => {
|
||||
const root = $getRoot();
|
||||
const allRecallNodes = findAllRecallNodes(root);
|
||||
for (const recallNode of allRecallNodes) {
|
||||
const recallItem = recallNode.getRecallItem();
|
||||
const currentLabel =
|
||||
getRecallItemLabel(recallItem.id, localSurvey, selectedLanguageCode) ?? recallItem.label;
|
||||
if (currentLabel !== recallItem.label) {
|
||||
needsUpdate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!needsUpdate) return;
|
||||
|
||||
const rootElement = editor.getRootElement();
|
||||
const editorHasFocus = rootElement?.contains(document.activeElement) ?? false;
|
||||
|
||||
// Save the full DOM selection so we can restore the exact cursor position if stolen
|
||||
const domSelection = editorHasFocus ? null : globalThis.getSelection();
|
||||
const savedRange =
|
||||
domSelection && domSelection.rangeCount > 0 ? domSelection.getRangeAt(0).cloneRange() : null;
|
||||
|
||||
editor.update(
|
||||
() => {
|
||||
const root = $getRoot();
|
||||
const allRecallNodes = findAllRecallNodes(root);
|
||||
for (const recallNode of allRecallNodes) {
|
||||
const recallItem = recallNode.getRecallItem();
|
||||
const currentLabel =
|
||||
getRecallItemLabel(recallItem.id, localSurvey, selectedLanguageCode) ?? recallItem.label;
|
||||
if (currentLabel !== recallItem.label) {
|
||||
recallNode.setRecallItemLabel(currentLabel);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
onUpdate: () => {
|
||||
// Restore the full selection (including cursor position) after reconciliation
|
||||
if (!editorHasFocus && savedRange) {
|
||||
const sel = globalThis.getSelection();
|
||||
if (sel) {
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(savedRange);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
}, [editor, localSurvey, selectedLanguageCode, findAllRecallNodes]);
|
||||
|
||||
useEffect(() => {
|
||||
const removeUpdateListener = editor.registerUpdateListener(handleEditorUpdate);
|
||||
const removeKeyListener = editor.registerCommand(KEY_DOWN_COMMAND, handleKeyDown, COMMAND_PRIORITY_HIGH);
|
||||
|
||||
@@ -89,6 +89,7 @@ const ToolbarButton = ({ icon: Icon, active, onClick, tooltipText, disabled }: T
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
type="button"
|
||||
tabIndex={-1}
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
className={getButtonClassName(active)}>
|
||||
@@ -385,7 +386,7 @@ export const ToolbarPlugin = (
|
||||
<div className="toolbar flex" ref={toolbarRef}>
|
||||
{!props.excludedToolbarItems?.includes("blockType") && supportedBlockTypes.has(blockType) && (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger className="text-subtle">
|
||||
<DropdownMenuTrigger className="text-subtle" tabIndex={-1}>
|
||||
<>
|
||||
<span className={cn("icon", blockType)} />
|
||||
<span className="text text-default hidden sm:flex">
|
||||
|
||||
@@ -21,8 +21,6 @@ interface ElementToggleTableProps {
|
||||
elementIdx: number;
|
||||
isInvalid: boolean;
|
||||
updateElement: (elementIdx: number, updatedAttributes: Partial<TSurveyElement>) => void;
|
||||
selectedLanguageCode: string;
|
||||
setSelectedLanguageCode: (languageCode: string) => void;
|
||||
locale: TUserLocale;
|
||||
isStorageConfigured: boolean;
|
||||
}
|
||||
@@ -34,8 +32,6 @@ export const ElementToggleTable = ({
|
||||
elementIdx,
|
||||
isInvalid,
|
||||
updateElement,
|
||||
selectedLanguageCode,
|
||||
setSelectedLanguageCode,
|
||||
locale,
|
||||
isStorageConfigured,
|
||||
}: ElementToggleTableProps) => {
|
||||
@@ -114,8 +110,6 @@ export const ElementToggleTable = ({
|
||||
elementIdx={elementIdx}
|
||||
isInvalid={isInvalid}
|
||||
updateElement={updateElement}
|
||||
selectedLanguageCode={selectedLanguageCode}
|
||||
setSelectedLanguageCode={setSelectedLanguageCode}
|
||||
locale={locale}
|
||||
isStorageConfigured={isStorageConfigured}
|
||||
/>
|
||||
|
||||
@@ -2,13 +2,21 @@
|
||||
|
||||
import { Environment, Project } from "@prisma/client";
|
||||
import { motion } from "framer-motion";
|
||||
import { ExpandIcon, MonitorIcon, ShrinkIcon, SmartphoneIcon } from "lucide-react";
|
||||
import { ExpandIcon, GlobeIcon, MonitorIcon, ShrinkIcon, SmartphoneIcon } from "lucide-react";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { getLanguageLabel } from "@formbricks/i18n-utils/src/utils";
|
||||
import { TProjectStyling } from "@formbricks/types/project";
|
||||
import { TSurvey, TSurveyStyling } from "@formbricks/types/surveys/types";
|
||||
import { TSurvey, TSurveyLanguage, TSurveyStyling } from "@formbricks/types/surveys/types";
|
||||
import { TUserLocale } from "@formbricks/types/user";
|
||||
import { cn } from "@/lib/cn";
|
||||
import { ClientLogo } from "@/modules/ui/components/client-logo";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/modules/ui/components/dropdown-menu";
|
||||
import { MediaBackground } from "@/modules/ui/components/media-background";
|
||||
import { ResetProgressButton } from "@/modules/ui/components/reset-progress-button";
|
||||
import { SurveyInline } from "@/modules/ui/components/survey";
|
||||
@@ -24,6 +32,8 @@ interface PreviewSurveyProps {
|
||||
project: Project;
|
||||
environment: Pick<Environment, "id" | "appSetupCompleted">;
|
||||
languageCode: string;
|
||||
setLanguageCode?: (code: string) => void;
|
||||
locale?: TUserLocale;
|
||||
isSpamProtectionAllowed: boolean;
|
||||
publicDomain: string;
|
||||
}
|
||||
@@ -38,6 +48,8 @@ export const PreviewSurvey = ({
|
||||
project,
|
||||
environment,
|
||||
languageCode,
|
||||
setLanguageCode,
|
||||
locale,
|
||||
isSpamProtectionAllowed,
|
||||
publicDomain,
|
||||
}: PreviewSurveyProps) => {
|
||||
@@ -48,6 +60,10 @@ export const PreviewSurvey = ({
|
||||
|
||||
const [previewMode, setPreviewMode] = useState("desktop");
|
||||
const ContentRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const enabledLanguages = useMemo(() => survey.languages.filter((l) => l.enabled), [survey.languages]);
|
||||
const showLanguageSelector = setLanguageCode && enabledLanguages.length > 1;
|
||||
|
||||
const { projectOverwrites } = survey || {};
|
||||
|
||||
const { placement: surveyPlacement } = projectOverwrites || {};
|
||||
@@ -228,7 +244,15 @@ export const PreviewSurvey = ({
|
||||
<p className="absolute left-0 top-0 m-2 rounded bg-slate-100 px-2 py-1 text-xs text-slate-400">
|
||||
{t("common.preview")}
|
||||
</p>
|
||||
<div className="absolute right-0 top-0 m-2">
|
||||
<div className="absolute right-0 top-0 m-2 flex items-center gap-1">
|
||||
{showLanguageSelector && (
|
||||
<LanguageSelector
|
||||
languages={enabledLanguages}
|
||||
languageCode={languageCode}
|
||||
setLanguageCode={setLanguageCode}
|
||||
locale={locale}
|
||||
/>
|
||||
)}
|
||||
<ResetProgressButton onClick={resetProgress} />
|
||||
</div>
|
||||
<MediaBackground
|
||||
@@ -324,6 +348,14 @@ export const PreviewSurvey = ({
|
||||
</p>
|
||||
|
||||
<div className="flex items-center">
|
||||
{showLanguageSelector && (
|
||||
<LanguageSelector
|
||||
languages={enabledLanguages}
|
||||
languageCode={languageCode}
|
||||
setLanguageCode={setLanguageCode}
|
||||
locale={locale}
|
||||
/>
|
||||
)}
|
||||
{isFullScreenPreview ? (
|
||||
<ShrinkIcon
|
||||
className="mr-1 h-[22px] w-[22px] cursor-pointer rounded-md bg-white p-1 text-slate-500 hover:text-slate-700"
|
||||
@@ -426,4 +458,44 @@ export const PreviewSurvey = ({
|
||||
);
|
||||
};
|
||||
|
||||
const LanguageSelector = ({
|
||||
languages,
|
||||
languageCode,
|
||||
setLanguageCode,
|
||||
locale,
|
||||
}: {
|
||||
languages: TSurveyLanguage[];
|
||||
languageCode: string;
|
||||
setLanguageCode: (code: string) => void;
|
||||
locale?: TUserLocale;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
className="mr-1 flex h-[22px] w-[22px] cursor-pointer items-center justify-center rounded-md bg-white p-1 text-slate-500 hover:text-slate-700"
|
||||
aria-label="Change preview language">
|
||||
<GlobeIcon className="h-full w-full" />
|
||||
</button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
{languages.map((surveyLang) => {
|
||||
const code = surveyLang.default ? "default" : surveyLang.language.code;
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
key={surveyLang.language.code}
|
||||
className={cn("text-xs", languageCode === code && "font-semibold")}
|
||||
onSelect={() => setLanguageCode(code)}>
|
||||
{getLanguageLabel(surveyLang.language.code, locale ?? "en")}
|
||||
{surveyLang.default && ` (${t("common.default")})`}
|
||||
</DropdownMenuItem>
|
||||
);
|
||||
})}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
};
|
||||
|
||||
export { getPlacementStyle } from "./lib/utils";
|
||||
|
||||
@@ -60,9 +60,9 @@ test.describe("Survey Styling", async () => {
|
||||
await setDimension(page, "Headline Font Size", "24");
|
||||
await setDimension(page, "Description Font Size", "18");
|
||||
await setDimension(page, "Headline Font Weight", "700");
|
||||
await setColor(page, "Headline Label Color", "0000aa"); // Blue-ish
|
||||
await setDimension(page, "Headline Label Font Size", "14");
|
||||
await setDimension(page, "Headline Label Font Weight", "600");
|
||||
await setColor(page, "Label Color", "0000aa"); // Blue-ish
|
||||
await setDimension(page, "Label Font Size", "14");
|
||||
await setDimension(page, "Label Font Weight", "600");
|
||||
|
||||
// Verify Typography Variables
|
||||
await page.waitForTimeout(1000);
|
||||
@@ -206,8 +206,8 @@ test.describe("Survey Styling", async () => {
|
||||
await setColor(page, "Brand color", "e11d48");
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Click "Suggest colors" button
|
||||
await page.getByRole("button", { name: "Suggest colors" }).click();
|
||||
// Click "Suggest colors" button (the top-level one, not the disabled one inside FormStylingSettings)
|
||||
await page.getByRole("button", { name: "Suggest colors" }).first().click();
|
||||
|
||||
// Confirm the dialog
|
||||
await page.getByRole("button", { name: "Generate" }).click();
|
||||
|
||||
+263
-291
@@ -247,7 +247,7 @@ test.describe("Multi Language Survey Create", async () => {
|
||||
|
||||
await page.waitForURL(/\/environments\/[^/]+\/surveys/);
|
||||
|
||||
//add a new language
|
||||
// Add workspace languages (English + German)
|
||||
await page.getByRole("link", { name: "Configuration" }).click();
|
||||
await page.getByRole("link", { name: "Survey Languages" }).click();
|
||||
await page.getByRole("button", { name: "Edit languages" }).click();
|
||||
@@ -265,14 +265,13 @@ test.describe("Multi Language Survey Create", async () => {
|
||||
await page.getByText("German", { exact: true }).nth(1).click();
|
||||
await page.getByRole("button", { name: "Save changes" }).click();
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// Create survey and add all questions in English (default language)
|
||||
await page.getByRole("link", { name: "Surveys" }).click();
|
||||
await page.getByText("Start from scratch").click();
|
||||
await page.getByRole("button", { name: "Create survey", exact: true }).click();
|
||||
await page.locator("#multi-lang-toggle").click();
|
||||
await page.getByRole("combobox").click();
|
||||
await page.getByLabel("English (en)").click();
|
||||
await page.getByRole("button", { name: "Confirm" }).click();
|
||||
await page.getByLabel("German").click();
|
||||
|
||||
// Enable welcome card
|
||||
await page.locator("#welcome-toggle").click();
|
||||
|
||||
// Add questions in default language
|
||||
@@ -292,6 +291,7 @@ test.describe("Multi Language Survey Create", async () => {
|
||||
await page.getByPlaceholder("Option 1").fill(surveys.createAndSubmit.multiSelectQuestion.options[0]);
|
||||
await page.getByPlaceholder("Option 2").fill(surveys.createAndSubmit.multiSelectQuestion.options[1]);
|
||||
await page.getByPlaceholder("Option 3").fill(surveys.createAndSubmit.multiSelectQuestion.options[2]);
|
||||
|
||||
await page
|
||||
.locator("div")
|
||||
.filter({ hasText: /^Add BlockChoose the first question on your Block$/ })
|
||||
@@ -404,310 +404,282 @@ test.describe("Multi Language Survey Create", async () => {
|
||||
await page.getByPlaceholder("Option 5").click();
|
||||
await page.getByPlaceholder("Option 5").fill(surveys.createAndSubmit.ranking.choices[4]);
|
||||
|
||||
// Enable translation in german
|
||||
await page.getByText("Welcome CardShownOn").click();
|
||||
await page.getByRole("button", { name: "English" }).nth(1).click();
|
||||
await page.getByRole("button", { name: "German" }).click();
|
||||
// Navigate to Language tab to enable translations and add German
|
||||
await page.getByText("Language").click();
|
||||
await page.locator("#activate-translations-toggle").click();
|
||||
|
||||
// Fill welcome card in german using rich text editor helper
|
||||
await helper.fillRichTextEditor(page, "Note*", surveys.germanCreate.welcomeCard.headline);
|
||||
await helper.fillRichTextEditor(page, "Welcome message", surveys.germanCreate.welcomeCard.description);
|
||||
await page.getByPlaceholder("Next").click();
|
||||
await page.getByPlaceholder("Next").fill(surveys.germanCreate.welcomeCard.buttonLabel);
|
||||
// Select English as default language
|
||||
await page.locator("button", { hasText: "Select Language" }).click();
|
||||
await page.getByText("English (en)", { exact: true }).click();
|
||||
await page.getByRole("button", { name: "Confirm" }).click();
|
||||
|
||||
// Fill Open text question in german
|
||||
await page.getByRole("main").getByText("Free text").click();
|
||||
await helper.fillRichTextEditor(page, "Question*", surveys.germanCreate.openTextQuestion.question);
|
||||
await page.getByLabel("Placeholder").click();
|
||||
await page.getByLabel("Placeholder").fill(surveys.germanCreate.openTextQuestion.placeholder);
|
||||
await page.getByText("Show Block settings").first().click();
|
||||
await page.getByRole("textbox", { name: "Button Label", exact: true }).first().click();
|
||||
// Enable German by toggling its switch in the language table
|
||||
await page
|
||||
.getByRole("textbox", { name: "Button Label", exact: true })
|
||||
.first()
|
||||
.fill(surveys.germanCreate.next);
|
||||
await page
|
||||
.locator("div")
|
||||
.filter({ hasText: /^Block 11 question$/ })
|
||||
.first()
|
||||
.getByRole("row", { name: /German/ })
|
||||
.getByRole("switch")
|
||||
.click();
|
||||
|
||||
// Fill Single select question in german
|
||||
await page.getByRole("main").getByText("Single-Select").click();
|
||||
await helper.fillRichTextEditor(page, "Question*", surveys.germanCreate.singleSelectQuestion.question);
|
||||
await page.getByPlaceholder("Option 1").click();
|
||||
await page.getByPlaceholder("Option 1").fill(surveys.germanCreate.singleSelectQuestion.options[0]);
|
||||
await page.getByPlaceholder("Option 2").click();
|
||||
await page.getByPlaceholder("Option 2").fill(surveys.germanCreate.singleSelectQuestion.options[1]);
|
||||
await page.getByText("Show Block settings").first().click();
|
||||
await page.getByRole("textbox", { name: "Button Label", exact: true }).first().click();
|
||||
await page
|
||||
.getByRole("textbox", { name: "Button Label", exact: true })
|
||||
.first()
|
||||
.fill(surveys.germanCreate.next);
|
||||
await page.getByRole("textbox", { name: "“Back” Button Label", exact: true }).first().click();
|
||||
await page
|
||||
.getByRole("textbox", { name: "“Back” Button Label", exact: true })
|
||||
.first()
|
||||
.fill(surveys.germanCreate.back);
|
||||
await page
|
||||
.locator("div")
|
||||
.filter({ hasText: /^Block 21 question$/ })
|
||||
.first()
|
||||
.click();
|
||||
// Open translation modal for German by clicking the German row
|
||||
await page.getByRole("cell", { name: "German" }).click();
|
||||
await expect(page.getByRole("dialog")).toBeVisible();
|
||||
|
||||
// Fill Multi select question in german
|
||||
await page.getByRole("main").getByRole("heading", { name: "Multi-Select" }).click();
|
||||
// Fill translations in the Manage Translations modal
|
||||
// Welcome card translations (rich text for headline/subheader, plain for buttonLabel)
|
||||
await helper.fillModalRichTranslation(
|
||||
page,
|
||||
"welcomeCard.headline",
|
||||
surveys.germanCreate.welcomeCard.headline
|
||||
);
|
||||
await helper.fillModalRichTranslation(
|
||||
page,
|
||||
"welcomeCard.subheader",
|
||||
surveys.germanCreate.welcomeCard.description
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"welcomeCard.buttonLabel",
|
||||
surveys.germanCreate.welcomeCard.buttonLabel
|
||||
);
|
||||
|
||||
await helper.fillRichTextEditor(page, "Question*", surveys.germanCreate.multiSelectQuestion.question);
|
||||
await page.getByPlaceholder("Option 1").click();
|
||||
await page.getByPlaceholder("Option 1").fill(surveys.germanCreate.multiSelectQuestion.options[0]);
|
||||
await page.getByPlaceholder("Option 2").click();
|
||||
await page.getByPlaceholder("Option 2").fill(surveys.germanCreate.multiSelectQuestion.options[1]);
|
||||
await page.getByPlaceholder("Option 3").click();
|
||||
await page.getByPlaceholder("Option 3").fill(surveys.germanCreate.multiSelectQuestion.options[2]);
|
||||
await page.getByText("Show Block settings").first().click();
|
||||
await page.getByRole("textbox", { name: "Button Label", exact: true }).first().click();
|
||||
await page
|
||||
.getByRole("textbox", { name: "Button Label", exact: true })
|
||||
.first()
|
||||
.fill(surveys.germanCreate.next);
|
||||
await page.getByRole("textbox", { name: "“Back” Button Label", exact: true }).first().click();
|
||||
await page
|
||||
.getByRole("textbox", { name: "“Back” Button Label", exact: true })
|
||||
.first()
|
||||
.fill(surveys.germanCreate.back);
|
||||
await page
|
||||
.locator("div")
|
||||
.filter({ hasText: /^Block 31 question$/ })
|
||||
.first()
|
||||
.click();
|
||||
// Block 0 - Open text question
|
||||
await helper.fillModalTranslation(page, "blocks.0.buttonLabel", surveys.germanCreate.next);
|
||||
await helper.fillModalRichTranslation(
|
||||
page,
|
||||
"blocks.0.elements.0.headline",
|
||||
surveys.germanCreate.openTextQuestion.question
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.0.elements.0.placeholder",
|
||||
surveys.germanCreate.openTextQuestion.placeholder
|
||||
);
|
||||
|
||||
// Fill Picture select question in german
|
||||
await page.getByRole("main").getByText("Picture Selection").click();
|
||||
await helper.fillRichTextEditor(page, "Question*", surveys.germanCreate.pictureSelectQuestion.question);
|
||||
await page.getByText("Show Block settings").first().click();
|
||||
await page.getByRole("textbox", { name: "Button Label", exact: true }).first().click();
|
||||
await page
|
||||
.getByRole("textbox", { name: "Button Label", exact: true })
|
||||
.first()
|
||||
.fill(surveys.germanCreate.next);
|
||||
await page.getByRole("textbox", { name: "“Back” Button Label", exact: true }).first().click();
|
||||
await page
|
||||
.getByRole("textbox", { name: "“Back” Button Label", exact: true })
|
||||
.first()
|
||||
.fill(surveys.germanCreate.back);
|
||||
await page
|
||||
.locator("div")
|
||||
.filter({ hasText: /^Block 41 question$/ })
|
||||
.first()
|
||||
.click();
|
||||
// Block 1 - Single-select question
|
||||
await helper.fillModalRichTranslation(
|
||||
page,
|
||||
"blocks.1.elements.0.headline",
|
||||
surveys.germanCreate.singleSelectQuestion.question
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.1.elements.0.choices.0.label",
|
||||
surveys.germanCreate.singleSelectQuestion.options[0]
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.1.elements.0.choices.1.label",
|
||||
surveys.germanCreate.singleSelectQuestion.options[1]
|
||||
);
|
||||
|
||||
// Fill Rating question in german
|
||||
await page.getByRole("main").getByText("Rating").click();
|
||||
await helper.fillRichTextEditor(page, "Question*", surveys.germanCreate.ratingQuestion.question);
|
||||
await page.getByPlaceholder("Not good").click();
|
||||
await page.getByPlaceholder("Not good").fill(surveys.germanCreate.ratingQuestion.lowLabel);
|
||||
await page.getByPlaceholder("Very satisfied").click();
|
||||
await page.getByPlaceholder("Very satisfied").fill(surveys.germanCreate.ratingQuestion.highLabel);
|
||||
await page.getByText("Show Block settings").first().click();
|
||||
await page.getByRole("textbox", { name: "Button Label", exact: true }).first().click();
|
||||
await page
|
||||
.getByRole("textbox", { name: "Button Label", exact: true })
|
||||
.first()
|
||||
.fill(surveys.germanCreate.next);
|
||||
await page.getByRole("textbox", { name: "“Back” Button Label", exact: true }).first().click();
|
||||
await page
|
||||
.getByRole("textbox", { name: "“Back” Button Label", exact: true })
|
||||
.first()
|
||||
.fill(surveys.germanCreate.back);
|
||||
await page
|
||||
.locator("div")
|
||||
.filter({ hasText: /^Block 51 question$/ })
|
||||
.first()
|
||||
.click();
|
||||
// Block 2 - Multi-select question
|
||||
await helper.fillModalRichTranslation(
|
||||
page,
|
||||
"blocks.2.elements.0.headline",
|
||||
surveys.germanCreate.multiSelectQuestion.question
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.2.elements.0.choices.0.label",
|
||||
surveys.germanCreate.multiSelectQuestion.options[0]
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.2.elements.0.choices.1.label",
|
||||
surveys.germanCreate.multiSelectQuestion.options[1]
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.2.elements.0.choices.2.label",
|
||||
surveys.germanCreate.multiSelectQuestion.options[2]
|
||||
);
|
||||
|
||||
// Fill NPS question in german
|
||||
await page.getByRole("main").getByText("Net Promoter Score (NPS)").click();
|
||||
await helper.fillRichTextEditor(page, "Question*", surveys.germanCreate.npsQuestion.question);
|
||||
await page.getByLabel("Lower Label").click();
|
||||
await page.getByLabel("Lower Label").fill(surveys.germanCreate.npsQuestion.lowLabel);
|
||||
await page.getByLabel("Upper Label").click();
|
||||
await page.getByLabel("Upper Label").fill(surveys.germanCreate.npsQuestion.highLabel);
|
||||
await page.getByText("Show Block settings").first().click();
|
||||
await page.getByRole("textbox", { name: "Button Label", exact: true }).first().click();
|
||||
await page
|
||||
.getByRole("textbox", { name: "Button Label", exact: true })
|
||||
.first()
|
||||
.fill(surveys.germanCreate.next);
|
||||
await page.getByRole("textbox", { name: "“Back” Button Label", exact: true }).first().click();
|
||||
await page
|
||||
.getByRole("textbox", { name: "“Back” Button Label", exact: true })
|
||||
.first()
|
||||
.fill(surveys.germanCreate.back);
|
||||
await page
|
||||
.locator("div")
|
||||
.filter({ hasText: /^Block 61 question$/ })
|
||||
.first()
|
||||
.click();
|
||||
// Block 3 - Picture selection question
|
||||
await helper.fillModalRichTranslation(
|
||||
page,
|
||||
"blocks.3.elements.0.headline",
|
||||
surveys.germanCreate.pictureSelectQuestion.question
|
||||
);
|
||||
|
||||
// Fill Date question in german
|
||||
await page.getByRole("main").getByText("Date").click();
|
||||
await helper.fillRichTextEditor(page, "Question*", surveys.germanCreate.dateQuestion.question);
|
||||
await page.getByText("Show Block settings").first().click();
|
||||
await page.getByRole("textbox", { name: "Button Label", exact: true }).first().click();
|
||||
await page
|
||||
.getByRole("textbox", { name: "Button Label", exact: true })
|
||||
.first()
|
||||
.fill(surveys.germanCreate.next);
|
||||
await page.getByRole("textbox", { name: "“Back” Button Label", exact: true }).first().click();
|
||||
await page
|
||||
.getByRole("textbox", { name: "“Back” Button Label", exact: true })
|
||||
.first()
|
||||
.fill(surveys.germanCreate.back);
|
||||
await page
|
||||
.locator("div")
|
||||
.filter({ hasText: /^Block 71 question$/ })
|
||||
.first()
|
||||
.click();
|
||||
// Block 4 - Rating question
|
||||
await helper.fillModalRichTranslation(
|
||||
page,
|
||||
"blocks.4.elements.0.headline",
|
||||
surveys.germanCreate.ratingQuestion.question
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.4.elements.0.lowerLabel",
|
||||
surveys.germanCreate.ratingQuestion.lowLabel
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.4.elements.0.upperLabel",
|
||||
surveys.germanCreate.ratingQuestion.highLabel
|
||||
);
|
||||
|
||||
// Fill File upload question in german
|
||||
await page.getByRole("main").getByText("File Upload").click();
|
||||
await helper.fillRichTextEditor(page, "Question*", surveys.germanCreate.fileUploadQuestion.question);
|
||||
await page.getByText("Show Block settings").first().click();
|
||||
await page.getByRole("textbox", { name: "Button Label", exact: true }).first().click();
|
||||
await page
|
||||
.getByRole("textbox", { name: "Button Label", exact: true })
|
||||
.first()
|
||||
.fill(surveys.germanCreate.next);
|
||||
await page.getByRole("textbox", { name: "“Back” Button Label", exact: true }).first().click();
|
||||
await page
|
||||
.getByRole("textbox", { name: "“Back” Button Label", exact: true })
|
||||
.first()
|
||||
.fill(surveys.germanCreate.back);
|
||||
await page
|
||||
.locator("div")
|
||||
.filter({ hasText: /^Block 81 question$/ })
|
||||
.first()
|
||||
.click();
|
||||
// Block 5 - NPS question
|
||||
await helper.fillModalRichTranslation(
|
||||
page,
|
||||
"blocks.5.elements.0.headline",
|
||||
surveys.germanCreate.npsQuestion.question
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.5.elements.0.lowerLabel",
|
||||
surveys.germanCreate.npsQuestion.lowLabel
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.5.elements.0.upperLabel",
|
||||
surveys.germanCreate.npsQuestion.highLabel
|
||||
);
|
||||
|
||||
// Fill Matrix question in german
|
||||
await page.getByRole("main").getByText("Matrix").click();
|
||||
await helper.fillRichTextEditor(page, "Question*", surveys.germanCreate.matrix.question);
|
||||
await page.locator("#row-0").click();
|
||||
await page.locator("#row-0").fill(surveys.germanCreate.matrix.rows[0]);
|
||||
await page.locator("#row-1").click();
|
||||
await page.locator("#row-1").fill(surveys.germanCreate.matrix.rows[1]);
|
||||
await page.locator("#row-2").click();
|
||||
await page.locator("#row-2").fill(surveys.germanCreate.matrix.rows[2]);
|
||||
await page.locator("#column-0").click();
|
||||
await page.locator("#column-0").fill(surveys.germanCreate.matrix.columns[0]);
|
||||
await page.locator("#column-1").click();
|
||||
await page.locator("#column-1").fill(surveys.germanCreate.matrix.columns[1]);
|
||||
await page.locator("#column-2").click();
|
||||
await page.locator("#column-2").fill(surveys.germanCreate.matrix.columns[2]);
|
||||
await page.locator("#column-3").click();
|
||||
await page.locator("#column-3").fill(surveys.germanCreate.matrix.columns[3]);
|
||||
await page.getByText("Show Block settings").first().click();
|
||||
await page.getByRole("textbox", { name: "Button Label", exact: true }).first().click();
|
||||
await page
|
||||
.getByRole("textbox", { name: "Button Label", exact: true })
|
||||
.first()
|
||||
.fill(surveys.germanCreate.next);
|
||||
await page.getByRole("textbox", { name: "“Back” Button Label", exact: true }).first().click();
|
||||
await page
|
||||
.getByRole("textbox", { name: "“Back” Button Label", exact: true })
|
||||
.first()
|
||||
.fill(surveys.germanCreate.back);
|
||||
await page
|
||||
.locator("div")
|
||||
.filter({ hasText: /^Block 91 question$/ })
|
||||
.first()
|
||||
.click();
|
||||
// Block 6 - Date question
|
||||
await helper.fillModalRichTranslation(
|
||||
page,
|
||||
"blocks.6.elements.0.headline",
|
||||
surveys.germanCreate.dateQuestion.question
|
||||
);
|
||||
|
||||
// Fill Address question in german
|
||||
await page.getByRole("main").getByText("Address").click();
|
||||
await helper.fillRichTextEditor(page, "Question*", surveys.germanCreate.addressQuestion.question);
|
||||
await page.locator('[id="addressLine1\\.placeholder"]').click();
|
||||
await page
|
||||
.locator('[id="addressLine1\\.placeholder"]')
|
||||
.fill(surveys.germanCreate.addressQuestion.placeholder.addressLine1);
|
||||
await page.locator('[id="addressLine2\\.placeholder"]').click();
|
||||
await page
|
||||
.locator('[id="addressLine2\\.placeholder"]')
|
||||
.fill(surveys.germanCreate.addressQuestion.placeholder.addressLine2);
|
||||
await page.locator('[id="city\\.placeholder"]').click();
|
||||
await page
|
||||
.locator('[id="city\\.placeholder"]')
|
||||
.fill(surveys.germanCreate.addressQuestion.placeholder.city);
|
||||
await page.locator('[id="state\\.placeholder"]').click();
|
||||
await page
|
||||
.locator('[id="state\\.placeholder"]')
|
||||
.fill(surveys.germanCreate.addressQuestion.placeholder.state);
|
||||
await page.locator('[id="zip\\.placeholder"]').click();
|
||||
await page.locator('[id="zip\\.placeholder"]').fill(surveys.germanCreate.addressQuestion.placeholder.zip);
|
||||
await page.locator('[id="country\\.placeholder"]').click();
|
||||
await page
|
||||
.locator('[id="country\\.placeholder"]')
|
||||
.fill(surveys.germanCreate.addressQuestion.placeholder.country);
|
||||
await page.getByText("Show Block settings").first().click();
|
||||
await page.getByRole("textbox", { name: "Button Label", exact: true }).first().click();
|
||||
await page
|
||||
.getByRole("textbox", { name: "Button Label", exact: true })
|
||||
.first()
|
||||
.fill(surveys.germanCreate.next);
|
||||
await page.getByRole("textbox", { name: "“Back” Button Label", exact: true }).first().click();
|
||||
await page
|
||||
.getByRole("textbox", { name: "“Back” Button Label", exact: true })
|
||||
.first()
|
||||
.fill(surveys.germanCreate.back);
|
||||
await page
|
||||
.locator("div")
|
||||
.filter({ hasText: /^Block 101 question$/ })
|
||||
.first()
|
||||
.click();
|
||||
// Block 7 - File upload question
|
||||
await helper.fillModalRichTranslation(
|
||||
page,
|
||||
"blocks.7.elements.0.headline",
|
||||
surveys.germanCreate.fileUploadQuestion.question
|
||||
);
|
||||
|
||||
// Fill Ranking question in german
|
||||
await page.getByRole("main").getByText("Ranking").click();
|
||||
await helper.fillRichTextEditor(page, "Question*", surveys.germanCreate.ranking.question);
|
||||
await page.getByPlaceholder("Option 1").click();
|
||||
await page.getByPlaceholder("Option 1").fill(surveys.germanCreate.ranking.choices[0]);
|
||||
await page.getByPlaceholder("Option 2").click();
|
||||
await page.getByPlaceholder("Option 2").fill(surveys.germanCreate.ranking.choices[1]);
|
||||
await page.getByPlaceholder("Option 3").click();
|
||||
await page.getByPlaceholder("Option 3").fill(surveys.germanCreate.ranking.choices[2]);
|
||||
await page.getByPlaceholder("Option 4").click();
|
||||
await page.getByPlaceholder("Option 4").fill(surveys.germanCreate.ranking.choices[3]);
|
||||
await page.getByPlaceholder("Option 5").click();
|
||||
await page.getByPlaceholder("Option 5").fill(surveys.germanCreate.ranking.choices[4]);
|
||||
await page.getByText("Show Block settings").first().click();
|
||||
await page.getByRole("textbox", { name: "Button Label", exact: true }).first().click();
|
||||
await page
|
||||
.getByRole("textbox", { name: "Button Label", exact: true })
|
||||
.first()
|
||||
.fill(surveys.germanCreate.next);
|
||||
await page.getByRole("textbox", { name: "“Back” Button Label", exact: true }).first().click();
|
||||
await page
|
||||
.getByRole("textbox", { name: "“Back” Button Label", exact: true })
|
||||
.first()
|
||||
.fill(surveys.germanCreate.back);
|
||||
await page
|
||||
.locator("div")
|
||||
.filter({ hasText: /^Block 111 question$/ })
|
||||
.first()
|
||||
.click();
|
||||
// Block 8 - Matrix question
|
||||
await helper.fillModalRichTranslation(
|
||||
page,
|
||||
"blocks.8.elements.0.headline",
|
||||
surveys.germanCreate.matrix.question
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.8.elements.0.rows.0.label",
|
||||
surveys.germanCreate.matrix.rows[0]
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.8.elements.0.rows.1.label",
|
||||
surveys.germanCreate.matrix.rows[1]
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.8.elements.0.rows.2.label",
|
||||
surveys.germanCreate.matrix.rows[2]
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.8.elements.0.columns.0.label",
|
||||
surveys.germanCreate.matrix.columns[0]
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.8.elements.0.columns.1.label",
|
||||
surveys.germanCreate.matrix.columns[1]
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.8.elements.0.columns.2.label",
|
||||
surveys.germanCreate.matrix.columns[2]
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.8.elements.0.columns.3.label",
|
||||
surveys.germanCreate.matrix.columns[3]
|
||||
);
|
||||
|
||||
// Fill Thank you card in german
|
||||
await page.getByText("Ending card").first().click();
|
||||
await helper.fillRichTextEditor(page, "Note*", surveys.germanCreate.endingCard.headline);
|
||||
await helper.fillRichTextEditor(page, "Description", surveys.germanCreate.endingCard.description);
|
||||
// Block 9 - Address question
|
||||
await helper.fillModalRichTranslation(
|
||||
page,
|
||||
"blocks.9.elements.0.headline",
|
||||
surveys.germanCreate.addressQuestion.question
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.9.elements.0.addressLine1.placeholder",
|
||||
surveys.germanCreate.addressQuestion.placeholder.addressLine1
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.9.elements.0.addressLine2.placeholder",
|
||||
surveys.germanCreate.addressQuestion.placeholder.addressLine2
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.9.elements.0.city.placeholder",
|
||||
surveys.germanCreate.addressQuestion.placeholder.city
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.9.elements.0.state.placeholder",
|
||||
surveys.germanCreate.addressQuestion.placeholder.state
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.9.elements.0.zip.placeholder",
|
||||
surveys.germanCreate.addressQuestion.placeholder.zip
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.9.elements.0.country.placeholder",
|
||||
surveys.germanCreate.addressQuestion.placeholder.country
|
||||
);
|
||||
|
||||
await page.locator("#showButton").check();
|
||||
// Block 10 - Ranking question
|
||||
await helper.fillModalRichTranslation(
|
||||
page,
|
||||
"blocks.10.elements.0.headline",
|
||||
surveys.germanCreate.ranking.question
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.10.elements.0.choices.0.label",
|
||||
surveys.germanCreate.ranking.choices[0]
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.10.elements.0.choices.1.label",
|
||||
surveys.germanCreate.ranking.choices[1]
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.10.elements.0.choices.2.label",
|
||||
surveys.germanCreate.ranking.choices[2]
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.10.elements.0.choices.3.label",
|
||||
surveys.germanCreate.ranking.choices[3]
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"blocks.10.elements.0.choices.4.label",
|
||||
surveys.germanCreate.ranking.choices[4]
|
||||
);
|
||||
|
||||
await page.getByPlaceholder("Create your own Survey").click();
|
||||
await page.getByPlaceholder("Create your own Survey").fill(surveys.germanCreate.endingCard.buttonLabel);
|
||||
// Ending card translations
|
||||
await helper.fillModalRichTranslation(
|
||||
page,
|
||||
"endings.0.headline",
|
||||
surveys.germanCreate.endingCard.headline
|
||||
);
|
||||
await helper.fillModalRichTranslation(
|
||||
page,
|
||||
"endings.0.subheader",
|
||||
surveys.germanCreate.endingCard.description
|
||||
);
|
||||
await helper.fillModalTranslation(
|
||||
page,
|
||||
"endings.0.buttonLabel",
|
||||
surveys.germanCreate.endingCard.buttonLabel
|
||||
);
|
||||
|
||||
// TODO: @pandeymangg - figure out if this is required
|
||||
// Save translations
|
||||
await page.getByRole("button", { name: "Save" }).click();
|
||||
|
||||
// Configure as link survey and publish
|
||||
await page.getByRole("button", { name: "Settings", exact: true }).click();
|
||||
|
||||
await page.locator("#howToSendCardTrigger").click();
|
||||
|
||||
@@ -179,6 +179,30 @@ export const fillRichTextEditor = async (page: Page, labelText: string, content:
|
||||
await editor.pressSequentially(content, { delay: 50 });
|
||||
};
|
||||
|
||||
/**
|
||||
* Fill a plain text translation in the Manage Translations modal.
|
||||
* Targets the row by data-testid which includes the translation path.
|
||||
*/
|
||||
export const fillModalTranslation = async (page: Page, path: string, text: string): Promise<void> => {
|
||||
const row = page.locator(`[data-testid="translation-row-${path}"]`);
|
||||
await row.scrollIntoViewIfNeeded();
|
||||
const input = row.locator("input");
|
||||
await input.fill(text);
|
||||
};
|
||||
|
||||
/**
|
||||
* Fill a rich text translation in the Manage Translations modal.
|
||||
*/
|
||||
export const fillModalRichTranslation = async (page: Page, path: string, text: string): Promise<void> => {
|
||||
const row = page.locator(`[data-testid="translation-row-${path}"]`);
|
||||
await row.scrollIntoViewIfNeeded();
|
||||
const editor = row.locator(".editor-input").first();
|
||||
await editor.click();
|
||||
await editor.press("Meta+a");
|
||||
await editor.press("Backspace");
|
||||
await editor.pressSequentially(text, { delay: 50 });
|
||||
};
|
||||
|
||||
export const createSurvey = async (page: Page, params: CreateSurveyParams) => {
|
||||
const addBlock = "Add BlockChoose the first question on your Block";
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
!function () {
|
||||
var appUrl = "https://app.formbricks.com"; // use PUBLIC_URL if you are using multi-domain setup, otherwise use WEBAPP_URL
|
||||
var environmentId = "clgwcwp4z000lpf0hur7pzbuv";
|
||||
|
||||
var t = document.createElement("script");
|
||||
t.type = "text/javascript";
|
||||
t.async = !0;
|
||||
t.src = appUrl + "/js/formbricks.umd.cjs";
|
||||
|
||||
var e = document.getElementsByTagName("script")[0];
|
||||
e.parentNode.insertBefore(t, e);
|
||||
|
||||
setTimeout(function () {
|
||||
window.formbricks.setup({ environmentId: environmentId, appUrl: appUrl });
|
||||
}, 500);
|
||||
}();
|
||||
@@ -141,7 +141,6 @@ function DropdownVariant({
|
||||
otherOptionPlaceholder,
|
||||
dir,
|
||||
otherInputRef,
|
||||
required,
|
||||
searchPlaceholder,
|
||||
searchNoResultsText,
|
||||
}: Readonly<DropdownVariantProps>): React.JSX.Element {
|
||||
@@ -279,7 +278,7 @@ function DropdownVariant({
|
||||
onChange={handleOtherInputChange}
|
||||
placeholder={otherOptionPlaceholder}
|
||||
disabled={disabled}
|
||||
aria-required={required}
|
||||
required
|
||||
aria-invalid={Boolean(errorMessage)}
|
||||
dir={dir}
|
||||
className="mt-2 w-full"
|
||||
@@ -330,7 +329,6 @@ function ListVariant({
|
||||
otherOptionPlaceholder,
|
||||
dir,
|
||||
otherInputRef,
|
||||
required,
|
||||
}: Readonly<ListVariantProps>): React.JSX.Element {
|
||||
const isNoneSelected = value.includes("none");
|
||||
|
||||
@@ -401,7 +399,7 @@ function ListVariant({
|
||||
onChange={handleOtherInputChange}
|
||||
placeholder={otherOptionPlaceholder}
|
||||
disabled={disabled}
|
||||
aria-required={required}
|
||||
required
|
||||
aria-invalid={Boolean(errorMessage)}
|
||||
dir={dir}
|
||||
className="mt-2 w-full"
|
||||
|
||||
@@ -182,12 +182,12 @@ function NPS({
|
||||
{(lowerLabel ?? upperLabel) ? (
|
||||
<div className="mt-2 flex justify-between gap-8 px-1.5">
|
||||
{lowerLabel ? (
|
||||
<Label variant="default" className="max-w-[50%] text-xs leading-6" dir={dir}>
|
||||
<Label variant="card" className="max-w-[50%] leading-6" dir={dir}>
|
||||
{lowerLabel}
|
||||
</Label>
|
||||
) : null}
|
||||
{upperLabel ? (
|
||||
<Label variant="default" className="max-w-[50%] text-right text-xs leading-6" dir={dir}>
|
||||
<Label variant="card" className="max-w-[50%] text-right leading-6" dir={dir}>
|
||||
{upperLabel}
|
||||
</Label>
|
||||
) : null}
|
||||
|
||||
@@ -426,12 +426,12 @@ function Rating({
|
||||
{(lowerLabel ?? upperLabel) ? (
|
||||
<div className="mt-4 flex justify-between gap-8 px-1.5">
|
||||
{lowerLabel ? (
|
||||
<Label variant="default" className="max-w-[50%] text-xs leading-6" dir={dir}>
|
||||
<Label variant="card" className="max-w-[50%] leading-6" dir={dir}>
|
||||
{lowerLabel}
|
||||
</Label>
|
||||
) : null}
|
||||
{upperLabel ? (
|
||||
<Label variant="default" className="max-w-[50%] text-right text-xs leading-6" dir={dir}>
|
||||
<Label variant="card" className="max-w-[50%] text-right leading-6" dir={dir}>
|
||||
{upperLabel}
|
||||
</Label>
|
||||
) : null}
|
||||
|
||||
@@ -272,6 +272,7 @@ function SingleSelect({
|
||||
onChange={handleOtherInputChange}
|
||||
placeholder={otherOptionPlaceholder}
|
||||
disabled={disabled}
|
||||
required
|
||||
aria-invalid={Boolean(errorMessage)}
|
||||
dir={dir}
|
||||
className="mt-2 w-full"
|
||||
@@ -334,7 +335,7 @@ function SingleSelect({
|
||||
onChange={handleOtherInputChange}
|
||||
placeholder={otherOptionPlaceholder}
|
||||
disabled={disabled}
|
||||
aria-required={required}
|
||||
required
|
||||
aria-invalid={Boolean(errorMessage)}
|
||||
dir={dir}
|
||||
className="mt-2 w-full"
|
||||
|
||||
@@ -4,17 +4,16 @@ import * as React from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-button text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-button text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 aria-invalid:border-destructive",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
||||
destructive:
|
||||
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||
outline:
|
||||
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
||||
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20",
|
||||
outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground",
|
||||
secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
custom: "button-custom",
|
||||
},
|
||||
|
||||
@@ -225,7 +225,7 @@ function CalendarDayButton({
|
||||
data-range-end={modifiers.range_end}
|
||||
data-range-middle={modifiers.range_middle}
|
||||
className={cn(
|
||||
"data-[selected-single=true]:bg-brand data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground hover:text-primary-foreground data-[selected-single=true]:hover:bg-brand data-[selected-single=true]:hover:text-primary-foreground data-[range-start=true]:hover:bg-primary data-[range-start=true]:hover:text-primary-foreground data-[range-end=true]:hover:bg-primary data-[range-end=true]:hover:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 dark:hover:text-accent-foreground flex aspect-square h-auto w-full min-w-[--cell-size] flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] hover:bg-[color-mix(in_srgb,var(--fb-survey-brand-color)_70%,transparent)] data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md [&>span]:text-xs [&>span]:opacity-70",
|
||||
"data-[selected-single=true]:bg-brand data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground hover:text-primary-foreground data-[selected-single=true]:hover:bg-brand data-[selected-single=true]:hover:text-primary-foreground data-[range-start=true]:hover:bg-primary data-[range-start=true]:hover:text-primary-foreground data-[range-end=true]:hover:bg-primary data-[range-end=true]:hover:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 flex aspect-square h-auto w-full min-w-[--cell-size] flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] hover:bg-[color-mix(in_srgb,var(--fb-survey-brand-color)_70%,transparent)] data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md [&>span]:text-xs [&>span]:opacity-70",
|
||||
defaultClassNames.day,
|
||||
className
|
||||
)}
|
||||
|
||||
@@ -11,7 +11,7 @@ function Checkbox({
|
||||
<CheckboxPrimitive.Root
|
||||
data-slot="checkbox"
|
||||
className={cn(
|
||||
"border-input-border dark:bg-input/30 data-[state=checked]:bg-brand data-[state=checked]:text-brand-foreground dark:data-[state=checked]:bg-brand data-[state=checked]:border-brand focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive text-input-text peer size-4 shrink-0 rounded-[4px] border bg-white shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"border-input-border data-[state=checked]:bg-brand data-[state=checked]:text-brand-foreground data-[state=checked]:border-brand focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 aria-invalid:border-destructive text-input-text peer size-4 shrink-0 rounded-[4px] border bg-white shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}>
|
||||
|
||||
@@ -58,7 +58,7 @@ function DropdownMenuItem({
|
||||
data-inset={inset}
|
||||
data-variant={variant}
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none focus-visible:outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none focus-visible:outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -75,7 +75,7 @@ function ElementHeader({
|
||||
|
||||
{/* Headline */}
|
||||
<div>
|
||||
<div>{required ? <span className="label-upper mb-[3px]">{requiredLabel}</span> : null}</div>
|
||||
<div>{required ? <span className="label-card mb-[3px]">{requiredLabel}</span> : null}</div>
|
||||
<div className="flex">
|
||||
{isHeadlineHtml && safeHeadlineHtml ? (
|
||||
<Label htmlFor={htmlFor} variant="headline">
|
||||
|
||||
@@ -41,7 +41,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(function Input(
|
||||
// Focus ring
|
||||
"focus-visible:border-ring focus-visible:ring-ring focus-visible:ring-[3px]",
|
||||
// Error state ring
|
||||
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
"aria-invalid:ring-destructive/20 aria-invalid:border-destructive",
|
||||
// Disabled state
|
||||
"disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
|
||||
@@ -4,7 +4,7 @@ import { cn, stripInlineStyles } from "@/lib/utils";
|
||||
|
||||
interface LabelProps extends React.ComponentProps<"label"> {
|
||||
/** Label variant for different styling contexts */
|
||||
variant?: "default" | "headline" | "description";
|
||||
variant?: "default" | "headline" | "description" | "card";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,6 +51,8 @@ function Label({
|
||||
variantClass = "label-headline";
|
||||
} else if (variant === "description") {
|
||||
variantClass = "label-description";
|
||||
} else if (variant === "card") {
|
||||
variantClass = "label-card";
|
||||
}
|
||||
|
||||
// Base classes - use flex-col for HTML content to allow line breaks, flex items-center for non-HTML
|
||||
|
||||
@@ -31,7 +31,7 @@ function RadioGroupItem({
|
||||
<RadioGroupPrimitive.Item
|
||||
data-slot="radio-group-item"
|
||||
className={cn(
|
||||
"border-input-border text-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 aspect-square size-4 shrink-0 rounded-full border bg-white shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"border-input-border text-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 aria-invalid:border-destructive aspect-square size-4 shrink-0 rounded-full border bg-white shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}>
|
||||
|
||||
@@ -13,7 +13,7 @@ function Textarea({ className, dir = "auto", ...props }: TextareaProps): React.J
|
||||
style={{ fontSize: "var(--fb-input-font-size)" }}
|
||||
dir={dir}
|
||||
className={cn(
|
||||
"w-input bg-input-bg border-input-border rounded-input font-input font-input-weight px-input-x py-input-y shadow-input placeholder:text-input-placeholder placeholder:opacity-input-placeholder focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 text-input text-input-text flex field-sizing-content min-h-16 border transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"w-input bg-input-bg border-input-border rounded-input font-input font-input-weight px-input-x py-input-y shadow-input placeholder:text-input-placeholder placeholder:opacity-input-placeholder focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 aria-invalid:border-destructive text-input text-input-text flex field-sizing-content min-h-16 border transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -197,8 +197,8 @@
|
||||
opacity: var(--fb-label-opacity);
|
||||
}
|
||||
|
||||
#fbjs .label-upper,
|
||||
#fbjs .label-upper * {
|
||||
#fbjs .label-card,
|
||||
#fbjs .label-card * {
|
||||
font-family: var(--fb-element-upper-label-font-family);
|
||||
font-weight: var(--fb-element-upper-label-font-weight);
|
||||
font-size: var(--fb-element-upper-label-font-size);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user