Compare commits

..

2 Commits

499 changed files with 13233 additions and 15458 deletions
+22 -20
View File
@@ -6,9 +6,19 @@ permissions:
on: on:
pull_request: pull_request:
types: [opened, synchronize, reopened] types: [opened, synchronize, reopened]
paths:
- "apps/web/**/*.ts"
- "apps/web/**/*.tsx"
- "apps/web/locales/**/*.json"
- "scan-translations.ts"
push: push:
branches: branches:
- main - main
paths:
- "apps/web/**/*.ts"
- "apps/web/**/*.tsx"
- "apps/web/locales/**/*.json"
- "scan-translations.ts"
jobs: jobs:
validate-translations: validate-translations:
@@ -23,38 +33,30 @@ jobs:
egress-policy: audit egress-policy: audit
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Check for relevant changes
id: changes
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
with: with:
filters: | fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
translations:
- 'apps/web/**/*.ts'
- 'apps/web/**/*.tsx'
- 'apps/web/locales/**/*.json'
- 'packages/surveys/src/**/*.{ts,tsx}'
- 'packages/surveys/locales/**/*.json'
- 'packages/email/**/*.{ts,tsx}'
- name: Setup Node.js 22.x - name: Setup Node.js 22.x
if: steps.changes.outputs.translations == 'true'
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af
with: with:
node-version: 22.x node-version: 22.x
- name: Install pnpm - name: Install pnpm
if: steps.changes.outputs.translations == 'true'
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
- name: Install dependencies - name: Install dependencies
if: steps.changes.outputs.translations == 'true'
run: pnpm install --config.platform=linux --config.architecture=x64 run: pnpm install --config.platform=linux --config.architecture=x64
- name: Validate translation keys - name: Validate translation keys
if: steps.changes.outputs.translations == 'true' run: |
run: pnpm run scan-translations echo ""
echo "🔍 Validating translation keys..."
echo ""
pnpm run scan-translations
- name: Skip (no translation-related changes) - name: Summary
if: steps.changes.outputs.translations != 'true' if: success()
run: echo "No translation-related files changed — skipping validation." run: |
echo ""
echo "✅ Translation validation completed successfully!"
echo ""
+2
View File
@@ -0,0 +1,2 @@
echo "{\"branchName\": \"$(git rev-parse --abbrev-ref HEAD)\"}" > ./branch.json
prettier --write ./branch.json
+40 -1
View File
@@ -1 +1,40 @@
pnpm lint-staged # Load environment variables from .env files
if [ -f .env ]; then
set -a
. .env
set +a
fi
pnpm lint-staged
# Run Lingo.dev i18n workflow if LINGODOTDEV_API_KEY is set
if [ -n "$LINGODOTDEV_API_KEY" ]; then
echo ""
echo "🌍 Running Lingo.dev translation workflow..."
echo ""
# Run translation generation and validation
if pnpm run i18n; then
echo ""
echo "✅ Translation validation passed"
echo ""
# Add updated locale files to git
git add apps/web/locales/*.json
else
echo ""
echo "❌ Translation validation failed!"
echo ""
echo "Please fix the translation issues above before committing:"
echo " • Add missing translation keys to your locale files"
echo " • Remove unused translation keys"
echo ""
echo "Or run 'pnpm i18n' to see the detailed report"
echo ""
exit 1
fi
else
echo ""
echo "⚠️ Skipping translation validation: LINGODOTDEV_API_KEY is not set"
echo " (This is expected for community contributors)"
echo ""
fi
+17 -12
View File
@@ -10,20 +10,25 @@
"build-storybook": "storybook build", "build-storybook": "storybook build",
"clean": "rimraf .turbo node_modules dist storybook-static" "clean": "rimraf .turbo node_modules dist storybook-static"
}, },
"dependencies": {
"@formbricks/survey-ui": "workspace:*"
},
"devDependencies": { "devDependencies": {
"@chromatic-com/storybook": "^5.0.1", "@chromatic-com/storybook": "^5.0.0",
"@storybook/addon-a11y": "10.2.15", "@storybook/addon-a11y": "10.1.11",
"@storybook/addon-links": "10.2.15", "@storybook/addon-links": "10.1.11",
"@storybook/addon-onboarding": "10.2.15", "@storybook/addon-onboarding": "10.1.11",
"@storybook/react-vite": "10.2.15", "@storybook/react-vite": "10.1.11",
"@typescript-eslint/eslint-plugin": "8.56.1", "@typescript-eslint/eslint-plugin": "8.53.0",
"@tailwindcss/vite": "4.2.1", "@tailwindcss/vite": "4.1.18",
"@typescript-eslint/parser": "8.56.1", "@typescript-eslint/parser": "8.53.0",
"@vitejs/plugin-react": "5.1.4", "@vitejs/plugin-react": "5.1.2",
"esbuild": "0.25.12",
"eslint-plugin-react-refresh": "0.4.26", "eslint-plugin-react-refresh": "0.4.26",
"eslint-plugin-storybook": "10.2.14", "eslint-plugin-storybook": "10.1.11",
"storybook": "10.2.15", "prop-types": "15.8.1",
"storybook": "10.1.11",
"vite": "7.3.1", "vite": "7.3.1",
"@storybook/addon-docs": "10.2.15" "@storybook/addon-docs": "10.1.11"
} }
} }
-6
View File
@@ -1,6 +0,0 @@
const baseConfig = require("../../.prettierrc.js");
module.exports = {
...baseConfig,
tailwindConfig: "./tailwind.config.js",
};
-3
View File
@@ -101,9 +101,6 @@ RUN chown -R nextjs:nextjs ./apps/web/public && chmod -R 755 ./apps/web/public
# Create packages/database directory structure with proper ownership for runtime migrations # Create packages/database directory structure with proper ownership for runtime migrations
RUN mkdir -p ./packages/database/migrations && chown -R nextjs:nextjs ./packages/database RUN mkdir -p ./packages/database/migrations && chown -R nextjs:nextjs ./packages/database
COPY --from=installer /app/packages/database/package.json ./packages/database/package.json
RUN chown nextjs:nextjs ./packages/database/package.json && chmod 644 ./packages/database/package.json
COPY --from=installer /app/packages/database/schema.prisma ./packages/database/schema.prisma COPY --from=installer /app/packages/database/schema.prisma ./packages/database/schema.prisma
RUN chown nextjs:nextjs ./packages/database/schema.prisma && chmod 644 ./packages/database/schema.prisma RUN chown nextjs:nextjs ./packages/database/schema.prisma && chmod 644 ./packages/database/schema.prisma
@@ -69,7 +69,7 @@ export const ConnectWithFormbricks = ({
) : ( ) : (
<div className="flex animate-pulse flex-col items-center space-y-4"> <div className="flex animate-pulse flex-col items-center space-y-4">
<span className="relative flex h-10 w-10"> <span className="relative flex h-10 w-10">
<span className="absolute inline-flex h-full w-full animate-ping-slow rounded-full bg-slate-400 opacity-75"></span> <span className="animate-ping-slow absolute inline-flex h-full w-full rounded-full bg-slate-400 opacity-75"></span>
<span className="relative inline-flex h-10 w-10 rounded-full bg-slate-500"></span> <span className="relative inline-flex h-10 w-10 rounded-full bg-slate-500"></span>
</span> </span>
<p className="pt-4 text-sm font-medium text-slate-600"> <p className="pt-4 text-sm font-medium text-slate-600">
@@ -46,7 +46,7 @@ const Page = async (props: ConnectPageProps) => {
channel={channel} channel={channel}
/> />
<Button <Button
className="absolute right-5 top-5 !mt-0 text-slate-500 hover:text-slate-700" className="absolute top-5 right-5 !mt-0 text-slate-500 hover:text-slate-700"
variant="ghost" variant="ghost"
asChild> asChild>
<Link href={`/environments/${environment.id}`}> <Link href={`/environments/${environment.id}`}>
@@ -49,7 +49,7 @@ const Page = async (props: XMTemplatePageProps) => {
<XMTemplateList project={project} user={user} environmentId={environment.id} /> <XMTemplateList project={project} user={user} environmentId={environment.id} />
{projects.length >= 2 && ( {projects.length >= 2 && (
<Button <Button
className="absolute right-5 top-5 !mt-0 text-slate-500 hover:text-slate-700" className="absolute top-5 right-5 !mt-0 text-slate-500 hover:text-slate-700"
variant="ghost" variant="ghost"
asChild> asChild>
<Link href={`/environments/${environment.id}/surveys`}> <Link href={`/environments/${environment.id}/surveys`}>
@@ -42,7 +42,7 @@ export const LandingSidebar = ({ user, organization }: LandingSidebarProps) => {
return ( return (
<aside <aside
className={cn( className={cn(
"z-40 flex w-sidebar-collapsed flex-col justify-between rounded-r-xl border-r border-slate-200 bg-white pt-3 shadow-md transition-all duration-100" "w-sidebar-collapsed z-40 flex flex-col justify-between rounded-r-xl border-r border-slate-200 bg-white pt-3 shadow-md transition-all duration-100"
)}> )}>
<Image src={FBLogo} width={160} height={30} alt={t("environments.formbricks_logo")} /> <Image src={FBLogo} width={160} height={30} alt={t("environments.formbricks_logo")} />
@@ -50,7 +50,7 @@ const Page = async (props: ChannelPageProps) => {
<OnboardingOptionsContainer options={channelOptions} /> <OnboardingOptionsContainer options={channelOptions} />
{projects.length >= 1 && ( {projects.length >= 1 && (
<Button <Button
className="absolute right-5 top-5 !mt-0 text-slate-500 hover:text-slate-700" className="absolute top-5 right-5 !mt-0 text-slate-500 hover:text-slate-700"
variant="ghost" variant="ghost"
asChild> asChild>
<Link href={"/"}> <Link href={"/"}>
@@ -47,7 +47,7 @@ const Page = async (props: ModePageProps) => {
<OnboardingOptionsContainer options={channelOptions} /> <OnboardingOptionsContainer options={channelOptions} />
{projects.length >= 1 && ( {projects.length >= 1 && (
<Button <Button
className="absolute right-5 top-5 !mt-0 text-slate-500 hover:text-slate-700" className="absolute top-5 right-5 !mt-0 text-slate-500 hover:text-slate-700"
variant="ghost" variant="ghost"
asChild> asChild>
<Link href={"/"}> <Link href={"/"}>
@@ -228,7 +228,7 @@ export const ProjectSettings = ({
</FormProvider> </FormProvider>
</div> </div>
<div className="relative flex w-1/2 flex-col items-center justify-center space-y-2 rounded-lg border bg-slate-200 p-6 shadow"> <div className="relative flex h-[30rem] w-1/2 flex-col items-center justify-center space-y-2 rounded-lg border bg-slate-200 shadow">
{logoUrl && ( {logoUrl && (
<Image <Image
src={logoUrl} src={logoUrl}
@@ -239,16 +239,18 @@ export const ProjectSettings = ({
/> />
)} )}
<p className="text-sm text-slate-400">{t("common.preview")}</p> <p className="text-sm text-slate-400">{t("common.preview")}</p>
<SurveyInline <div className="z-0 h-3/4 w-3/4">
appUrl={publicDomain} <SurveyInline
isPreviewMode={true} appUrl={publicDomain}
survey={previewSurvey(projectName || t("common.my_product"), t)} isPreviewMode={true}
styling={previewStyling} survey={previewSurvey(projectName || "my Product", t)}
isBrandingEnabled={false} styling={previewStyling}
languageCode="default" isBrandingEnabled={false}
onFileUpload={async (file) => file.name} languageCode="default"
autoFocus={false} onFileUpload={async (file) => file.name}
/> autoFocus={false}
/>
</div>
</div> </div>
<CreateTeamModal <CreateTeamModal
open={createTeamModalOpen} open={createTeamModalOpen}
@@ -69,7 +69,7 @@ const Page = async (props: ProjectSettingsPageProps) => {
/> />
{projects.length >= 1 && ( {projects.length >= 1 && (
<Button <Button
className="absolute right-5 top-5 !mt-0 text-slate-500 hover:text-slate-700" className="absolute top-5 right-5 !mt-0 text-slate-500 hover:text-slate-700"
variant="ghost" variant="ghost"
asChild> asChild>
<Link href={"/"}> <Link href={"/"}>
@@ -1,7 +1,7 @@
import { z } from "zod"; import { z } from "zod";
export const ZOrganizationTeam = z.object({ export const ZOrganizationTeam = z.object({
id: z.cuid2(), id: z.string().cuid2(),
name: z.string(), name: z.string(),
}); });
@@ -25,7 +25,7 @@ const ZCreateProjectAction = z.object({
data: ZProjectUpdateInput, data: ZProjectUpdateInput,
}); });
export const createProjectAction = authenticatedActionClient.inputSchema(ZCreateProjectAction).action( export const createProjectAction = authenticatedActionClient.schema(ZCreateProjectAction).action(
withAuditLogging( withAuditLogging(
"created", "created",
"project", "project",
@@ -97,7 +97,7 @@ const ZGetOrganizationsForSwitcherAction = z.object({
* Called on-demand when user opens the organization switcher. * Called on-demand when user opens the organization switcher.
*/ */
export const getOrganizationsForSwitcherAction = authenticatedActionClient export const getOrganizationsForSwitcherAction = authenticatedActionClient
.inputSchema(ZGetOrganizationsForSwitcherAction) .schema(ZGetOrganizationsForSwitcherAction)
.action(async ({ ctx, parsedInput }) => { .action(async ({ ctx, parsedInput }) => {
await checkAuthorizationUpdated({ await checkAuthorizationUpdated({
userId: ctx.user.id, userId: ctx.user.id,
@@ -122,7 +122,7 @@ const ZGetProjectsForSwitcherAction = z.object({
* Called on-demand when user opens the project switcher. * Called on-demand when user opens the project switcher.
*/ */
export const getProjectsForSwitcherAction = authenticatedActionClient export const getProjectsForSwitcherAction = authenticatedActionClient
.inputSchema(ZGetProjectsForSwitcherAction) .schema(ZGetProjectsForSwitcherAction)
.action(async ({ ctx, parsedInput }) => { .action(async ({ ctx, parsedInput }) => {
await checkAuthorizationUpdated({ await checkAuthorizationUpdated({
userId: ctx.user.id, userId: ctx.user.id,
@@ -11,7 +11,6 @@ import {
RocketIcon, RocketIcon,
UserCircleIcon, UserCircleIcon,
UserIcon, UserIcon,
WorkflowIcon,
} from "lucide-react"; } from "lucide-react";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
@@ -115,13 +114,6 @@ export const MainNavigation = ({
pathname?.includes("/segments") || pathname?.includes("/segments") ||
pathname?.includes("/attributes"), pathname?.includes("/attributes"),
}, },
{
name: t("common.workflows"),
href: `/environments/${environment.id}/workflows`,
icon: WorkflowIcon,
isActive: pathname?.includes("/workflows"),
isHidden: !isFormbricksCloud,
},
{ {
name: t("common.configuration"), name: t("common.configuration"),
href: `/environments/${environment.id}/workspace/general`, href: `/environments/${environment.id}/workspace/general`,
@@ -129,7 +121,7 @@ export const MainNavigation = ({
isActive: pathname?.includes("/project"), isActive: pathname?.includes("/project"),
}, },
], ],
[t, environment.id, pathname, isFormbricksCloud] [t, environment.id, pathname]
); );
const dropdownNavigation = [ const dropdownNavigation = [
@@ -196,7 +188,7 @@ export const MainNavigation = ({
size="icon" size="icon"
onClick={toggleSidebar} onClick={toggleSidebar}
className={cn( className={cn(
"rounded-xl bg-slate-50 p-1 text-slate-600 transition-all hover:bg-slate-100 focus:outline-none focus:ring-0 focus:ring-transparent" "rounded-xl bg-slate-50 p-1 text-slate-600 transition-all hover:bg-slate-100 focus:ring-0 focus:ring-transparent focus:outline-none"
)}> )}>
{isCollapsed ? ( {isCollapsed ? (
<PanelLeftOpenIcon strokeWidth={1.5} /> <PanelLeftOpenIcon strokeWidth={1.5} />
@@ -53,7 +53,7 @@ export const WidgetStatusIndicator = ({ environment }: WidgetStatusIndicatorProp
<currentStatus.icon /> <currentStatus.icon />
</div> </div>
<p className="text-md font-bold text-slate-800 md:text-xl">{currentStatus.title}</p> <p className="text-md font-bold text-slate-800 md:text-xl">{currentStatus.title}</p>
<p className="w-2/3 text-balance text-sm text-slate-600">{currentStatus.subtitle}</p> <p className="w-2/3 text-sm text-balance text-slate-600">{currentStatus.subtitle}</p>
{status === "notImplemented" && ( {status === "notImplemented" && (
<Button variant="outline" size="sm" className="bg-white" onClick={() => router.refresh()}> <Button variant="outline" size="sm" className="bg-white" onClick={() => router.refresh()}>
<RotateCcwIcon /> <RotateCcwIcon />
@@ -81,7 +81,7 @@ export const OrganizationBreadcrumb = ({
getOrganizationsForSwitcherAction({ organizationId: currentOrganizationId }).then((result) => { getOrganizationsForSwitcherAction({ organizationId: currentOrganizationId }).then((result) => {
if (result?.data) { if (result?.data) {
// Sort organizations by name // Sort organizations by name
const sorted = [...result.data].sort((a, b) => a.name.localeCompare(b.name)); const sorted = result.data.toSorted((a, b) => a.name.localeCompare(b.name));
setOrganizations(sorted); setOrganizations(sorted);
} else { } else {
// Handle server errors or validation errors // Handle server errors or validation errors
@@ -82,7 +82,7 @@ export const ProjectBreadcrumb = ({
getProjectsForSwitcherAction({ organizationId: currentOrganizationId }).then((result) => { getProjectsForSwitcherAction({ organizationId: currentOrganizationId }).then((result) => {
if (result?.data) { if (result?.data) {
// Sort projects by name // Sort projects by name
const sorted = [...result.data].sort((a, b) => a.name.localeCompare(b.name)); const sorted = result.data.toSorted((a, b) => a.name.localeCompare(b.name));
setProjects(sorted); setProjects(sorted);
} else { } else {
// Handle server errors or validation errors // Handle server errors or validation errors
@@ -12,7 +12,7 @@ const ZUpdateNotificationSettingsAction = z.object({
}); });
export const updateNotificationSettingsAction = authenticatedActionClient export const updateNotificationSettingsAction = authenticatedActionClient
.inputSchema(ZUpdateNotificationSettingsAction) .schema(ZUpdateNotificationSettingsAction)
.action( .action(
withAuditLogging( withAuditLogging(
"updated", "updated",
@@ -63,7 +63,7 @@ async function handleEmailUpdate({
return payload; return payload;
} }
export const updateUserAction = authenticatedActionClient.inputSchema(ZUserPersonalInfoUpdateInput).action( export const updateUserAction = authenticatedActionClient.schema(ZUserPersonalInfoUpdateInput).action(
withAuditLogging( withAuditLogging(
"updated", "updated",
"user", "user",
@@ -9,7 +9,7 @@ import { useTranslation } from "react-i18next";
import { z } from "zod"; import { z } from "zod";
import { TUser, TUserUpdateInput, ZUser, ZUserEmail } from "@formbricks/types/user"; import { TUser, TUserUpdateInput, ZUser, ZUserEmail } from "@formbricks/types/user";
import { PasswordConfirmationModal } from "@/app/(app)/environments/[environmentId]/settings/(account)/profile/components/password-confirmation-modal"; import { PasswordConfirmationModal } from "@/app/(app)/environments/[environmentId]/settings/(account)/profile/components/password-confirmation-modal";
import { appLanguages, sortedAppLanguages } from "@/lib/i18n/utils"; import { appLanguages } from "@/lib/i18n/utils";
import { getFormattedErrorMessage } from "@/lib/utils/helper"; import { getFormattedErrorMessage } from "@/lib/utils/helper";
import { useSignOut } from "@/modules/auth/hooks/use-sign-out"; import { useSignOut } from "@/modules/auth/hooks/use-sign-out";
import { Button } from "@/modules/ui/components/button"; import { Button } from "@/modules/ui/components/button";
@@ -198,54 +198,41 @@ export const EditProfileDetailsForm = ({
<FormField <FormField
control={form.control} control={form.control}
name="locale" name="locale"
render={({ field }) => { render={({ field }) => (
const selectedLanguage = appLanguages.find((l) => l.code === field.value); <FormItem className="mt-4">
<FormLabel>{t("common.language")}</FormLabel>
return ( <FormControl>
<FormItem className="mt-4"> <DropdownMenu>
<FormLabel>{t("common.language")}</FormLabel> <DropdownMenuTrigger asChild>
<FormControl> <Button
<DropdownMenu> type="button"
<DropdownMenuTrigger asChild> variant="ghost"
<Button className="h-10 w-full border border-slate-300 px-3 text-left">
type="button" <div className="flex w-full items-center justify-between">
variant="ghost" {appLanguages.find((l) => l.code === field.value)?.label["en-US"] ?? "NA"}
className="h-10 w-full border border-slate-300 px-3 text-left"> <ChevronDownIcon className="h-4 w-4 text-slate-500" />
<div className="flex w-full items-center justify-between"> </div>
{selectedLanguage ? ( </Button>
<> </DropdownMenuTrigger>
{selectedLanguage.label["en-US"]} <DropdownMenuContent
{selectedLanguage.label.native !== selectedLanguage.label["en-US"] && className="min-w-[var(--radix-dropdown-menu-trigger-width)] bg-white text-slate-700"
` (${selectedLanguage.label.native})`} align="start">
</> <DropdownMenuRadioGroup value={field.value} onValueChange={field.onChange}>
) : ( {appLanguages.map((lang) => (
t("common.select") <DropdownMenuRadioItem
)} key={lang.code}
<ChevronDownIcon className="h-4 w-4 text-slate-500" /> value={lang.code}
</div> className="min-h-8 cursor-pointer">
</Button> {lang.label["en-US"]}
</DropdownMenuTrigger> </DropdownMenuRadioItem>
<DropdownMenuContent ))}
className="min-w-[var(--radix-dropdown-menu-trigger-width)] bg-white text-slate-700" </DropdownMenuRadioGroup>
align="start"> </DropdownMenuContent>
<DropdownMenuRadioGroup value={field.value} onValueChange={field.onChange}> </DropdownMenu>
{sortedAppLanguages.map((lang) => ( </FormControl>
<DropdownMenuRadioItem <FormError />
key={lang.code} </FormItem>
value={lang.code} )}
className="min-h-8 cursor-pointer">
{lang.label["en-US"]}
{lang.label.native !== lang.label["en-US"] && ` (${lang.label.native})`}
</DropdownMenuRadioItem>
))}
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>
</FormControl>
<FormError />
</FormItem>
);
}}
/> />
{isPasswordResetEnabled && ( {isPasswordResetEnabled && (
@@ -98,7 +98,7 @@ export const PasswordConfirmationModal = ({
aria-label="password" aria-label="password"
aria-required="true" aria-required="true"
required required
className="block w-full rounded-md border-slate-300 shadow-sm focus:border-brand-dark focus:ring-brand-dark sm:text-sm" className="focus:border-brand-dark focus:ring-brand-dark block w-full rounded-md border-slate-300 shadow-sm sm:text-sm"
value={field.value} value={field.value}
onChange={(password) => field.onChange(password)} onChange={(password) => field.onChange(password)}
/> />
@@ -17,7 +17,7 @@ const ZUpdateOrganizationNameAction = z.object({
}); });
export const updateOrganizationNameAction = authenticatedActionClient export const updateOrganizationNameAction = authenticatedActionClient
.inputSchema(ZUpdateOrganizationNameAction) .schema(ZUpdateOrganizationNameAction)
.action( .action(
withAuditLogging( withAuditLogging(
"updated", "updated",
@@ -55,36 +55,28 @@ const ZDeleteOrganizationAction = z.object({
organizationId: ZId, organizationId: ZId,
}); });
export const deleteOrganizationAction = authenticatedActionClient export const deleteOrganizationAction = authenticatedActionClient.schema(ZDeleteOrganizationAction).action(
.inputSchema(ZDeleteOrganizationAction) withAuditLogging(
.action( "deleted",
withAuditLogging( "organization",
"deleted", async ({ ctx, parsedInput }: { ctx: AuthenticatedActionClientCtx; parsedInput: Record<string, any> }) => {
"organization", const isMultiOrgEnabled = await getIsMultiOrgEnabled();
async ({ if (!isMultiOrgEnabled) throw new OperationNotAllowedError("Organization deletion disabled");
ctx,
parsedInput,
}: {
ctx: AuthenticatedActionClientCtx;
parsedInput: Record<string, any>;
}) => {
const isMultiOrgEnabled = await getIsMultiOrgEnabled();
if (!isMultiOrgEnabled) throw new OperationNotAllowedError("Organization deletion disabled");
await checkAuthorizationUpdated({ await checkAuthorizationUpdated({
userId: ctx.user.id, userId: ctx.user.id,
organizationId: parsedInput.organizationId, organizationId: parsedInput.organizationId,
access: [ access: [
{ {
type: "organization", type: "organization",
roles: ["owner"], roles: ["owner"],
}, },
], ],
}); });
ctx.auditLoggingCtx.organizationId = parsedInput.organizationId; ctx.auditLoggingCtx.organizationId = parsedInput.organizationId;
const oldObject = await getOrganization(parsedInput.organizationId); const oldObject = await getOrganization(parsedInput.organizationId);
ctx.auditLoggingCtx.oldObject = oldObject; ctx.auditLoggingCtx.oldObject = oldObject;
return await deleteOrganization(parsedInput.organizationId); return await deleteOrganization(parsedInput.organizationId);
} }
) )
); );
@@ -9,7 +9,6 @@ import { Alert, AlertDescription } from "@/modules/ui/components/alert";
import { IdBadge } from "@/modules/ui/components/id-badge"; import { IdBadge } from "@/modules/ui/components/id-badge";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper"; import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header"; import { PageHeader } from "@/modules/ui/components/page-header";
import packageJson from "@/package.json";
import { SettingsCard } from "../../components/SettingsCard"; import { SettingsCard } from "../../components/SettingsCard";
import { DeleteOrganization } from "./components/DeleteOrganization"; import { DeleteOrganization } from "./components/DeleteOrganization";
import { EditOrganizationNameForm } from "./components/EditOrganizationNameForm"; import { EditOrganizationNameForm } from "./components/EditOrganizationNameForm";
@@ -82,10 +81,7 @@ const Page = async (props: { params: Promise<{ environmentId: string }> }) => {
</SettingsCard> </SettingsCard>
)} )}
<div className="space-y-2"> <IdBadge id={organization.id} label={t("common.organization_id")} variant="column" />
<IdBadge id={organization.id} label={t("common.organization_id")} variant="column" />
<IdBadge id={packageJson.version} label={t("common.formbricks_version")} variant="column" />
</div>
</PageContentWrapper> </PageContentWrapper>
); );
}; };
@@ -4,7 +4,6 @@ import { revalidatePath } from "next/cache";
import { z } from "zod"; import { z } from "zod";
import { ZId } from "@formbricks/types/common"; import { ZId } from "@formbricks/types/common";
import { ZResponseFilterCriteria } from "@formbricks/types/responses"; import { ZResponseFilterCriteria } from "@formbricks/types/responses";
import { getDisplaysBySurveyIdWithContact } from "@/lib/display/service";
import { getResponseCountBySurveyId, getResponses } from "@/lib/response/service"; import { getResponseCountBySurveyId, getResponses } from "@/lib/response/service";
import { authenticatedActionClient } from "@/lib/utils/action-client"; import { authenticatedActionClient } from "@/lib/utils/action-client";
import { checkAuthorizationUpdated } from "@/lib/utils/action-client/action-client-middleware"; import { checkAuthorizationUpdated } from "@/lib/utils/action-client/action-client-middleware";
@@ -23,7 +22,7 @@ const ZGetResponsesAction = z.object({
}); });
export const getResponsesAction = authenticatedActionClient export const getResponsesAction = authenticatedActionClient
.inputSchema(ZGetResponsesAction) .schema(ZGetResponsesAction)
.action(async ({ ctx, parsedInput }) => { .action(async ({ ctx, parsedInput }) => {
await checkAuthorizationUpdated({ await checkAuthorizationUpdated({
userId: ctx.user.id, userId: ctx.user.id,
@@ -57,7 +56,7 @@ const ZGetSurveySummaryAction = z.object({
}); });
export const getSurveySummaryAction = authenticatedActionClient export const getSurveySummaryAction = authenticatedActionClient
.inputSchema(ZGetSurveySummaryAction) .schema(ZGetSurveySummaryAction)
.action(async ({ ctx, parsedInput }) => { .action(async ({ ctx, parsedInput }) => {
await checkAuthorizationUpdated({ await checkAuthorizationUpdated({
userId: ctx.user.id, userId: ctx.user.id,
@@ -85,7 +84,7 @@ const ZGetResponseCountAction = z.object({
}); });
export const getResponseCountAction = authenticatedActionClient export const getResponseCountAction = authenticatedActionClient
.inputSchema(ZGetResponseCountAction) .schema(ZGetResponseCountAction)
.action(async ({ ctx, parsedInput }) => { .action(async ({ ctx, parsedInput }) => {
await checkAuthorizationUpdated({ await checkAuthorizationUpdated({
userId: ctx.user.id, userId: ctx.user.id,
@@ -107,31 +106,3 @@ export const getResponseCountAction = authenticatedActionClient
return getResponseCountBySurveyId(parsedInput.surveyId, parsedInput.filterCriteria); return getResponseCountBySurveyId(parsedInput.surveyId, parsedInput.filterCriteria);
}); });
const ZGetDisplaysWithContactAction = z.object({
surveyId: ZId,
limit: z.int().min(1).max(100),
offset: z.int().nonnegative(),
});
export const getDisplaysWithContactAction = authenticatedActionClient
.inputSchema(ZGetDisplaysWithContactAction)
.action(async ({ ctx, parsedInput }) => {
await checkAuthorizationUpdated({
userId: ctx.user.id,
organizationId: await getOrganizationIdFromSurveyId(parsedInput.surveyId),
access: [
{
type: "organization",
roles: ["owner", "manager"],
},
{
type: "projectTeam",
minPermission: "read",
projectId: await getProjectIdFromSurveyId(parsedInput.surveyId),
},
],
});
return getDisplaysBySurveyIdWithContact(parsedInput.surveyId, parsedInput.limit, parsedInput.offset);
});
@@ -3,7 +3,6 @@ import { getServerSession } from "next-auth";
import { ResponseFilterProvider } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/components/response-filter-context"; import { ResponseFilterProvider } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/components/response-filter-context";
import { getResponseCountBySurveyId } from "@/lib/response/service"; import { getResponseCountBySurveyId } from "@/lib/response/service";
import { getSurvey } from "@/lib/survey/service"; import { getSurvey } from "@/lib/survey/service";
import { getTranslate } from "@/lingodotdev/server";
import { authOptions } from "@/modules/auth/lib/authOptions"; import { authOptions } from "@/modules/auth/lib/authOptions";
type Props = { type Props = {
@@ -15,11 +14,10 @@ export const generateMetadata = async (props: Props): Promise<Metadata> => {
const session = await getServerSession(authOptions); const session = await getServerSession(authOptions);
const survey = await getSurvey(params.surveyId); const survey = await getSurvey(params.surveyId);
const responseCount = await getResponseCountBySurveyId(params.surveyId); const responseCount = await getResponseCountBySurveyId(params.surveyId);
const t = await getTranslate();
if (session) { if (session) {
return { return {
title: `${t("common.count_responses", { count: responseCount })} | ${t("environments.surveys.summary.survey_results", { surveyName: survey?.name })}`, title: `${responseCount} Responses | ${survey?.name} Results`,
}; };
} }
return { return {
@@ -22,7 +22,7 @@ const ZSendEmbedSurveyPreviewEmailAction = z.object({
}); });
export const sendEmbedSurveyPreviewEmailAction = authenticatedActionClient export const sendEmbedSurveyPreviewEmailAction = authenticatedActionClient
.inputSchema(ZSendEmbedSurveyPreviewEmailAction) .schema(ZSendEmbedSurveyPreviewEmailAction)
.action(async ({ ctx, parsedInput }) => { .action(async ({ ctx, parsedInput }) => {
const organizationId = await getOrganizationIdFromSurveyId(parsedInput.surveyId); const organizationId = await getOrganizationIdFromSurveyId(parsedInput.surveyId);
const organizationLogoUrl = await getOrganizationLogoUrl(organizationId); const organizationLogoUrl = await getOrganizationLogoUrl(organizationId);
@@ -69,7 +69,7 @@ const ZResetSurveyAction = z.object({
projectId: ZId, projectId: ZId,
}); });
export const resetSurveyAction = authenticatedActionClient.inputSchema(ZResetSurveyAction).action( export const resetSurveyAction = authenticatedActionClient.schema(ZResetSurveyAction).action(
withAuditLogging( withAuditLogging(
"updated", "updated",
"survey", "survey",
@@ -123,7 +123,7 @@ const ZGetEmailHtmlAction = z.object({
}); });
export const getEmailHtmlAction = authenticatedActionClient export const getEmailHtmlAction = authenticatedActionClient
.inputSchema(ZGetEmailHtmlAction) .schema(ZGetEmailHtmlAction)
.action(async ({ ctx, parsedInput }) => { .action(async ({ ctx, parsedInput }) => {
await checkAuthorizationUpdated({ await checkAuthorizationUpdated({
userId: ctx.user.id, userId: ctx.user.id,
@@ -152,7 +152,7 @@ const ZGeneratePersonalLinksAction = z.object({
}); });
export const generatePersonalLinksAction = authenticatedActionClient export const generatePersonalLinksAction = authenticatedActionClient
.inputSchema(ZGeneratePersonalLinksAction) .schema(ZGeneratePersonalLinksAction)
.action(async ({ ctx, parsedInput }) => { .action(async ({ ctx, parsedInput }) => {
const isContactsEnabled = await getIsContactsEnabled(); const isContactsEnabled = await getIsContactsEnabled();
if (!isContactsEnabled) { if (!isContactsEnabled) {
@@ -231,7 +231,7 @@ const ZUpdateSingleUseLinksAction = z.object({
}); });
export const updateSingleUseLinksAction = authenticatedActionClient export const updateSingleUseLinksAction = authenticatedActionClient
.inputSchema(ZUpdateSingleUseLinksAction) .schema(ZUpdateSingleUseLinksAction)
.action(async ({ ctx, parsedInput }) => { .action(async ({ ctx, parsedInput }) => {
await checkAuthorizationUpdated({ await checkAuthorizationUpdated({
userId: ctx.user.id, userId: ctx.user.id,
@@ -30,7 +30,8 @@ export const CalSummary = ({ elementSummary, survey }: CalSummaryProps) => {
</div> </div>
</div> </div>
<p className="flex w-32 items-end justify-end text-slate-600"> <p className="flex w-32 items-end justify-end text-slate-600">
{t("common.count_responses", { count: elementSummary.booked.count })} {elementSummary.booked.count}{" "}
{elementSummary.booked.count === 1 ? t("common.response") : t("common.responses")}
</p> </p>
</div> </div>
<ProgressBar barColor="bg-brand-dark" progress={elementSummary.booked.percentage / 100} /> <ProgressBar barColor="bg-brand-dark" progress={elementSummary.booked.percentage / 100} />
@@ -46,7 +47,8 @@ export const CalSummary = ({ elementSummary, survey }: CalSummaryProps) => {
</div> </div>
</div> </div>
<p className="flex w-32 items-end justify-end text-slate-600"> <p className="flex w-32 items-end justify-end text-slate-600">
{t("common.count_responses", { count: elementSummary.skipped.count })} {elementSummary.skipped.count}{" "}
{elementSummary.skipped.count === 1 ? t("common.response") : t("common.responses")}
</p> </p>
</div> </div>
<ProgressBar barColor="bg-brand-dark" progress={elementSummary.skipped.percentage / 100} /> <ProgressBar barColor="bg-brand-dark" progress={elementSummary.skipped.percentage / 100} />
@@ -64,7 +64,7 @@ export const ConsentSummary = ({ elementSummary, survey, setFilter }: ConsentSum
</div> </div>
</div> </div>
<p className="flex w-32 items-end justify-end text-slate-600"> <p className="flex w-32 items-end justify-end text-slate-600">
{t("common.count_responses", { count: summaryItem.count })} {summaryItem.count} {summaryItem.count === 1 ? t("common.response") : t("common.responses")}
</p> </p>
</div> </div>
<div className="group-hover:opacity-80"> <div className="group-hover:opacity-80">
@@ -48,7 +48,7 @@ export const ElementSummaryHeader = ({
{showResponses && ( {showResponses && (
<div className="flex items-center rounded-lg bg-slate-100 p-2"> <div className="flex items-center rounded-lg bg-slate-100 p-2">
<InboxIcon className="mr-2 h-4 w-4" /> <InboxIcon className="mr-2 h-4 w-4" />
{t("common.count_responses", { count: elementSummary.responseCount })} {`${elementSummary.responseCount} ${t("common.responses")}`}
</div> </div>
)} )}
{additionalInfo} {additionalInfo}
@@ -41,7 +41,8 @@ export const HiddenFieldsSummary = ({ environment, elementSummary, locale }: Hid
</div> </div>
<div className="flex items-center rounded-lg bg-slate-100 p-2"> <div className="flex items-center rounded-lg bg-slate-100 p-2">
<InboxIcon className="mr-2 h-4 w-4" /> <InboxIcon className="mr-2 h-4 w-4" />
{t("common.count_responses", { count: elementSummary.responseCount })} {elementSummary.responseCount}{" "}
{elementSummary.responseCount === 1 ? t("common.response") : t("common.responses")}
</div> </div>
</div> </div>
</div> </div>
@@ -31,7 +31,7 @@ export const MatrixElementSummary = ({ elementSummary, survey, setFilter }: Matr
if (label) { if (label) {
return label; return label;
} else if (percentage !== undefined && totalResponsesForRow !== undefined) { } else if (percentage !== undefined && totalResponsesForRow !== undefined) {
return t("common.count_responses", { count: Math.round((percentage / 100) * totalResponsesForRow) }); return `${Math.round((percentage / 100) * totalResponsesForRow)} ${t("common.responses")}`;
} }
return ""; return "";
}; };
@@ -77,7 +77,7 @@ export const MatrixElementSummary = ({ elementSummary, survey, setFilter }: Matr
)}> )}>
<button <button
style={{ backgroundColor: `rgba(0,196,184,${getOpacityLevel(percentage)})` }} style={{ backgroundColor: `rgba(0,196,184,${getOpacityLevel(percentage)})` }}
className="m-1 flex h-full w-40 cursor-pointer items-center justify-center rounded p-4 text-sm text-slate-950 hover:outline hover:outline-brand-dark" className="hover:outline-brand-dark m-1 flex h-full w-40 cursor-pointer items-center justify-center rounded p-4 text-sm text-slate-950 hover:outline"
onClick={() => onClick={() =>
setFilter( setFilter(
elementSummary.element.id, elementSummary.element.id,
@@ -75,7 +75,7 @@ export const MultipleChoiceSummary = ({
elementSummary.type === "multipleChoiceMulti" ? ( elementSummary.type === "multipleChoiceMulti" ? (
<div className="flex items-center rounded-lg bg-slate-100 p-2"> <div className="flex items-center rounded-lg bg-slate-100 p-2">
<InboxIcon className="mr-2 h-4 w-4" /> <InboxIcon className="mr-2 h-4 w-4" />
{t("common.count_selections", { count: elementSummary.selectionCount })} {`${elementSummary.selectionCount} ${t("common.selections")}`}
</div> </div>
) : undefined ) : undefined
} }
@@ -110,7 +110,7 @@ export const MultipleChoiceSummary = ({
</div> </div>
<div className="flex w-full space-x-2"> <div className="flex w-full space-x-2">
<p className="flex w-full pt-1 text-slate-600 sm:items-end sm:justify-end sm:pt-0"> <p className="flex w-full pt-1 text-slate-600 sm:items-end sm:justify-end sm:pt-0">
{t("common.count_selections", { count: result.count })} {result.count} {result.count === 1 ? t("common.selection") : t("common.selections")}
</p> </p>
<p className="rounded-lg bg-slate-100 px-2 text-slate-700"> <p className="rounded-lg bg-slate-100 px-2 text-slate-700">
{convertFloatToNDecimal(result.percentage, 2)}% {convertFloatToNDecimal(result.percentage, 2)}%
@@ -123,7 +123,8 @@ export const NPSSummary = ({ elementSummary, survey, setFilter }: NPSSummaryProp
</div> </div>
</div> </div>
<p className="flex w-32 items-end justify-end text-slate-600"> <p className="flex w-32 items-end justify-end text-slate-600">
{t("common.count_responses", { count: elementSummary[group]?.count })} {elementSummary[group]?.count}{" "}
{elementSummary[group]?.count === 1 ? t("common.response") : t("common.responses")}
</p> </p>
</div> </div>
<ProgressBar <ProgressBar
@@ -157,7 +158,7 @@ export const NPSSummary = ({ elementSummary, survey, setFilter }: NPSSummaryProp
}> }>
<div className="flex h-32 w-full flex-col items-center justify-end"> <div className="flex h-32 w-full flex-col items-center justify-end">
<div <div
className="w-full rounded-t-lg border border-slate-200 bg-brand-dark transition-all group-hover:brightness-110" className="bg-brand-dark w-full rounded-t-lg border border-slate-200 transition-all group-hover:brightness-110"
style={{ style={{
height: `${Math.max(choice.percentage, 2)}%`, height: `${Math.max(choice.percentage, 2)}%`,
opacity, opacity,
@@ -37,7 +37,7 @@ export const PictureChoiceSummary = ({ elementSummary, survey, setFilter }: Pict
elementSummary.element.allowMulti ? ( elementSummary.element.allowMulti ? (
<div className="flex items-center rounded-lg bg-slate-100 p-2"> <div className="flex items-center rounded-lg bg-slate-100 p-2">
<InboxIcon className="mr-2 h-4 w-4" /> <InboxIcon className="mr-2 h-4 w-4" />
{t("common.count_selections", { count: elementSummary.selectionCount })} {`${elementSummary.selectionCount} ${t("common.selections")}`}
</div> </div>
) : undefined ) : undefined
} }
@@ -74,7 +74,7 @@ export const PictureChoiceSummary = ({ elementSummary, survey, setFilter }: Pict
</div> </div>
<div className="flex w-full space-x-2"> <div className="flex w-full space-x-2">
<p className="flex w-full pt-1 text-slate-600 sm:items-end sm:justify-end sm:pt-0"> <p className="flex w-full pt-1 text-slate-600 sm:items-end sm:justify-end sm:pt-0">
{t("common.count_selections", { count: result.count })} {result.count} {result.count === 1 ? t("common.selection") : t("common.selections")}
</p> </p>
<p className="self-end rounded-lg bg-slate-100 px-2 text-slate-700"> <p className="self-end rounded-lg bg-slate-100 px-2 text-slate-700">
{convertFloatToNDecimal(result.percentage, 2)}% {convertFloatToNDecimal(result.percentage, 2)}%
@@ -116,7 +116,7 @@ export const RatingSummary = ({ elementSummary, survey, setFilter }: RatingSumma
) )
}> }>
<div <div
className={`h-full bg-brand-dark ${isFirst ? "rounded-tl-lg" : ""} ${isLast ? "rounded-tr-lg" : ""}`} className={`bg-brand-dark h-full ${isFirst ? "rounded-tl-lg" : ""} ${isLast ? "rounded-tr-lg" : ""}`}
style={{ opacity }} style={{ opacity }}
/> />
</ClickableBarSegment> </ClickableBarSegment>
@@ -198,7 +198,7 @@ export const RatingSummary = ({ elementSummary, survey, setFilter }: RatingSumma
</div> </div>
</div> </div>
<p className="flex w-32 items-end justify-end text-slate-600"> <p className="flex w-32 items-end justify-end text-slate-600">
{t("common.count_responses", { count: result.count })} {result.count} {result.count === 1 ? t("common.response") : t("common.responses")}
</p> </p>
</div> </div>
<ProgressBar barColor="bg-brand-dark" progress={result.percentage / 100} /> <ProgressBar barColor="bg-brand-dark" progress={result.percentage / 100} />
@@ -215,7 +215,8 @@ export const RatingSummary = ({ elementSummary, survey, setFilter }: RatingSumma
<div className="text flex justify-between px-2"> <div className="text flex justify-between px-2">
<p className="font-semibold text-slate-700">{t("common.dismissed")}</p> <p className="font-semibold text-slate-700">{t("common.dismissed")}</p>
<p className="flex w-32 items-end justify-end text-slate-600"> <p className="flex w-32 items-end justify-end text-slate-600">
{t("common.count_responses", { count: elementSummary.dismissed.count })} {elementSummary.dismissed.count}{" "}
{elementSummary.dismissed.count === 1 ? t("common.response") : t("common.responses")}
</p> </p>
</div> </div>
</div> </div>
@@ -1,125 +0,0 @@
"use client";
import { AlertCircleIcon, InfoIcon } from "lucide-react";
import Link from "next/link";
import { useTranslation } from "react-i18next";
import { TDisplayWithContact } from "@formbricks/types/displays";
import { TUserLocale } from "@formbricks/types/user";
import { timeSince } from "@/lib/time";
import { Button } from "@/modules/ui/components/button";
interface SummaryImpressionsProps {
displays: TDisplayWithContact[];
isLoading: boolean;
hasMore: boolean;
displaysError: string | null;
environmentId: string;
locale: TUserLocale;
onLoadMore: () => void;
onRetry: () => void;
}
const getDisplayContactIdentifier = (display: TDisplayWithContact): string => {
if (!display.contact) return "";
return display.contact.attributes?.email || display.contact.attributes?.userId || display.contact.id;
};
export const SummaryImpressions = ({
displays,
isLoading,
hasMore,
displaysError,
environmentId,
locale,
onLoadMore,
onRetry,
}: SummaryImpressionsProps) => {
const { t } = useTranslation();
const renderContent = () => {
if (displaysError) {
return (
<div className="p-8">
<div className="flex flex-col items-center gap-4 text-center">
<div className="flex items-center gap-2 text-red-600">
<AlertCircleIcon className="h-5 w-5" />
<span className="text-sm font-medium">{t("common.error_loading_data")}</span>
</div>
<p className="text-sm text-slate-500">{displaysError}</p>
<Button onClick={onRetry} variant="secondary" size="sm">
{t("common.try_again")}
</Button>
</div>
</div>
);
}
if (displays.length === 0) {
return (
<div className="p-8 text-center text-sm text-slate-500">
{t("environments.surveys.summary.no_identified_impressions")}
</div>
);
}
return (
<>
<div className="grid min-h-10 grid-cols-4 items-center border-b border-slate-200 bg-slate-100 text-sm font-semibold text-slate-600">
<div className="col-span-2 px-4 md:px-6">{t("common.user")}</div>
<div className="col-span-2 px-4 md:px-6">{t("environments.contacts.survey_viewed_at")}</div>
</div>
<div className="max-h-[62vh] overflow-y-auto">
{displays.map((display) => (
<div
key={display.id}
className="grid grid-cols-4 items-center border-b border-slate-100 py-2 text-xs text-slate-800 last:border-transparent md:text-sm">
<div className="col-span-2 pl-4 md:pl-6">
{display.contact ? (
<Link
className="ph-no-capture break-all text-slate-600 hover:underline"
href={`/environments/${environmentId}/contacts/${display.contact.id}`}>
{getDisplayContactIdentifier(display)}
</Link>
) : (
<span className="break-all text-slate-600">{t("common.anonymous")}</span>
)}
</div>
<div className="col-span-2 px-4 text-slate-500 md:px-6">
{timeSince(display.createdAt.toString(), locale)}
</div>
</div>
))}
</div>
{hasMore && (
<div className="flex justify-center border-t border-slate-100 py-4">
<Button onClick={onLoadMore} variant="secondary" size="sm">
{t("common.load_more")}
</Button>
</div>
)}
</>
);
};
if (isLoading) {
return (
<div className="rounded-xl border border-slate-200 bg-white p-8 shadow-sm">
<div className="flex items-center justify-center">
<div className="h-6 w-32 animate-pulse rounded-full bg-slate-200"></div>
</div>
</div>
);
}
return (
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
<div className="flex items-center gap-2 rounded-t-xl border-b border-slate-200 bg-slate-50 px-4 py-3 text-sm text-slate-600">
<InfoIcon className="h-4 w-4 shrink-0" />
<span>{t("environments.surveys.summary.impressions_identified_only")}</span>
</div>
{renderContent()}
</div>
);
};
@@ -10,8 +10,8 @@ interface SummaryMetadataProps {
surveySummary: TSurveySummary["meta"]; surveySummary: TSurveySummary["meta"];
quotasCount: number; quotasCount: number;
isLoading: boolean; isLoading: boolean;
tab: "dropOffs" | "quotas" | "impressions" | undefined; tab: "dropOffs" | "quotas" | undefined;
setTab: React.Dispatch<React.SetStateAction<"dropOffs" | "quotas" | "impressions" | undefined>>; setTab: React.Dispatch<React.SetStateAction<"dropOffs" | "quotas" | undefined>>;
isQuotasAllowed: boolean; isQuotasAllowed: boolean;
} }
@@ -53,7 +53,7 @@ export const SummaryMetadata = ({
const { t } = useTranslation(); const { t } = useTranslation();
const dropoffCountValue = dropOffCount === 0 ? <span>-</span> : dropOffCount; const dropoffCountValue = dropOffCount === 0 ? <span>-</span> : dropOffCount;
const handleTabChange = (val: "dropOffs" | "quotas" | "impressions") => { const handleTabChange = (val: "dropOffs" | "quotas") => {
const change = tab === val ? undefined : val; const change = tab === val ? undefined : val;
setTab(change); setTab(change);
}; };
@@ -65,16 +65,12 @@ export const SummaryMetadata = ({
`grid gap-4 sm:grid-cols-2 md:grid-cols-3 md:gap-x-2 lg:grid-cols-3 2xl:grid-cols-5`, `grid gap-4 sm:grid-cols-2 md:grid-cols-3 md:gap-x-2 lg:grid-cols-3 2xl:grid-cols-5`,
isQuotasAllowed && quotasCount > 0 && "2xl:grid-cols-6" isQuotasAllowed && quotasCount > 0 && "2xl:grid-cols-6"
)}> )}>
<InteractiveCard <StatCard
key="impressions"
tab="impressions"
label={t("environments.surveys.summary.impressions")} label={t("environments.surveys.summary.impressions")}
percentage={null} percentage={null}
value={displayCount === 0 ? <span>-</span> : displayCount} value={displayCount === 0 ? <span>-</span> : displayCount}
tooltipText={t("environments.surveys.summary.impressions_tooltip")} tooltipText={t("environments.surveys.summary.impressions_tooltip")}
isLoading={isLoading} isLoading={isLoading}
onClick={() => handleTabChange("impressions")}
isActive={tab === "impressions"}
/> />
<StatCard <StatCard
label={t("environments.surveys.summary.starts")} label={t("environments.surveys.summary.starts")}
@@ -1,31 +1,21 @@
"use client"; "use client";
import { useSearchParams } from "next/navigation"; import { useSearchParams } from "next/navigation";
import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import toast from "react-hot-toast";
import { useTranslation } from "react-i18next";
import { TDisplayWithContact } from "@formbricks/types/displays";
import { TEnvironment } from "@formbricks/types/environment"; import { TEnvironment } from "@formbricks/types/environment";
import { TSurvey, TSurveySummary } from "@formbricks/types/surveys/types"; import { TSurvey, TSurveySummary } from "@formbricks/types/surveys/types";
import { TUserLocale } from "@formbricks/types/user"; import { TUserLocale } from "@formbricks/types/user";
import { import { getSurveySummaryAction } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/actions";
getDisplaysWithContactAction,
getSurveySummaryAction,
} from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/actions";
import { useResponseFilter } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/components/response-filter-context"; import { useResponseFilter } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/components/response-filter-context";
import ScrollToTop from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/ScrollToTop"; import ScrollToTop from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/ScrollToTop";
import { SummaryDropOffs } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryDropOffs"; import { SummaryDropOffs } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryDropOffs";
import { SummaryImpressions } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryImpressions";
import { CustomFilter } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/components/CustomFilter"; import { CustomFilter } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/components/CustomFilter";
import { getFormattedFilters } from "@/app/lib/surveys/surveys"; import { getFormattedFilters } from "@/app/lib/surveys/surveys";
import { getFormattedErrorMessage } from "@/lib/utils/helper";
import { replaceHeadlineRecall } from "@/lib/utils/recall"; import { replaceHeadlineRecall } from "@/lib/utils/recall";
import { QuotasSummary } from "@/modules/ee/quotas/components/quotas-summary"; import { QuotasSummary } from "@/modules/ee/quotas/components/quotas-summary";
import { SummaryList } from "./SummaryList"; import { SummaryList } from "./SummaryList";
import { SummaryMetadata } from "./SummaryMetadata"; import { SummaryMetadata } from "./SummaryMetadata";
const DISPLAYS_PER_PAGE = 15;
const defaultSurveySummary: TSurveySummary = { const defaultSurveySummary: TSurveySummary = {
meta: { meta: {
completedPercentage: 0, completedPercentage: 0,
@@ -61,76 +51,17 @@ export const SummaryPage = ({
initialSurveySummary, initialSurveySummary,
isQuotasAllowed, isQuotasAllowed,
}: SummaryPageProps) => { }: SummaryPageProps) => {
const { t } = useTranslation();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const [surveySummary, setSurveySummary] = useState<TSurveySummary>( const [surveySummary, setSurveySummary] = useState<TSurveySummary>(
initialSurveySummary || defaultSurveySummary initialSurveySummary || defaultSurveySummary
); );
const [tab, setTab] = useState<"dropOffs" | "quotas" | "impressions" | undefined>(undefined); const [tab, setTab] = useState<"dropOffs" | "quotas" | undefined>(undefined);
const [isLoading, setIsLoading] = useState(!initialSurveySummary); const [isLoading, setIsLoading] = useState(!initialSurveySummary);
const { selectedFilter, dateRange, resetState } = useResponseFilter(); const { selectedFilter, dateRange, resetState } = useResponseFilter();
const [displays, setDisplays] = useState<TDisplayWithContact[]>([]);
const [isDisplaysLoading, setIsDisplaysLoading] = useState(false);
const [hasMoreDisplays, setHasMoreDisplays] = useState(true);
const [displaysError, setDisplaysError] = useState<string | null>(null);
const displaysFetchedRef = useRef(false);
const fetchDisplays = useCallback(
async (offset: number) => {
const response = await getDisplaysWithContactAction({
surveyId,
limit: DISPLAYS_PER_PAGE,
offset,
});
if (!response?.data) {
const errorMessage = getFormattedErrorMessage(response);
throw new Error(errorMessage);
}
return response?.data ?? [];
},
[surveyId]
);
const loadInitialDisplays = useCallback(async () => {
setIsDisplaysLoading(true);
setDisplaysError(null);
try {
const data = await fetchDisplays(0);
setDisplays(data);
setHasMoreDisplays(data.length === DISPLAYS_PER_PAGE);
} catch (error) {
toast.error(error);
setDisplays([]);
setHasMoreDisplays(false);
} finally {
setIsDisplaysLoading(false);
}
}, [fetchDisplays, t]);
const handleLoadMoreDisplays = useCallback(async () => {
try {
const data = await fetchDisplays(displays.length);
setDisplays((prev) => [...prev, ...data]);
setHasMoreDisplays(data.length === DISPLAYS_PER_PAGE);
} catch (error) {
const errorMessage = error instanceof Error ? error.message : t("common.something_went_wrong");
toast.error(errorMessage);
}
}, [fetchDisplays, displays.length, t]);
useEffect(() => {
if (tab === "impressions" && !displaysFetchedRef.current) {
displaysFetchedRef.current = true;
loadInitialDisplays();
}
}, [tab, loadInitialDisplays]);
// Only fetch data when filters change or when there's no initial data // Only fetch data when filters change or when there's no initial data
useEffect(() => { useEffect(() => {
// If we have initial data and no filters are applied, don't fetch // If we have initial data and no filters are applied, don't fetch
@@ -190,18 +121,6 @@ export const SummaryPage = ({
setTab={setTab} setTab={setTab}
isQuotasAllowed={isQuotasAllowed} isQuotasAllowed={isQuotasAllowed}
/> />
{tab === "impressions" && (
<SummaryImpressions
displays={displays}
isLoading={isDisplaysLoading}
hasMore={hasMoreDisplays}
displaysError={displaysError}
environmentId={environment.id}
locale={locale}
onLoadMore={handleLoadMoreDisplays}
onRetry={loadInitialDisplays}
/>
)}
{tab === "dropOffs" && <SummaryDropOffs dropOff={surveySummary.dropOff} survey={surveyMemoized} />} {tab === "dropOffs" && <SummaryDropOffs dropOff={surveySummary.dropOff} survey={surveyMemoized} />}
{isQuotasAllowed && tab === "quotas" && <QuotasSummary quotas={surveySummary.quotas} />} {isQuotasAllowed && tab === "quotas" && <QuotasSummary quotas={surveySummary.quotas} />}
<div className="flex gap-1.5"> <div className="flex gap-1.5">
@@ -4,9 +4,9 @@ import { ChevronDownIcon, ChevronUpIcon } from "lucide-react";
import { BaseCard } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/base-card"; import { BaseCard } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/base-card";
interface InteractiveCardProps { interface InteractiveCardProps {
tab: "dropOffs" | "quotas" | "impressions"; tab: "dropOffs" | "quotas";
label: string; label: string;
percentage: number | null; percentage: number;
value: React.ReactNode; value: React.ReactNode;
tooltipText: string; tooltipText: string;
isLoading: boolean; isLoading: boolean;
@@ -352,7 +352,7 @@ export const AnonymousLinksTab = ({
}, },
{ {
title: t("environments.surveys.share.anonymous_links.custom_start_point"), title: t("environments.surveys.share.anonymous_links.custom_start_point"),
href: "https://formbricks.com/docs/xm-and-surveys/surveys/link-surveys/start-at-block", href: "https://formbricks.com/docs/xm-and-surveys/surveys/link-surveys/start-at-question",
}, },
]} ]}
/> />
@@ -105,7 +105,7 @@ export const CustomHtmlTab = ({ projectCustomScripts, isReadOnly }: CustomHtmlTa
<div className={scriptsMode === "replace" ? "opacity-50" : ""}> <div className={scriptsMode === "replace" ? "opacity-50" : ""}>
<FormLabel>{t("environments.surveys.share.custom_html.workspace_scripts_label")}</FormLabel> <FormLabel>{t("environments.surveys.share.custom_html.workspace_scripts_label")}</FormLabel>
<div className="mt-2 max-h-32 overflow-auto rounded-md border border-slate-200 bg-slate-50 p-3"> <div className="mt-2 max-h-32 overflow-auto rounded-md border border-slate-200 bg-slate-50 p-3">
<pre className="whitespace-pre-wrap font-mono text-xs text-slate-600"> <pre className="font-mono text-xs whitespace-pre-wrap text-slate-600">
{projectCustomScripts} {projectCustomScripts}
</pre> </pre>
</div> </div>
@@ -135,7 +135,7 @@ export const CustomHtmlTab = ({ projectCustomScripts, isReadOnly }: CustomHtmlTa
rows={8} rows={8}
placeholder={t("environments.surveys.share.custom_html.placeholder")} placeholder={t("environments.surveys.share.custom_html.placeholder")}
className={cn( className={cn(
"flex w-full rounded-md border border-slate-300 bg-white px-3 py-2 font-mono text-xs text-slate-800 placeholder:text-slate-400 focus:border-brand-dark focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50" "focus:border-brand-dark flex w-full rounded-md border border-slate-300 bg-white px-3 py-2 font-mono text-xs text-slate-800 placeholder:text-slate-400 focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50"
)} )}
{...field} {...field}
disabled={isReadOnly} disabled={isReadOnly}
@@ -66,7 +66,7 @@ export const SuccessView: React.FC<SuccessViewProps> = ({
className="relative flex flex-col items-center gap-3 rounded-lg border border-slate-100 bg-white p-4 text-center text-sm text-slate-900 hover:border-slate-200 md:p-8"> className="relative flex flex-col items-center gap-3 rounded-lg border border-slate-100 bg-white p-4 text-center text-sm text-slate-900 hover:border-slate-200 md:p-8">
<UserIcon className="h-8 w-8 stroke-1 text-slate-900" /> <UserIcon className="h-8 w-8 stroke-1 text-slate-900" />
{t("environments.surveys.summary.use_personal_links")} {t("environments.surveys.summary.use_personal_links")}
<Badge size="normal" type="success" className="absolute right-3 top-3" text={t("common.new")} /> <Badge size="normal" type="success" className="absolute top-3 right-3" text={t("common.new")} />
</button> </button>
<Link <Link
href={`/environments/${environmentId}/settings/notifications`} href={`/environments/${environmentId}/settings/notifications`}
@@ -1095,7 +1095,7 @@ export const getResponsesForSummary = reactCache(
[limit, ZOptionalNumber], [limit, ZOptionalNumber],
[offset, ZOptionalNumber], [offset, ZOptionalNumber],
[filterCriteria, ZResponseFilterCriteria.optional()], [filterCriteria, ZResponseFilterCriteria.optional()],
[cursor, z.cuid2().optional()] [cursor, z.string().cuid2().optional()]
); );
const queryLimit = limit ?? RESPONSES_PER_PAGE; const queryLimit = limit ?? RESPONSES_PER_PAGE;
@@ -28,7 +28,7 @@ const ZGetResponsesDownloadUrlAction = z.object({
}); });
export const getResponsesDownloadUrlAction = authenticatedActionClient export const getResponsesDownloadUrlAction = authenticatedActionClient
.inputSchema(ZGetResponsesDownloadUrlAction) .schema(ZGetResponsesDownloadUrlAction)
.action(async ({ ctx, parsedInput }) => { .action(async ({ ctx, parsedInput }) => {
await checkAuthorizationUpdated({ await checkAuthorizationUpdated({
userId: ctx.user.id, userId: ctx.user.id,
@@ -58,7 +58,7 @@ const ZGetSurveyFilterDataAction = z.object({
}); });
export const getSurveyFilterDataAction = authenticatedActionClient export const getSurveyFilterDataAction = authenticatedActionClient
.inputSchema(ZGetSurveyFilterDataAction) .schema(ZGetSurveyFilterDataAction)
.action(async ({ ctx, parsedInput }) => { .action(async ({ ctx, parsedInput }) => {
const survey = await getSurvey(parsedInput.surveyId); const survey = await getSurvey(parsedInput.surveyId);
@@ -121,7 +121,7 @@ const checkSurveyFollowUpsPermission = async (organizationId: string): Promise<v
} }
}; };
export const updateSurveyAction = authenticatedActionClient.inputSchema(ZSurvey).action( export const updateSurveyAction = authenticatedActionClient.schema(ZSurvey).action(
withAuditLogging( withAuditLogging(
"updated", "updated",
"survey", "survey",
@@ -192,7 +192,7 @@ export const ElementsComboBox = ({ options, selected, onChangeValue }: ElementCo
value={inputValue} value={inputValue}
onValueChange={setInputValue} onValueChange={setInputValue}
placeholder={open ? `${t("common.search")}...` : t("common.select_filter")} placeholder={open ? `${t("common.search")}...` : t("common.select_filter")}
className="max-w-full grow border-none p-0 pl-2 text-sm shadow-none outline-none ring-offset-transparent focus:border-none focus:shadow-none focus:outline-none focus:ring-offset-0" className="max-w-full grow border-none p-0 pl-2 text-sm shadow-none ring-offset-transparent outline-none focus:border-none focus:shadow-none focus:ring-offset-0 focus:outline-none"
/> />
)} )}
<Button <Button
@@ -241,7 +241,7 @@ export const ResponseFilter = ({ survey }: ResponseFilterProps) => {
<Popover open={isOpen} onOpenChange={handleOpenChange}> <Popover open={isOpen} onOpenChange={handleOpenChange}>
<PopoverTrigger asChild> <PopoverTrigger asChild>
<PopoverTriggerButton isOpen={isOpen}> <PopoverTriggerButton isOpen={isOpen}>
{t("common.filter")} <b>{activeFilterCount > 0 && `(${activeFilterCount})`}</b> Filter <b>{activeFilterCount > 0 && `(${activeFilterCount})`}</b>
</PopoverTriggerButton> </PopoverTriggerButton>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent <PopoverContent
@@ -329,7 +329,7 @@ export const ResponseFilter = ({ survey }: ResponseFilterProps) => {
</div> </div>
{i !== filterValue.filter.length - 1 && ( {i !== filterValue.filter.length - 1 && (
<div className="my-4 flex items-center"> <div className="my-4 flex items-center">
<p className="mr-4 font-semibold text-slate-800">{t("common.and")}</p> <p className="mr-4 font-semibold text-slate-800">and</p>
<hr className="w-full text-slate-600" /> <hr className="w-full text-slate-600" />
</div> </div>
)} )}
@@ -1,208 +0,0 @@
"use client";
import { CheckCircle2, Sparkles } from "lucide-react";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { Button } from "@/modules/ui/components/button";
const FORMBRICKS_HOST = "https://app.formbricks.com";
const SURVEY_ID = "cr9r4b2r73x6hlmn5aa2ha44";
const ENVIRONMENT_ID = "cmk41i8bi92bdad01svi74dec";
interface WorkflowsPageProps {
userEmail: string;
organizationName: string;
billingPlan: string;
}
type Step = "prompt" | "followup" | "thankyou";
export const WorkflowsPage = ({ userEmail, organizationName, billingPlan }: WorkflowsPageProps) => {
const { t } = useTranslation();
const [step, setStep] = useState<Step>("prompt");
const [promptValue, setPromptValue] = useState("");
const [detailsValue, setDetailsValue] = useState("");
const [responseId, setResponseId] = useState<string | null>(null);
const [isSubmitting, setIsSubmitting] = useState(false);
const handleGenerateWorkflow = async () => {
if (promptValue.trim().length < 100 || isSubmitting) return;
setIsSubmitting(true);
try {
const res = await fetch(`${FORMBRICKS_HOST}/api/v2/client/${ENVIRONMENT_ID}/responses`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
surveyId: SURVEY_ID,
finished: false,
data: {
workflow: promptValue.trim(),
useremail: userEmail,
orgname: organizationName,
billingplan: billingPlan,
},
}),
});
if (res.ok) {
const json = await res.json();
setResponseId(json.data?.id ?? null);
}
setStep("followup");
} catch {
setStep("followup");
} finally {
setIsSubmitting(false);
}
};
const handleSubmitFeedback = async () => {
if (isSubmitting) return;
setIsSubmitting(true);
if (responseId) {
try {
await fetch(`${FORMBRICKS_HOST}/api/v1/client/${ENVIRONMENT_ID}/responses/${responseId}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
finished: true,
data: {
details: detailsValue.trim(),
},
}),
});
} catch {
// silently fail
}
}
setIsSubmitting(false);
setStep("thankyou");
};
const handleSkipFeedback = async () => {
if (!responseId) {
setStep("thankyou");
return;
}
try {
await fetch(`${FORMBRICKS_HOST}/api/v1/client/${ENVIRONMENT_ID}/responses/${responseId}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
finished: true,
data: {},
}),
});
} catch {
// silently fail
}
setStep("thankyou");
};
if (step === "prompt") {
return (
<div className="flex h-full flex-col items-center px-4 pt-[15vh]">
<div className="w-full max-w-2xl space-y-8">
<div className="space-y-3 text-center">
<div className="from-brand-light to-brand-dark mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-xl bg-gradient-to-br shadow-md">
<Sparkles className="h-6 w-6 text-white" />
</div>
<h1 className="text-4xl font-bold tracking-tight text-slate-800">{t("workflows.heading")}</h1>
<p className="text-lg text-slate-500">{t("workflows.subheading")}</p>
</div>
<div className="relative">
<textarea
value={promptValue}
onChange={(e) => setPromptValue(e.target.value)}
placeholder={t("workflows.placeholder")}
rows={5}
className="focus:border-brand-dark focus:ring-brand-light/20 w-full resize-none rounded-xl border border-slate-200 bg-white px-5 py-4 text-base text-slate-800 shadow-sm transition-all placeholder:text-slate-400 focus:outline-none focus:ring-2"
onKeyDown={(e) => {
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
handleGenerateWorkflow();
}
}}
/>
<div className="mt-3 flex items-center justify-between">
<span
className={`text-xs ${promptValue.trim().length >= 100 ? "text-slate-400" : "text-amber-500"}`}>
{promptValue.trim().length} / 100
</span>
<Button
onClick={handleGenerateWorkflow}
disabled={promptValue.trim().length < 100 || isSubmitting}
loading={isSubmitting}
size="lg">
<Sparkles className="h-4 w-4" />
{t("workflows.generate_button")}
</Button>
</div>
</div>
</div>
</div>
);
}
if (step === "followup") {
return (
<div className="flex h-full flex-col items-center px-4 pt-[15vh]">
<div className="w-full max-w-2xl space-y-8">
<div className="space-y-3 text-center">
<div className="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-xl bg-slate-100">
<Sparkles className="text-brand-dark h-6 w-6" />
</div>
<h1 className="text-3xl font-bold tracking-tight text-slate-800">
{t("workflows.coming_soon_title")}
</h1>
<p className="mx-auto max-w-md text-base text-slate-500">
{t("workflows.coming_soon_description")}
</p>
</div>
<div className="rounded-xl border border-slate-200 bg-white p-6 shadow-sm">
<label className="text-md mb-2 block font-medium text-slate-700">
{t("workflows.follow_up_label")}
</label>
<textarea
value={detailsValue}
onChange={(e) => setDetailsValue(e.target.value)}
placeholder={t("workflows.follow_up_placeholder")}
rows={4}
className="focus:border-brand-dark focus:ring-brand-light/20 w-full resize-none rounded-lg border border-slate-200 bg-slate-50 px-4 py-3 text-sm text-slate-800 transition-all placeholder:text-slate-400 focus:bg-white focus:outline-none focus:ring-2"
/>
<div className="mt-4 flex items-center justify-end gap-3">
<Button variant="ghost" onClick={handleSkipFeedback} className="text-slate-500">
{t("common.skip")}
</Button>
<Button
onClick={handleSubmitFeedback}
disabled={!detailsValue.trim() || isSubmitting}
loading={isSubmitting}>
{t("workflows.submit_button")}
</Button>
</div>
</div>
</div>
</div>
);
}
return (
<div className="flex h-full flex-col items-center px-4 pt-[15vh]">
<div className="w-full max-w-md space-y-6 text-center">
<div className="mx-auto flex h-16 w-16 items-center justify-center rounded-full bg-green-50">
<CheckCircle2 className="h-8 w-8 text-green-500" />
</div>
<h1 className="text-2xl font-bold text-slate-800">{t("workflows.thank_you_title")}</h1>
<p className="text-base text-slate-500">{t("workflows.thank_you_description")}</p>
</div>
</div>
);
};
@@ -1,39 +0,0 @@
import { Metadata } from "next";
import { notFound, redirect } from "next/navigation";
import { IS_FORMBRICKS_CLOUD } from "@/lib/constants";
import { getUser } from "@/lib/user/service";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { WorkflowsPage } from "./components/workflows-page";
export const metadata: Metadata = {
title: "Workflows",
};
const Page = async (props: { params: Promise<{ environmentId: string }> }) => {
const params = await props.params;
if (!IS_FORMBRICKS_CLOUD) {
return notFound();
}
const { session, organization, isBilling } = await getEnvironmentAuth(params.environmentId);
if (isBilling) {
return redirect(`/environments/${params.environmentId}/settings/billing`);
}
const user = await getUser(session.user.id);
if (!user) {
return redirect("/auth/login");
}
return (
<WorkflowsPage
userEmail={user.email}
organizationName={organization.name}
billingPlan={organization.billing.plan}
/>
);
};
export default Page;
@@ -21,7 +21,7 @@ const ZCreateOrUpdateIntegrationAction = z.object({
}); });
export const createOrUpdateIntegrationAction = authenticatedActionClient export const createOrUpdateIntegrationAction = authenticatedActionClient
.inputSchema(ZCreateOrUpdateIntegrationAction) .schema(ZCreateOrUpdateIntegrationAction)
.action( .action(
withAuditLogging( withAuditLogging(
"createdUpdated", "createdUpdated",
@@ -67,7 +67,7 @@ const ZDeleteIntegrationAction = z.object({
integrationId: ZId, integrationId: ZId,
}); });
export const deleteIntegrationAction = authenticatedActionClient.inputSchema(ZDeleteIntegrationAction).action( export const deleteIntegrationAction = authenticatedActionClient.schema(ZDeleteIntegrationAction).action(
withAuditLogging( withAuditLogging(
"deleted", "deleted",
"integration", "integration",
@@ -1,49 +1,12 @@
"use server"; "use server";
import { z } from "zod"; import { z } from "zod";
import { ZId } from "@formbricks/types/common"; import { ZIntegrationGoogleSheets } from "@formbricks/types/integration/google-sheet";
import { import { getSpreadsheetNameById } from "@/lib/googleSheet/service";
TIntegrationGoogleSheets,
ZIntegrationGoogleSheets,
} from "@formbricks/types/integration/google-sheet";
import { getSpreadsheetNameById, validateGoogleSheetsConnection } from "@/lib/googleSheet/service";
import { getIntegrationByType } from "@/lib/integration/service";
import { authenticatedActionClient } from "@/lib/utils/action-client"; import { authenticatedActionClient } from "@/lib/utils/action-client";
import { checkAuthorizationUpdated } from "@/lib/utils/action-client/action-client-middleware"; import { checkAuthorizationUpdated } from "@/lib/utils/action-client/action-client-middleware";
import { getOrganizationIdFromEnvironmentId, getProjectIdFromEnvironmentId } from "@/lib/utils/helper"; import { getOrganizationIdFromEnvironmentId, getProjectIdFromEnvironmentId } from "@/lib/utils/helper";
const ZValidateGoogleSheetsConnectionAction = z.object({
environmentId: ZId,
});
export const validateGoogleSheetsConnectionAction = authenticatedActionClient
.inputSchema(ZValidateGoogleSheetsConnectionAction)
.action(async ({ ctx, parsedInput }) => {
await checkAuthorizationUpdated({
userId: ctx.user.id,
organizationId: await getOrganizationIdFromEnvironmentId(parsedInput.environmentId),
access: [
{
type: "organization",
roles: ["owner", "manager"],
},
{
type: "projectTeam",
projectId: await getProjectIdFromEnvironmentId(parsedInput.environmentId),
minPermission: "readWrite",
},
],
});
const integration = await getIntegrationByType(parsedInput.environmentId, "googleSheets");
if (!integration) {
return { data: false };
}
await validateGoogleSheetsConnection(integration as TIntegrationGoogleSheets);
return { data: true };
});
const ZGetSpreadsheetNameByIdAction = z.object({ const ZGetSpreadsheetNameByIdAction = z.object({
googleSheetIntegration: ZIntegrationGoogleSheets, googleSheetIntegration: ZIntegrationGoogleSheets,
environmentId: z.string(), environmentId: z.string(),
@@ -51,7 +14,7 @@ const ZGetSpreadsheetNameByIdAction = z.object({
}); });
export const getSpreadsheetNameByIdAction = authenticatedActionClient export const getSpreadsheetNameByIdAction = authenticatedActionClient
.inputSchema(ZGetSpreadsheetNameByIdAction) .schema(ZGetSpreadsheetNameByIdAction)
.action(async ({ ctx, parsedInput }) => { .action(async ({ ctx, parsedInput }) => {
await checkAuthorizationUpdated({ await checkAuthorizationUpdated({
userId: ctx.user.id, userId: ctx.user.id,
@@ -20,10 +20,6 @@ import {
isValidGoogleSheetsUrl, isValidGoogleSheetsUrl,
} from "@/app/(app)/environments/[environmentId]/workspace/integrations/google-sheets/lib/util"; } from "@/app/(app)/environments/[environmentId]/workspace/integrations/google-sheets/lib/util";
import GoogleSheetLogo from "@/images/googleSheetsLogo.png"; import GoogleSheetLogo from "@/images/googleSheetsLogo.png";
import {
GOOGLE_SHEET_INTEGRATION_INSUFFICIENT_PERMISSION,
GOOGLE_SHEET_INTEGRATION_INVALID_GRANT,
} from "@/lib/googleSheet/constants";
import { getFormattedErrorMessage } from "@/lib/utils/helper"; import { getFormattedErrorMessage } from "@/lib/utils/helper";
import { recallToHeadline } from "@/lib/utils/recall"; import { recallToHeadline } from "@/lib/utils/recall";
import { getElementsFromBlocks } from "@/modules/survey/lib/client-utils"; import { getElementsFromBlocks } from "@/modules/survey/lib/client-utils";
@@ -122,17 +118,6 @@ export const AddIntegrationModal = ({
resetForm(); resetForm();
}, [selectedIntegration, surveys]); }, [selectedIntegration, surveys]);
const showErrorMessageToast = (response: Awaited<ReturnType<typeof getSpreadsheetNameByIdAction>>) => {
const errorMessage = getFormattedErrorMessage(response);
if (errorMessage === GOOGLE_SHEET_INTEGRATION_INVALID_GRANT) {
toast.error(t("environments.integrations.google_sheets.token_expired_error"));
} else if (errorMessage === GOOGLE_SHEET_INTEGRATION_INSUFFICIENT_PERMISSION) {
toast.error(t("environments.integrations.google_sheets.spreadsheet_permission_error"));
} else {
toast.error(errorMessage);
}
};
const linkSheet = async () => { const linkSheet = async () => {
try { try {
if (!isValidGoogleSheetsUrl(spreadsheetUrl)) { if (!isValidGoogleSheetsUrl(spreadsheetUrl)) {
@@ -144,7 +129,6 @@ export const AddIntegrationModal = ({
if (selectedElements.length === 0) { if (selectedElements.length === 0) {
throw new Error(t("environments.integrations.select_at_least_one_question_error")); throw new Error(t("environments.integrations.select_at_least_one_question_error"));
} }
setIsLinkingSheet(true);
const spreadsheetId = extractSpreadsheetIdFromUrl(spreadsheetUrl); const spreadsheetId = extractSpreadsheetIdFromUrl(spreadsheetUrl);
const spreadsheetNameResponse = await getSpreadsheetNameByIdAction({ const spreadsheetNameResponse = await getSpreadsheetNameByIdAction({
googleSheetIntegration, googleSheetIntegration,
@@ -153,11 +137,13 @@ export const AddIntegrationModal = ({
}); });
if (!spreadsheetNameResponse?.data) { if (!spreadsheetNameResponse?.data) {
showErrorMessageToast(spreadsheetNameResponse); const errorMessage = getFormattedErrorMessage(spreadsheetNameResponse);
return; throw new Error(errorMessage);
} }
const spreadsheetName = spreadsheetNameResponse.data; const spreadsheetName = spreadsheetNameResponse.data;
setIsLinkingSheet(true);
integrationData.spreadsheetId = spreadsheetId; integrationData.spreadsheetId = spreadsheetId;
integrationData.spreadsheetName = spreadsheetName; integrationData.spreadsheetName = spreadsheetName;
integrationData.surveyId = selectedSurvey.id; integrationData.surveyId = selectedSurvey.id;
@@ -294,7 +280,7 @@ export const AddIntegrationModal = ({
<div className="space-y-4"> <div className="space-y-4">
<div> <div>
<Label htmlFor="Surveys">{t("common.questions")}</Label> <Label htmlFor="Surveys">{t("common.questions")}</Label>
<div className="mt-1 max-h-[15vh] overflow-y-auto overflow-x-hidden rounded-lg border border-slate-200"> <div className="mt-1 max-h-[15vh] overflow-x-hidden overflow-y-auto rounded-lg border border-slate-200">
<div className="grid content-center rounded-lg bg-slate-50 p-3 text-left text-sm text-slate-900"> <div className="grid content-center rounded-lg bg-slate-50 p-3 text-left text-sm text-slate-900">
{surveyElements.map((question) => ( {surveyElements.map((question) => (
<div key={question.id} className="my-1 flex items-center space-x-2"> <div key={question.id} className="my-1 flex items-center space-x-2">
@@ -1,6 +1,6 @@
"use client"; "use client";
import { useCallback, useEffect, useState } from "react"; import { useState } from "react";
import { TEnvironment } from "@formbricks/types/environment"; import { TEnvironment } from "@formbricks/types/environment";
import { import {
TIntegrationGoogleSheets, TIntegrationGoogleSheets,
@@ -8,11 +8,9 @@ import {
} from "@formbricks/types/integration/google-sheet"; } from "@formbricks/types/integration/google-sheet";
import { TSurvey } from "@formbricks/types/surveys/types"; import { TSurvey } from "@formbricks/types/surveys/types";
import { TUserLocale } from "@formbricks/types/user"; import { TUserLocale } from "@formbricks/types/user";
import { validateGoogleSheetsConnectionAction } from "@/app/(app)/environments/[environmentId]/workspace/integrations/google-sheets/actions";
import { ManageIntegration } from "@/app/(app)/environments/[environmentId]/workspace/integrations/google-sheets/components/ManageIntegration"; import { ManageIntegration } from "@/app/(app)/environments/[environmentId]/workspace/integrations/google-sheets/components/ManageIntegration";
import { authorize } from "@/app/(app)/environments/[environmentId]/workspace/integrations/google-sheets/lib/google"; import { authorize } from "@/app/(app)/environments/[environmentId]/workspace/integrations/google-sheets/lib/google";
import googleSheetLogo from "@/images/googleSheetsLogo.png"; import googleSheetLogo from "@/images/googleSheetsLogo.png";
import { GOOGLE_SHEET_INTEGRATION_INVALID_GRANT } from "@/lib/googleSheet/constants";
import { ConnectIntegration } from "@/modules/ui/components/connect-integration"; import { ConnectIntegration } from "@/modules/ui/components/connect-integration";
import { AddIntegrationModal } from "./AddIntegrationModal"; import { AddIntegrationModal } from "./AddIntegrationModal";
@@ -37,23 +35,10 @@ export const GoogleSheetWrapper = ({
googleSheetIntegration ? googleSheetIntegration.config?.key : false googleSheetIntegration ? googleSheetIntegration.config?.key : false
); );
const [isModalOpen, setIsModalOpen] = useState<boolean>(false); const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
const [showReconnectButton, setShowReconnectButton] = useState<boolean>(false);
const [selectedIntegration, setSelectedIntegration] = useState< const [selectedIntegration, setSelectedIntegration] = useState<
(TIntegrationGoogleSheetsConfigData & { index: number }) | null (TIntegrationGoogleSheetsConfigData & { index: number }) | null
>(null); >(null);
const validateConnection = useCallback(async () => {
if (!isConnected || !googleSheetIntegration) return;
const response = await validateGoogleSheetsConnectionAction({ environmentId: environment.id });
if (response?.serverError === GOOGLE_SHEET_INTEGRATION_INVALID_GRANT) {
setShowReconnectButton(true);
}
}, [environment.id, isConnected, googleSheetIntegration]);
useEffect(() => {
validateConnection();
}, [validateConnection]);
const handleGoogleAuthorization = async () => { const handleGoogleAuthorization = async () => {
authorize(environment.id, webAppUrl).then((url: string) => { authorize(environment.id, webAppUrl).then((url: string) => {
if (url) { if (url) {
@@ -79,8 +64,6 @@ export const GoogleSheetWrapper = ({
setOpenAddIntegrationModal={setIsModalOpen} setOpenAddIntegrationModal={setIsModalOpen}
setIsConnected={setIsConnected} setIsConnected={setIsConnected}
setSelectedIntegration={setSelectedIntegration} setSelectedIntegration={setSelectedIntegration}
showReconnectButton={showReconnectButton}
handleGoogleAuthorization={handleGoogleAuthorization}
locale={locale} locale={locale}
/> />
</> </>
@@ -1,6 +1,6 @@
"use client"; "use client";
import { RefreshCcwIcon, Trash2Icon } from "lucide-react"; import { Trash2Icon } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -12,19 +12,15 @@ import { TUserLocale } from "@formbricks/types/user";
import { deleteIntegrationAction } from "@/app/(app)/environments/[environmentId]/workspace/integrations/actions"; import { deleteIntegrationAction } from "@/app/(app)/environments/[environmentId]/workspace/integrations/actions";
import { timeSince } from "@/lib/time"; import { timeSince } from "@/lib/time";
import { getFormattedErrorMessage } from "@/lib/utils/helper"; import { getFormattedErrorMessage } from "@/lib/utils/helper";
import { Alert, AlertButton, AlertDescription } from "@/modules/ui/components/alert";
import { Button } from "@/modules/ui/components/button"; import { Button } from "@/modules/ui/components/button";
import { DeleteDialog } from "@/modules/ui/components/delete-dialog"; import { DeleteDialog } from "@/modules/ui/components/delete-dialog";
import { EmptyState } from "@/modules/ui/components/empty-state"; import { EmptyState } from "@/modules/ui/components/empty-state";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/modules/ui/components/tooltip";
interface ManageIntegrationProps { interface ManageIntegrationProps {
googleSheetIntegration: TIntegrationGoogleSheets; googleSheetIntegration: TIntegrationGoogleSheets;
setOpenAddIntegrationModal: (v: boolean) => void; setOpenAddIntegrationModal: (v: boolean) => void;
setIsConnected: (v: boolean) => void; setIsConnected: (v: boolean) => void;
setSelectedIntegration: (v: (TIntegrationGoogleSheetsConfigData & { index: number }) | null) => void; setSelectedIntegration: (v: (TIntegrationGoogleSheetsConfigData & { index: number }) | null) => void;
showReconnectButton: boolean;
handleGoogleAuthorization: () => void;
locale: TUserLocale; locale: TUserLocale;
} }
@@ -33,8 +29,6 @@ export const ManageIntegration = ({
setOpenAddIntegrationModal, setOpenAddIntegrationModal,
setIsConnected, setIsConnected,
setSelectedIntegration, setSelectedIntegration,
showReconnectButton,
handleGoogleAuthorization,
locale, locale,
}: ManageIntegrationProps) => { }: ManageIntegrationProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -74,17 +68,7 @@ export const ManageIntegration = ({
return ( return (
<div className="mt-6 flex w-full flex-col items-center justify-center p-6"> <div className="mt-6 flex w-full flex-col items-center justify-center p-6">
{showReconnectButton && ( <div className="flex w-full justify-end">
<Alert variant="warning" size="small" className="mb-4 w-full">
<AlertDescription>
{t("environments.integrations.google_sheets.reconnect_button_description")}
</AlertDescription>
<AlertButton onClick={handleGoogleAuthorization}>
{t("environments.integrations.google_sheets.reconnect_button")}
</AlertButton>
</Alert>
)}
<div className="flex w-full justify-end space-x-2">
<div className="mr-6 flex items-center"> <div className="mr-6 flex items-center">
<span className="mr-4 h-4 w-4 rounded-full bg-green-600"></span> <span className="mr-4 h-4 w-4 rounded-full bg-green-600"></span>
<span className="text-slate-500"> <span className="text-slate-500">
@@ -93,19 +77,6 @@ export const ManageIntegration = ({
})} })}
</span> </span>
</div> </div>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="outline" onClick={handleGoogleAuthorization}>
<RefreshCcwIcon className="mr-2 h-4 w-4" />
{t("environments.integrations.google_sheets.reconnect_button")}
</Button>
</TooltipTrigger>
<TooltipContent>
{t("environments.integrations.google_sheets.reconnect_button_tooltip")}
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Button <Button
onClick={() => { onClick={() => {
setSelectedIntegration(null); setSelectedIntegration(null);
@@ -10,7 +10,7 @@ const Loading = () => {
<div className="mt-6 p-6"> <div className="mt-6 p-6">
<GoBackButton /> <GoBackButton />
<div className="mb-6 text-right"> <div className="mb-6 text-right">
<Button className="pointer-events-none animate-pulse cursor-not-allowed select-none bg-slate-200"> <Button className="pointer-events-none animate-pulse cursor-not-allowed bg-slate-200 select-none">
{t("environments.integrations.google_sheets.link_new_sheet")} {t("environments.integrations.google_sheets.link_new_sheet")}
</Button> </Button>
</div> </div>
@@ -51,7 +51,7 @@ const Loading = () => {
<div className="mt-0 h-4 w-24 animate-pulse rounded-full bg-slate-200"></div> <div className="mt-0 h-4 w-24 animate-pulse rounded-full bg-slate-200"></div>
</div> </div>
</div> </div>
<div className="col-span-2 my-auto flex items-center justify-center whitespace-nowrap text-center text-sm text-slate-500"> <div className="col-span-2 my-auto flex items-center justify-center text-center text-sm whitespace-nowrap text-slate-500">
<div className="h-4 w-16 animate-pulse rounded-full bg-slate-200"></div> <div className="h-4 w-16 animate-pulse rounded-full bg-slate-200"></div>
</div> </div>
<div className="text-center"></div> <div className="text-center"></div>
@@ -10,7 +10,7 @@ const Loading = () => {
<div className="mt-6 p-6"> <div className="mt-6 p-6">
<GoBackButton /> <GoBackButton />
<div className="mb-6 text-right"> <div className="mb-6 text-right">
<Button className="pointer-events-none animate-pulse cursor-not-allowed select-none bg-slate-200"> <Button className="pointer-events-none animate-pulse cursor-not-allowed bg-slate-200 select-none">
{t("environments.integrations.notion.link_database")} {t("environments.integrations.notion.link_database")}
</Button> </Button>
</div> </div>
@@ -48,7 +48,7 @@ const Loading = () => {
<div className="mt-0 h-4 w-24 animate-pulse rounded-full bg-slate-200"></div> <div className="mt-0 h-4 w-24 animate-pulse rounded-full bg-slate-200"></div>
</div> </div>
</div> </div>
<div className="col-span-2 my-auto flex items-center justify-center whitespace-nowrap text-center text-sm text-slate-500"> <div className="col-span-2 my-auto flex items-center justify-center text-center text-sm whitespace-nowrap text-slate-500">
<div className="h-4 w-16 animate-pulse rounded-full bg-slate-200"></div> <div className="h-4 w-16 animate-pulse rounded-full bg-slate-200"></div>
</div> </div>
<div className="text-center"></div> <div className="text-center"></div>
@@ -12,7 +12,7 @@ const ZGetSlackChannelsAction = z.object({
}); });
export const getSlackChannelsAction = authenticatedActionClient export const getSlackChannelsAction = authenticatedActionClient
.inputSchema(ZGetSlackChannelsAction) .schema(ZGetSlackChannelsAction)
.action(async ({ ctx, parsedInput }) => { .action(async ({ ctx, parsedInput }) => {
await checkAuthorizationUpdated({ await checkAuthorizationUpdated({
userId: ctx.user.id, userId: ctx.user.id,
+7 -12
View File
@@ -15,7 +15,6 @@ import { getOrganizationByEnvironmentId } from "@/lib/organization/service";
import { getResponseCountBySurveyId } from "@/lib/response/service"; import { getResponseCountBySurveyId } from "@/lib/response/service";
import { getSurvey, updateSurvey } from "@/lib/survey/service"; import { getSurvey, updateSurvey } from "@/lib/survey/service";
import { convertDatesInObject } from "@/lib/time"; import { convertDatesInObject } from "@/lib/time";
import { validateWebhookUrl } from "@/lib/utils/validate-webhook-url";
import { queueAuditEvent } from "@/modules/ee/audit-logs/lib/handler"; import { queueAuditEvent } from "@/modules/ee/audit-logs/lib/handler";
import { TAuditStatus, UNKNOWN_DATA } from "@/modules/ee/audit-logs/types/audit-log"; import { TAuditStatus, UNKNOWN_DATA } from "@/modules/ee/audit-logs/types/audit-log";
import { sendResponseFinishedEmail } from "@/modules/email"; import { sendResponseFinishedEmail } from "@/modules/email";
@@ -136,17 +135,13 @@ export const POST = async (request: Request) => {
); );
} }
return validateWebhookUrl(webhook.url) return fetchWithTimeout(webhook.url, {
.then(() => method: "POST",
fetchWithTimeout(webhook.url, { headers: requestHeaders,
method: "POST", body,
headers: requestHeaders, }).catch((error) => {
body, logger.error({ error, url: request.url }, `Webhook call to ${webhook.url} failed`);
}) });
)
.catch((error) => {
logger.error({ error, url: request.url }, `Webhook call to ${webhook.url} failed`);
});
}); });
if (event === "responseFinished") { if (event === "responseFinished") {
+22 -29
View File
@@ -1,6 +1,5 @@
import { google } from "googleapis"; import { google } from "googleapis";
import { getServerSession } from "next-auth"; import { getServerSession } from "next-auth";
import { TIntegrationGoogleSheetsConfig } from "@formbricks/types/integration/google-sheet";
import { responses } from "@/app/lib/api/response"; import { responses } from "@/app/lib/api/response";
import { import {
GOOGLE_SHEETS_CLIENT_ID, GOOGLE_SHEETS_CLIENT_ID,
@@ -9,7 +8,7 @@ import {
WEBAPP_URL, WEBAPP_URL,
} from "@/lib/constants"; } from "@/lib/constants";
import { hasUserEnvironmentAccess } from "@/lib/environment/auth"; import { hasUserEnvironmentAccess } from "@/lib/environment/auth";
import { createOrUpdateIntegration, getIntegrationByType } from "@/lib/integration/service"; import { createOrUpdateIntegration } from "@/lib/integration/service";
import { authOptions } from "@/modules/auth/lib/authOptions"; import { authOptions } from "@/modules/auth/lib/authOptions";
export const GET = async (req: Request) => { export const GET = async (req: Request) => {
@@ -43,39 +42,33 @@ export const GET = async (req: Request) => {
if (!redirect_uri) return responses.internalServerErrorResponse("Google redirect url is missing"); if (!redirect_uri) return responses.internalServerErrorResponse("Google redirect url is missing");
const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uri); const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uri);
if (!code) { let key;
return Response.redirect( let userEmail;
`${WEBAPP_URL}/environments/${environmentId}/workspace/integrations/google-sheets`
); if (code) {
const token = await oAuth2Client.getToken(code);
key = token.res?.data;
// Set credentials using the provided token
oAuth2Client.setCredentials({
access_token: key.access_token,
});
// Fetch user's email
const oauth2 = google.oauth2({
auth: oAuth2Client,
version: "v2",
});
const userInfo = await oauth2.userinfo.get();
userEmail = userInfo.data.email;
} }
const token = await oAuth2Client.getToken(code);
const key = token.res?.data;
if (!key) {
return Response.redirect(
`${WEBAPP_URL}/environments/${environmentId}/workspace/integrations/google-sheets`
);
}
oAuth2Client.setCredentials({ access_token: key.access_token });
const oauth2 = google.oauth2({ auth: oAuth2Client, version: "v2" });
const userInfo = await oauth2.userinfo.get();
const userEmail = userInfo.data.email;
if (!userEmail) {
return responses.internalServerErrorResponse("Failed to get user email");
}
const integrationType = "googleSheets" as const;
const existingIntegration = await getIntegrationByType(environmentId, integrationType);
const existingConfig = existingIntegration?.config as TIntegrationGoogleSheetsConfig;
const googleSheetIntegration = { const googleSheetIntegration = {
type: integrationType, type: "googleSheets" as "googleSheets",
environment: environmentId, environment: environmentId,
config: { config: {
key, key,
data: existingConfig?.data ?? [], data: [],
email: userEmail, email: userEmail,
}, },
}; };
@@ -50,7 +50,7 @@ export const GET = withV1ApiWrapper({
{ {
environmentId: params.environmentId, environmentId: params.environmentId,
url: req.url, url: req.url,
validationError: cuidValidation.error.issues[0]?.message, validationError: cuidValidation.error.errors[0]?.message,
}, },
"Invalid CUID v1 format detected" "Invalid CUID v1 format detected"
); );
+108 -106
View File
@@ -6,138 +6,140 @@ export const GET = async (req: NextRequest) => {
let brandColor = req.nextUrl.searchParams.get("brandColor"); let brandColor = req.nextUrl.searchParams.get("brandColor");
return new ImageResponse( return new ImageResponse(
<div (
style={{
display: "flex",
flexDirection: "column",
width: "100%",
height: "100%",
alignItems: "center",
backgroundColor: brandColor ? brandColor + "BF" : "#0000BFBF", // /75 opacity is approximately BF in hex
borderRadius: "0.75rem",
}}>
<div <div
style={{ style={{
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
width: "80%", width: "100%",
height: "60%", height: "100%",
backgroundColor: "white",
borderRadius: "0.75rem",
marginTop: "3.25rem",
position: "absolute",
left: "3rem",
top: "0.75rem",
opacity: 0.2,
transform: "rotate(356deg)",
}}></div>
<div
style={{
display: "flex",
flexDirection: "column",
width: "84%",
height: "60%",
backgroundColor: "white",
borderRadius: "0.75rem",
marginTop: "3rem",
position: "absolute",
top: "1.25rem",
left: "3.25rem",
borderWidth: "2px",
opacity: 0.6,
transform: "rotate(357deg)",
}}></div>
<div
style={{
display: "flex",
flexDirection: "column",
width: "85%",
height: "67%",
alignItems: "center", alignItems: "center",
backgroundColor: "white", backgroundColor: brandColor ? brandColor + "BF" : "#0000BFBF", // /75 opacity is approximately BF in hex
borderRadius: "0.75rem", borderRadius: "0.75rem",
marginTop: "2rem",
position: "absolute",
top: "2.3rem",
left: "3.5rem",
transform: "rotate(360deg)",
}}> }}>
<div style={{ display: "flex", flexDirection: "column", width: "100%" }}> <div
<div style={{
style={{ display: "flex",
display: "flex", flexDirection: "column",
flexDirection: "column", width: "80%",
width: "100%", height: "60%",
justifyContent: "space-between", backgroundColor: "white",
}}> borderRadius: "0.75rem",
marginTop: "3.25rem",
position: "absolute",
left: "3rem",
top: "0.75rem",
opacity: 0.2,
transform: "rotate(356deg)",
}}></div>
<div
style={{
display: "flex",
flexDirection: "column",
width: "84%",
height: "60%",
backgroundColor: "white",
borderRadius: "0.75rem",
marginTop: "3rem",
position: "absolute",
top: "1.25rem",
left: "3.25rem",
borderWidth: "2px",
opacity: 0.6,
transform: "rotate(357deg)",
}}></div>
<div
style={{
display: "flex",
flexDirection: "column",
width: "85%",
height: "67%",
alignItems: "center",
backgroundColor: "white",
borderRadius: "0.75rem",
marginTop: "2rem",
position: "absolute",
top: "2.3rem",
left: "3.5rem",
transform: "rotate(360deg)",
}}>
<div style={{ display: "flex", flexDirection: "column", width: "100%" }}>
<div <div
style={{ style={{
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
paddingLeft: "2rem", width: "100%",
paddingRight: "2rem", justifyContent: "space-between",
}}> }}>
<h2 <div
style={{ style={{
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
fontSize: "2rem", paddingLeft: "2rem",
fontWeight: "700", paddingRight: "2rem",
letterSpacing: "-0.025em",
color: "#0f172a",
textAlign: "left",
marginTop: "3.75rem",
}}> }}>
{name} <h2
</h2> style={{
display: "flex",
flexDirection: "column",
fontSize: "2rem",
fontWeight: "700",
letterSpacing: "-0.025em",
color: "#0f172a",
textAlign: "left",
marginTop: "3.75rem",
}}>
{name}
</h2>
</div>
</div> </div>
</div> <div style={{ display: "flex", justifyContent: "flex-end", marginRight: "2.5rem" }}>
<div style={{ display: "flex", justifyContent: "flex-end", marginRight: "2.5rem" }}>
<div
style={{
display: "flex",
borderRadius: "1rem",
position: "absolute",
right: "-0.5rem",
marginTop: "0.5rem",
}}>
<div
content=""
style={{
borderRadius: "0.75rem",
border: "1px solid transparent",
backgroundColor: brandColor ?? "#000",
height: "4.5rem",
width: "9.5rem",
opacity: 0.5,
}}></div>
</div>
<div
style={{
display: "flex",
borderRadius: "1rem",
boxShadow: "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)",
}}>
<div <div
style={{ style={{
display: "flex", display: "flex",
alignItems: "center", borderRadius: "1rem",
justifyContent: "center", position: "absolute",
borderRadius: "0.75rem", right: "-0.5rem",
border: "1px solid transparent", marginTop: "0.5rem",
backgroundColor: brandColor ?? "#000",
fontSize: "1.5rem",
color: "white",
height: "4.5rem",
width: "9.5rem",
}}> }}>
Begin! <div
content=""
style={{
borderRadius: "0.75rem",
border: "1px solid transparent",
backgroundColor: brandColor ?? "#000",
height: "4.5rem",
width: "9.5rem",
opacity: 0.5,
}}></div>
</div>
<div
style={{
display: "flex",
borderRadius: "1rem",
boxShadow: "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)",
}}>
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
borderRadius: "0.75rem",
border: "1px solid transparent",
backgroundColor: brandColor ?? "#000",
fontSize: "1.5rem",
color: "white",
height: "4.5rem",
width: "9.5rem",
}}>
Begin!
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div>, ),
{ {
width: 800, width: 800,
height: 400, height: 400,
@@ -6,7 +6,7 @@ import {
} from "@formbricks/types/integration/slack"; } from "@formbricks/types/integration/slack";
import { responses } from "@/app/lib/api/response"; import { responses } from "@/app/lib/api/response";
import { TSessionAuthentication, withV1ApiWrapper } from "@/app/lib/api/with-api-logging"; import { TSessionAuthentication, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
import { SLACK_CLIENT_ID, SLACK_CLIENT_SECRET, SLACK_REDIRECT_URI, WEBAPP_URL } from "@/lib/constants"; import { SLACK_CLIENT_ID, SLACK_CLIENT_SECRET, WEBAPP_URL } from "@/lib/constants";
import { hasUserEnvironmentAccess } from "@/lib/environment/auth"; import { hasUserEnvironmentAccess } from "@/lib/environment/auth";
import { createOrUpdateIntegration, getIntegrationByType } from "@/lib/integration/service"; import { createOrUpdateIntegration, getIntegrationByType } from "@/lib/integration/service";
@@ -56,7 +56,6 @@ export const GET = withV1ApiWrapper({
code, code,
client_id: SLACK_CLIENT_ID, client_id: SLACK_CLIENT_ID,
client_secret: SLACK_CLIENT_SECRET, client_secret: SLACK_CLIENT_SECRET,
redirect_uri: SLACK_REDIRECT_URI,
}; };
const formBody: string[] = []; const formBody: string[] = [];
for (const property in formData) { for (const property in formData) {
@@ -6,7 +6,7 @@ import { DatabaseError } from "@formbricks/types/errors";
import { validateInputs } from "@/lib/utils/validate"; import { validateInputs } from "@/lib/utils/validate";
export const deleteSurvey = async (surveyId: string) => { export const deleteSurvey = async (surveyId: string) => {
validateInputs([surveyId, z.cuid2()]); validateInputs([surveyId, z.string().cuid2()]);
try { try {
const deletedSurvey = await prisma.survey.delete({ const deletedSurvey = await prisma.survey.delete({
@@ -2,11 +2,10 @@ import { Prisma, WebhookSource } from "@prisma/client";
import { cleanup } from "@testing-library/react"; import { cleanup } from "@testing-library/react";
import { afterEach, describe, expect, test, vi } from "vitest"; import { afterEach, describe, expect, test, vi } from "vitest";
import { prisma } from "@formbricks/database"; import { prisma } from "@formbricks/database";
import { DatabaseError, InvalidInputError, ValidationError } from "@formbricks/types/errors"; import { DatabaseError, ValidationError } from "@formbricks/types/errors";
import { createWebhook } from "@/app/api/v1/webhooks/lib/webhook"; import { createWebhook } from "@/app/api/v1/webhooks/lib/webhook";
import { TWebhookInput } from "@/app/api/v1/webhooks/types/webhooks"; import { TWebhookInput } from "@/app/api/v1/webhooks/types/webhooks";
import { validateInputs } from "@/lib/utils/validate"; import { validateInputs } from "@/lib/utils/validate";
import { validateWebhookUrl } from "@/lib/utils/validate-webhook-url";
vi.mock("@formbricks/database", () => ({ vi.mock("@formbricks/database", () => ({
prisma: { prisma: {
@@ -24,10 +23,6 @@ vi.mock("@/lib/crypto", () => ({
generateWebhookSecret: vi.fn(() => "whsec_test_secret_1234567890"), generateWebhookSecret: vi.fn(() => "whsec_test_secret_1234567890"),
})); }));
vi.mock("@/lib/utils/validate-webhook-url", () => ({
validateWebhookUrl: vi.fn().mockResolvedValue(undefined),
}));
describe("createWebhook", () => { describe("createWebhook", () => {
afterEach(() => { afterEach(() => {
cleanup(); cleanup();
@@ -80,41 +75,6 @@ describe("createWebhook", () => {
expect(result).toEqual(createdWebhook); expect(result).toEqual(createdWebhook);
}); });
test("should call validateWebhookUrl with the provided URL", async () => {
const webhookInput: TWebhookInput = {
environmentId: "test-env-id",
name: "Test Webhook",
url: "https://example.com",
source: "user",
triggers: ["responseCreated"],
surveyIds: ["survey1"],
};
vi.mocked(prisma.webhook.create).mockResolvedValueOnce({} as any);
await createWebhook(webhookInput);
expect(validateWebhookUrl).toHaveBeenCalledWith("https://example.com");
});
test("should throw InvalidInputError and skip Prisma create when URL fails SSRF validation", async () => {
const webhookInput: TWebhookInput = {
environmentId: "test-env-id",
name: "Test Webhook",
url: "http://169.254.169.254/latest/meta-data/",
source: "user",
triggers: ["responseCreated"],
surveyIds: ["survey1"],
};
vi.mocked(validateWebhookUrl).mockRejectedValueOnce(
new InvalidInputError("Webhook URL must not point to private or internal IP addresses")
);
await expect(createWebhook(webhookInput)).rejects.toThrow(InvalidInputError);
expect(prisma.webhook.create).not.toHaveBeenCalled();
});
test("should throw a ValidationError if the input data does not match the ZWebhookInput schema", async () => { test("should throw a ValidationError if the input data does not match the ZWebhookInput schema", async () => {
const invalidWebhookInput = { const invalidWebhookInput = {
environmentId: "test-env-id", environmentId: "test-env-id",
@@ -6,11 +6,9 @@ import { TWebhookInput, ZWebhookInput } from "@/app/api/v1/webhooks/types/webhoo
import { ITEMS_PER_PAGE } from "@/lib/constants"; import { ITEMS_PER_PAGE } from "@/lib/constants";
import { generateWebhookSecret } from "@/lib/crypto"; import { generateWebhookSecret } from "@/lib/crypto";
import { validateInputs } from "@/lib/utils/validate"; import { validateInputs } from "@/lib/utils/validate";
import { validateWebhookUrl } from "@/lib/utils/validate-webhook-url";
export const createWebhook = async (webhookInput: TWebhookInput): Promise<Webhook> => { export const createWebhook = async (webhookInput: TWebhookInput): Promise<Webhook> => {
validateInputs([webhookInput, ZWebhookInput]); validateInputs([webhookInput, ZWebhookInput]);
await validateWebhookUrl(webhookInput.url);
try { try {
const secret = generateWebhookSecret(); const secret = generateWebhookSecret();
@@ -101,9 +101,7 @@ describe("verifyRecaptchaToken", () => {
}, },
signal: {}, signal: {},
}; };
vi.spyOn(global, "AbortController").mockImplementation(function AbortController() { vi.spyOn(global, "AbortController").mockImplementation(() => abortController as any);
return abortController as any;
});
(global.fetch as any).mockImplementation(() => new Promise(() => {})); (global.fetch as any).mockImplementation(() => new Promise(() => {}));
verifyRecaptchaToken("token", 0.5); verifyRecaptchaToken("token", 0.5);
vi.advanceTimersByTime(5000); vi.advanceTimersByTime(5000);
+45 -30
View File
@@ -131,11 +131,13 @@ describe("withV1ApiWrapper", () => {
}); });
test("logs and audits on error response with API key authentication", async () => { test("logs and audits on error response with API key authentication", async () => {
const { queueAuditEvent: mockedQueueAuditEvent } = const { queueAuditEvent: mockedQueueAuditEvent } = (await import(
(await import("@/modules/ee/audit-logs/lib/handler")) as unknown as { queueAuditEvent: Mock }; "@/modules/ee/audit-logs/lib/handler"
)) as unknown as { queueAuditEvent: Mock };
const { authenticateRequest } = await import("@/app/api/v1/auth"); const { authenticateRequest } = await import("@/app/api/v1/auth");
const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = await import(
await import("@/app/middleware/endpoint-validator"); "@/app/middleware/endpoint-validator"
);
vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication); vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication);
vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true }); vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true });
@@ -183,11 +185,13 @@ describe("withV1ApiWrapper", () => {
}); });
test("does not log Sentry if not 500", async () => { test("does not log Sentry if not 500", async () => {
const { queueAuditEvent: mockedQueueAuditEvent } = const { queueAuditEvent: mockedQueueAuditEvent } = (await import(
(await import("@/modules/ee/audit-logs/lib/handler")) as unknown as { queueAuditEvent: Mock }; "@/modules/ee/audit-logs/lib/handler"
)) as unknown as { queueAuditEvent: Mock };
const { authenticateRequest } = await import("@/app/api/v1/auth"); const { authenticateRequest } = await import("@/app/api/v1/auth");
const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = await import(
await import("@/app/middleware/endpoint-validator"); "@/app/middleware/endpoint-validator"
);
vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication); vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication);
vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true }); vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true });
@@ -229,11 +233,13 @@ describe("withV1ApiWrapper", () => {
}); });
test("logs and audits on thrown error", async () => { test("logs and audits on thrown error", async () => {
const { queueAuditEvent: mockedQueueAuditEvent } = const { queueAuditEvent: mockedQueueAuditEvent } = (await import(
(await import("@/modules/ee/audit-logs/lib/handler")) as unknown as { queueAuditEvent: Mock }; "@/modules/ee/audit-logs/lib/handler"
)) as unknown as { queueAuditEvent: Mock };
const { authenticateRequest } = await import("@/app/api/v1/auth"); const { authenticateRequest } = await import("@/app/api/v1/auth");
const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = await import(
await import("@/app/middleware/endpoint-validator"); "@/app/middleware/endpoint-validator"
);
vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication); vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication);
vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true }); vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true });
@@ -285,11 +291,13 @@ describe("withV1ApiWrapper", () => {
}); });
test("does not log on success response but still audits", async () => { test("does not log on success response but still audits", async () => {
const { queueAuditEvent: mockedQueueAuditEvent } = const { queueAuditEvent: mockedQueueAuditEvent } = (await import(
(await import("@/modules/ee/audit-logs/lib/handler")) as unknown as { queueAuditEvent: Mock }; "@/modules/ee/audit-logs/lib/handler"
)) as unknown as { queueAuditEvent: Mock };
const { authenticateRequest } = await import("@/app/api/v1/auth"); const { authenticateRequest } = await import("@/app/api/v1/auth");
const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = await import(
await import("@/app/middleware/endpoint-validator"); "@/app/middleware/endpoint-validator"
);
vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication); vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication);
vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true }); vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true });
@@ -339,11 +347,13 @@ describe("withV1ApiWrapper", () => {
REDIS_URL: "redis://localhost:6379", REDIS_URL: "redis://localhost:6379",
})); }));
const { queueAuditEvent: mockedQueueAuditEvent } = const { queueAuditEvent: mockedQueueAuditEvent } = (await import(
(await import("@/modules/ee/audit-logs/lib/handler")) as unknown as { queueAuditEvent: Mock }; "@/modules/ee/audit-logs/lib/handler"
)) as unknown as { queueAuditEvent: Mock };
const { authenticateRequest } = await import("@/app/api/v1/auth"); const { authenticateRequest } = await import("@/app/api/v1/auth");
const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = await import(
await import("@/app/middleware/endpoint-validator"); "@/app/middleware/endpoint-validator"
);
const { withV1ApiWrapper } = await import("./with-api-logging"); const { withV1ApiWrapper } = await import("./with-api-logging");
vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication); vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication);
@@ -366,8 +376,9 @@ describe("withV1ApiWrapper", () => {
}); });
test("handles client-side API routes without authentication", async () => { test("handles client-side API routes without authentication", async () => {
const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = await import(
await import("@/app/middleware/endpoint-validator"); "@/app/middleware/endpoint-validator"
);
const { authenticateRequest } = await import("@/app/api/v1/auth"); const { authenticateRequest } = await import("@/app/api/v1/auth");
const { applyIPRateLimit } = await import("@/modules/core/rate-limit/helpers"); const { applyIPRateLimit } = await import("@/modules/core/rate-limit/helpers");
@@ -399,8 +410,9 @@ describe("withV1ApiWrapper", () => {
}); });
test("returns authentication error for non-client routes without auth", async () => { test("returns authentication error for non-client routes without auth", async () => {
const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = await import(
await import("@/app/middleware/endpoint-validator"); "@/app/middleware/endpoint-validator"
);
const { authenticateRequest } = await import("@/app/api/v1/auth"); const { authenticateRequest } = await import("@/app/api/v1/auth");
vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true }); vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true });
@@ -423,8 +435,9 @@ describe("withV1ApiWrapper", () => {
test("handles rate limiting errors", async () => { test("handles rate limiting errors", async () => {
const { applyRateLimit } = await import("@/modules/core/rate-limit/helpers"); const { applyRateLimit } = await import("@/modules/core/rate-limit/helpers");
const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = await import(
await import("@/app/middleware/endpoint-validator"); "@/app/middleware/endpoint-validator"
);
const { authenticateRequest } = await import("@/app/api/v1/auth"); const { authenticateRequest } = await import("@/app/api/v1/auth");
vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication); vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication);
@@ -449,11 +462,13 @@ describe("withV1ApiWrapper", () => {
}); });
test("skips audit log creation when no action/targetType provided", async () => { test("skips audit log creation when no action/targetType provided", async () => {
const { queueAuditEvent: mockedQueueAuditEvent } = const { queueAuditEvent: mockedQueueAuditEvent } = (await import(
(await import("@/modules/ee/audit-logs/lib/handler")) as unknown as { queueAuditEvent: Mock }; "@/modules/ee/audit-logs/lib/handler"
)) as unknown as { queueAuditEvent: Mock };
const { authenticateRequest } = await import("@/app/api/v1/auth"); const { authenticateRequest } = await import("@/app/api/v1/auth");
const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = await import(
await import("@/app/middleware/endpoint-validator"); "@/app/middleware/endpoint-validator"
);
vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication); vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication);
vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true }); vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true });
+5 -1
View File
@@ -1,4 +1,4 @@
import * as cuid2 from "@paralleldrive/cuid2"; import cuid2 from "@paralleldrive/cuid2";
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"; import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
import * as crypto from "@/lib/crypto"; import * as crypto from "@/lib/crypto";
import { generateSurveySingleUseId, validateSurveySingleUseId } from "./singleUseSurveys"; import { generateSurveySingleUseId, validateSurveySingleUseId } from "./singleUseSurveys";
@@ -20,6 +20,10 @@ vi.mock("@paralleldrive/cuid2", () => {
const isCuidMock = vi.fn(); const isCuidMock = vi.fn();
return { return {
default: {
createId: createIdMock,
isCuid: isCuidMock,
},
createId: createIdMock, createId: createIdMock,
isCuid: isCuidMock, isCuid: isCuidMock,
}; };
+3 -3
View File
@@ -1,10 +1,10 @@
import { createId, isCuid } from "@paralleldrive/cuid2"; import cuid2 from "@paralleldrive/cuid2";
import { ENCRYPTION_KEY } from "@/lib/constants"; import { ENCRYPTION_KEY } from "@/lib/constants";
import { symmetricDecrypt, symmetricEncrypt } from "@/lib/crypto"; import { symmetricDecrypt, symmetricEncrypt } from "@/lib/crypto";
// generate encrypted single use id for the survey // generate encrypted single use id for the survey
export const generateSurveySingleUseId = (isEncrypted: boolean): string => { export const generateSurveySingleUseId = (isEncrypted: boolean): string => {
const cuid = createId(); const cuid = cuid2.createId();
if (!isEncrypted) { if (!isEncrypted) {
return cuid; return cuid;
} }
@@ -30,7 +30,7 @@ export const validateSurveySingleUseId = (surveySingleUseId: string): string | u
return undefined; return undefined;
} }
if (isCuid(decryptedCuid)) { if (cuid2.isCuid(decryptedCuid)) {
return decryptedCuid; return decryptedCuid;
} else { } else {
return undefined; return undefined;
File diff suppressed because it is too large Load Diff
@@ -257,7 +257,6 @@ describe("endpoint-validator", () => {
expect(isAuthProtectedRoute("/api/v1/client/test")).toBe(false); expect(isAuthProtectedRoute("/api/v1/client/test")).toBe(false);
expect(isAuthProtectedRoute("/")).toBe(false); expect(isAuthProtectedRoute("/")).toBe(false);
expect(isAuthProtectedRoute("/s/survey123")).toBe(false); expect(isAuthProtectedRoute("/s/survey123")).toBe(false);
expect(isAuthProtectedRoute("/p/pretty-url")).toBe(false);
expect(isAuthProtectedRoute("/c/jwt-token")).toBe(false); expect(isAuthProtectedRoute("/c/jwt-token")).toBe(false);
expect(isAuthProtectedRoute("/health")).toBe(false); expect(isAuthProtectedRoute("/health")).toBe(false);
}); });
@@ -314,19 +313,6 @@ describe("endpoint-validator", () => {
expect(isPublicDomainRoute("/contact/token")).toBe(false); expect(isPublicDomainRoute("/contact/token")).toBe(false);
}); });
test("should return true for pretty URL survey routes", () => {
expect(isPublicDomainRoute("/p/pretty123")).toBe(true);
expect(isPublicDomainRoute("/p/pretty-name-with-dashes")).toBe(true);
expect(isPublicDomainRoute("/p/survey_id_with_underscores")).toBe(true);
expect(isPublicDomainRoute("/p/abc123def456")).toBe(true);
});
test("should return false for malformed pretty URL survey routes", () => {
expect(isPublicDomainRoute("/p/")).toBe(false);
expect(isPublicDomainRoute("/p")).toBe(false);
expect(isPublicDomainRoute("/pretty/123")).toBe(false);
});
test("should return true for client API routes", () => { test("should return true for client API routes", () => {
expect(isPublicDomainRoute("/api/v1/client/something")).toBe(true); expect(isPublicDomainRoute("/api/v1/client/something")).toBe(true);
expect(isPublicDomainRoute("/api/v2/client/other")).toBe(true); expect(isPublicDomainRoute("/api/v2/client/other")).toBe(true);
@@ -389,8 +375,6 @@ describe("endpoint-validator", () => {
expect(isAdminDomainRoute("/s/survey-id-with-dashes")).toBe(false); expect(isAdminDomainRoute("/s/survey-id-with-dashes")).toBe(false);
expect(isAdminDomainRoute("/c/jwt-token")).toBe(false); expect(isAdminDomainRoute("/c/jwt-token")).toBe(false);
expect(isAdminDomainRoute("/c/very-long-jwt-token-123")).toBe(false); expect(isAdminDomainRoute("/c/very-long-jwt-token-123")).toBe(false);
expect(isAdminDomainRoute("/p/pretty123")).toBe(false);
expect(isAdminDomainRoute("/p/pretty-name-with-dashes")).toBe(false);
expect(isAdminDomainRoute("/api/v1/client/test")).toBe(false); expect(isAdminDomainRoute("/api/v1/client/test")).toBe(false);
expect(isAdminDomainRoute("/api/v2/client/other")).toBe(false); expect(isAdminDomainRoute("/api/v2/client/other")).toBe(false);
}); });
@@ -406,7 +390,6 @@ describe("endpoint-validator", () => {
test("should allow public routes on public domain", () => { test("should allow public routes on public domain", () => {
expect(isRouteAllowedForDomain("/s/survey123", true)).toBe(true); expect(isRouteAllowedForDomain("/s/survey123", true)).toBe(true);
expect(isRouteAllowedForDomain("/c/jwt-token", true)).toBe(true); expect(isRouteAllowedForDomain("/c/jwt-token", true)).toBe(true);
expect(isRouteAllowedForDomain("/p/pretty123", true)).toBe(true);
expect(isRouteAllowedForDomain("/api/v1/client/test", true)).toBe(true); expect(isRouteAllowedForDomain("/api/v1/client/test", true)).toBe(true);
expect(isRouteAllowedForDomain("/api/v2/client/other", true)).toBe(true); expect(isRouteAllowedForDomain("/api/v2/client/other", true)).toBe(true);
expect(isRouteAllowedForDomain("/health", true)).toBe(true); expect(isRouteAllowedForDomain("/health", true)).toBe(true);
@@ -443,8 +426,6 @@ describe("endpoint-validator", () => {
expect(isRouteAllowedForDomain("/s/survey-id-with-dashes", false)).toBe(false); expect(isRouteAllowedForDomain("/s/survey-id-with-dashes", false)).toBe(false);
expect(isRouteAllowedForDomain("/c/jwt-token", false)).toBe(false); expect(isRouteAllowedForDomain("/c/jwt-token", false)).toBe(false);
expect(isRouteAllowedForDomain("/c/very-long-jwt-token-123", false)).toBe(false); expect(isRouteAllowedForDomain("/c/very-long-jwt-token-123", false)).toBe(false);
expect(isRouteAllowedForDomain("/p/pretty123", false)).toBe(false);
expect(isRouteAllowedForDomain("/p/pretty-name-with-dashes", false)).toBe(false);
expect(isRouteAllowedForDomain("/api/v1/client/test", false)).toBe(false); expect(isRouteAllowedForDomain("/api/v1/client/test", false)).toBe(false);
expect(isRouteAllowedForDomain("/api/v2/client/other", false)).toBe(false); expect(isRouteAllowedForDomain("/api/v2/client/other", false)).toBe(false);
}); });
@@ -459,8 +440,6 @@ describe("endpoint-validator", () => {
test("should handle paths with query parameters and fragments", () => { test("should handle paths with query parameters and fragments", () => {
expect(isRouteAllowedForDomain("/s/survey123?param=value", true)).toBe(true); expect(isRouteAllowedForDomain("/s/survey123?param=value", true)).toBe(true);
expect(isRouteAllowedForDomain("/s/survey123#section", true)).toBe(true); expect(isRouteAllowedForDomain("/s/survey123#section", true)).toBe(true);
expect(isRouteAllowedForDomain("/p/pretty123?param=value", true)).toBe(true);
expect(isRouteAllowedForDomain("/p/pretty123#section", true)).toBe(true);
expect(isRouteAllowedForDomain("/environments/123?tab=settings", true)).toBe(false); expect(isRouteAllowedForDomain("/environments/123?tab=settings", true)).toBe(false);
expect(isRouteAllowedForDomain("/environments/123?tab=settings", false)).toBe(true); expect(isRouteAllowedForDomain("/environments/123?tab=settings", false)).toBe(true);
}); });
@@ -471,7 +450,6 @@ describe("endpoint-validator", () => {
describe("URL parsing edge cases", () => { describe("URL parsing edge cases", () => {
test("should handle paths with query parameters", () => { test("should handle paths with query parameters", () => {
expect(isPublicDomainRoute("/s/survey123?param=value&other=test")).toBe(true); expect(isPublicDomainRoute("/s/survey123?param=value&other=test")).toBe(true);
expect(isPublicDomainRoute("/p/pretty123?param=value&other=test")).toBe(true);
expect(isPublicDomainRoute("/api/v1/client/test?query=data")).toBe(true); expect(isPublicDomainRoute("/api/v1/client/test?query=data")).toBe(true);
expect(isPublicDomainRoute("/environments/123?tab=settings")).toBe(false); expect(isPublicDomainRoute("/environments/123?tab=settings")).toBe(false);
expect(isAuthProtectedRoute("/environments/123?tab=overview")).toBe(true); expect(isAuthProtectedRoute("/environments/123?tab=overview")).toBe(true);
@@ -480,14 +458,12 @@ describe("endpoint-validator", () => {
test("should handle paths with fragments", () => { test("should handle paths with fragments", () => {
expect(isPublicDomainRoute("/s/survey123#section")).toBe(true); expect(isPublicDomainRoute("/s/survey123#section")).toBe(true);
expect(isPublicDomainRoute("/c/jwt-token#top")).toBe(true); expect(isPublicDomainRoute("/c/jwt-token#top")).toBe(true);
expect(isPublicDomainRoute("/p/pretty123#section")).toBe(true);
expect(isPublicDomainRoute("/environments/123#overview")).toBe(false); expect(isPublicDomainRoute("/environments/123#overview")).toBe(false);
expect(isAuthProtectedRoute("/organizations/456#settings")).toBe(true); expect(isAuthProtectedRoute("/organizations/456#settings")).toBe(true);
}); });
test("should handle trailing slashes", () => { test("should handle trailing slashes", () => {
expect(isPublicDomainRoute("/s/survey123/")).toBe(true); expect(isPublicDomainRoute("/s/survey123/")).toBe(true);
expect(isPublicDomainRoute("/p/pretty123/")).toBe(true);
expect(isPublicDomainRoute("/api/v1/client/test/")).toBe(true); expect(isPublicDomainRoute("/api/v1/client/test/")).toBe(true);
expect(isManagementApiRoute("/api/v1/management/test/")).toEqual({ expect(isManagementApiRoute("/api/v1/management/test/")).toEqual({
isManagementApi: true, isManagementApi: true,
@@ -502,9 +478,6 @@ describe("endpoint-validator", () => {
expect(isPublicDomainRoute("/s/survey123/preview")).toBe(true); expect(isPublicDomainRoute("/s/survey123/preview")).toBe(true);
expect(isPublicDomainRoute("/s/survey123/embed")).toBe(true); expect(isPublicDomainRoute("/s/survey123/embed")).toBe(true);
expect(isPublicDomainRoute("/s/survey123/thank-you")).toBe(true); expect(isPublicDomainRoute("/s/survey123/thank-you")).toBe(true);
expect(isPublicDomainRoute("/p/pretty123/preview")).toBe(true);
expect(isPublicDomainRoute("/p/pretty123/embed")).toBe(true);
expect(isPublicDomainRoute("/p/pretty123/thank-you")).toBe(true);
}); });
test("should handle nested client API routes", () => { test("should handle nested client API routes", () => {
@@ -556,7 +529,6 @@ describe("endpoint-validator", () => {
test("should handle special characters in survey IDs", () => { test("should handle special characters in survey IDs", () => {
expect(isPublicDomainRoute("/s/survey-123_test.v2")).toBe(true); expect(isPublicDomainRoute("/s/survey-123_test.v2")).toBe(true);
expect(isPublicDomainRoute("/c/jwt.token.with.dots")).toBe(true); expect(isPublicDomainRoute("/c/jwt.token.with.dots")).toBe(true);
expect(isPublicDomainRoute("/p/pretty-123_test.v2")).toBe(true);
}); });
}); });
@@ -564,7 +536,6 @@ describe("endpoint-validator", () => {
test("should properly validate malicious or injection-like URLs", () => { test("should properly validate malicious or injection-like URLs", () => {
// SQL injection-like attempts // SQL injection-like attempts
expect(isPublicDomainRoute("/s/'; DROP TABLE users; --")).toBe(true); // Still valid survey ID format expect(isPublicDomainRoute("/s/'; DROP TABLE users; --")).toBe(true); // Still valid survey ID format
expect(isPublicDomainRoute("/p/'; DROP TABLE users; --")).toBe(true);
expect(isManagementApiRoute("/api/v1/management/'; DROP TABLE users; --")).toEqual({ expect(isManagementApiRoute("/api/v1/management/'; DROP TABLE users; --")).toEqual({
isManagementApi: true, isManagementApi: true,
authenticationMethod: AuthenticationMethod.ApiKey, authenticationMethod: AuthenticationMethod.ApiKey,
@@ -572,12 +543,10 @@ describe("endpoint-validator", () => {
// Path traversal attempts // Path traversal attempts
expect(isPublicDomainRoute("/s/../../../etc/passwd")).toBe(true); // Still matches pattern expect(isPublicDomainRoute("/s/../../../etc/passwd")).toBe(true); // Still matches pattern
expect(isPublicDomainRoute("/p/../../../etc/passwd")).toBe(true);
expect(isAuthProtectedRoute("/environments/../../../etc/passwd")).toBe(true); expect(isAuthProtectedRoute("/environments/../../../etc/passwd")).toBe(true);
// XSS-like attempts // XSS-like attempts
expect(isPublicDomainRoute("/s/<script>alert('xss')</script>")).toBe(true); expect(isPublicDomainRoute("/s/<script>alert('xss')</script>")).toBe(true);
expect(isPublicDomainRoute("/p/<script>alert('xss')</script>")).toBe(true);
expect(isClientSideApiRoute("/api/v1/client/<script>alert('xss')</script>")).toEqual({ expect(isClientSideApiRoute("/api/v1/client/<script>alert('xss')</script>")).toEqual({
isClientSideApi: true, isClientSideApi: true,
isRateLimited: true, isRateLimited: true,
@@ -587,7 +556,6 @@ describe("endpoint-validator", () => {
test("should handle URL encoding", () => { test("should handle URL encoding", () => {
expect(isPublicDomainRoute("/s/survey%20123")).toBe(true); expect(isPublicDomainRoute("/s/survey%20123")).toBe(true);
expect(isPublicDomainRoute("/c/jwt%2Etoken")).toBe(true); expect(isPublicDomainRoute("/c/jwt%2Etoken")).toBe(true);
expect(isPublicDomainRoute("/p/pretty%20123")).toBe(true);
expect(isAuthProtectedRoute("/environments%2F123")).toBe(true); expect(isAuthProtectedRoute("/environments%2F123")).toBe(true);
expect(isManagementApiRoute("/api/v1/management/test%20route")).toEqual({ expect(isManagementApiRoute("/api/v1/management/test%20route")).toEqual({
isManagementApi: true, isManagementApi: true,
@@ -623,7 +591,6 @@ describe("endpoint-validator", () => {
// These should not match due to case sensitivity // These should not match due to case sensitivity
expect(isPublicDomainRoute("/S/survey123")).toBe(false); expect(isPublicDomainRoute("/S/survey123")).toBe(false);
expect(isPublicDomainRoute("/C/jwt-token")).toBe(false); expect(isPublicDomainRoute("/C/jwt-token")).toBe(false);
expect(isPublicDomainRoute("/P/pretty123")).toBe(false);
expect(isClientSideApiRoute("/API/V1/CLIENT/test")).toEqual({ expect(isClientSideApiRoute("/API/V1/CLIENT/test")).toEqual({
isClientSideApi: false, isClientSideApi: false,
isRateLimited: true, isRateLimited: true,
-1
View File
@@ -7,7 +7,6 @@ const PUBLIC_ROUTES = {
SURVEY_ROUTES: [ SURVEY_ROUTES: [
/^\/s\/[^/]+/, // /s/[surveyId] - survey pages /^\/s\/[^/]+/, // /s/[surveyId] - survey pages
/^\/c\/[^/]+/, // /c/[jwt] - contact survey pages /^\/c\/[^/]+/, // /c/[jwt] - contact survey pages
/^\/p\/[^/]+/, // /p/[prettyUrl] - pretty URL pages
], ],
// API routes accessible from public domain // API routes accessible from public domain
@@ -14,39 +14,31 @@ const ZCreateOrganizationAction = z.object({
organizationName: z.string(), organizationName: z.string(),
}); });
export const createOrganizationAction = authenticatedActionClient export const createOrganizationAction = authenticatedActionClient.schema(ZCreateOrganizationAction).action(
.inputSchema(ZCreateOrganizationAction) withAuditLogging(
.action( "created",
withAuditLogging( "organization",
"created", async ({ ctx, parsedInput }: { ctx: AuthenticatedActionClientCtx; parsedInput: Record<string, any> }) => {
"organization", const hasNoOrganizations = await gethasNoOrganizations();
async ({ const isMultiOrgEnabled = await getIsMultiOrgEnabled();
ctx,
parsedInput,
}: {
ctx: AuthenticatedActionClientCtx;
parsedInput: Record<string, any>;
}) => {
const hasNoOrganizations = await gethasNoOrganizations();
const isMultiOrgEnabled = await getIsMultiOrgEnabled();
if (!hasNoOrganizations && !isMultiOrgEnabled) { if (!hasNoOrganizations && !isMultiOrgEnabled) {
throw new OperationNotAllowedError("This action can only be performed on a fresh instance."); throw new OperationNotAllowedError("This action can only be performed on a fresh instance.");
}
const newOrganization = await createOrganization({
name: parsedInput.organizationName,
});
await createMembership(newOrganization.id, ctx.user.id, {
role: "owner",
accepted: true,
});
ctx.auditLoggingCtx.organizationId = newOrganization.id;
ctx.auditLoggingCtx.newObject = newOrganization;
return newOrganization;
} }
)
); const newOrganization = await createOrganization({
name: parsedInput.organizationName,
});
await createMembership(newOrganization.id, ctx.user.id, {
role: "owner",
accepted: true,
});
ctx.auditLoggingCtx.organizationId = newOrganization.id;
ctx.auditLoggingCtx.newObject = newOrganization;
return newOrganization;
}
)
);
+33 -85
View File
@@ -148,12 +148,9 @@ checksums:
common/copy: 627c00d2c850b9b45f7341a6ac01b6bb common/copy: 627c00d2c850b9b45f7341a6ac01b6bb
common/copy_code: 704c13d9bc01caad29a1cf3179baa111 common/copy_code: 704c13d9bc01caad29a1cf3179baa111
common/copy_link: 57a37acfe6d7ed71d00fbbc8079fbb35 common/copy_link: 57a37acfe6d7ed71d00fbbc8079fbb35
common/count_attributes: 48805e836a9b50f9635ad00fed953058 common/count_attributes: 042fba9baffef5afe2c24f13d4f50697
common/count_contacts: 9f71d503455264f1eec1ae58894cf143 common/count_contacts: b1c413a4b06961b71b6aeee95d6775d7
common/count_members: 31ce64ca63fdf95e02ab5543b6e2f717 common/count_responses: 690118a456c01c5b4d437ae82b50b131
common/count_questions: a7a34376a01eda781381fe7544541293
common/count_responses: 437e022825c7a08481d8f7e56926742d
common/count_selections: a1ec41682b9a7d8601c3905dfba34e16
common/create_new_organization: 51dae7b33143686ee218abf5bea764a5 common/create_new_organization: 51dae7b33143686ee218abf5bea764a5
common/create_segment: 9d8291cd4d778b53b73bbc84fd91c181 common/create_segment: 9d8291cd4d778b53b73bbc84fd91c181
common/create_survey: 1cfbba08d34876566d84b2960054a987 common/create_survey: 1cfbba08d34876566d84b2960054a987
@@ -167,7 +164,6 @@ checksums:
common/days: c95fe8aedde21a0b5653dbd0b3c58b48 common/days: c95fe8aedde21a0b5653dbd0b3c58b48
common/default: d9c6dc5c412fe94143dfd1d332ec81d4 common/default: d9c6dc5c412fe94143dfd1d332ec81d4
common/delete: 8bcf303dd10a645b5baacb02b47d72c9 common/delete: 8bcf303dd10a645b5baacb02b47d72c9
common/delete_what: 718ddfcc1dec7f3e8b67856fba838267
common/description: e17686a22ffad04cc7bb70524ed4478b common/description: e17686a22ffad04cc7bb70524ed4478b
common/dev_env: e650911d5e19ba256358e0cda154c005 common/dev_env: e650911d5e19ba256358e0cda154c005
common/development: 85211dbb918bda7a6e87649dcfc1b17a common/development: 85211dbb918bda7a6e87649dcfc1b17a
@@ -183,8 +179,6 @@ checksums:
common/download: 56b7d0834952b39ee394b44bd8179178 common/download: 56b7d0834952b39ee394b44bd8179178
common/draft: e8a92958ad300aacfe46c2bf6644927e common/draft: e8a92958ad300aacfe46c2bf6644927e
common/duplicate: 27756566785c2b8463e21582c4bb619b common/duplicate: 27756566785c2b8463e21582c4bb619b
common/duplicate_copy: 68d2201918610ca87c2914b61dc8010f
common/duplicate_copy_number: 083cfffd294672043dcbcc4c3dfeac6a
common/e_commerce: b9584e7d0449a6d1b0c182d7ff14061e common/e_commerce: b9584e7d0449a6d1b0c182d7ff14061e
common/edit: eee7f39ff90b18852afc1671f21fbaa9 common/edit: eee7f39ff90b18852afc1671f21fbaa9
common/email: e7f34943a0c2fb849db1839ff6ef5cb5 common/email: e7f34943a0c2fb849db1839ff6ef5cb5
@@ -197,16 +191,13 @@ checksums:
common/error: 3c95bcb32c2104b99a46f5b3dd015248 common/error: 3c95bcb32c2104b99a46f5b3dd015248
common/error_component_description: fa9eee04f864c3fe6e6681f716caa015 common/error_component_description: fa9eee04f864c3fe6e6681f716caa015
common/error_component_title: ae68fa341a143aaa13a5ea30dd57a63e common/error_component_title: ae68fa341a143aaa13a5ea30dd57a63e
common/error_loading_data: aaeffbfe4a2c2145442a57de524494be
common/error_rate_limit_description: 37791a33a947204662ee9c6544e90f51 common/error_rate_limit_description: 37791a33a947204662ee9c6544e90f51
common/error_rate_limit_title: 23ac9419e267e610e1bfd38e1dc35dc0 common/error_rate_limit_title: 23ac9419e267e610e1bfd38e1dc35dc0
common/expand_rows: b6e06327cb8718dfd6651720843e4dad common/expand_rows: b6e06327cb8718dfd6651720843e4dad
common/failed_to_copy_to_clipboard: de836a7d628d36c832809252f188f784 common/failed_to_copy_to_clipboard: de836a7d628d36c832809252f188f784
common/failed_to_load_organizations: 512808a2b674c7c28bca73f8f91fd87e common/failed_to_load_organizations: 512808a2b674c7c28bca73f8f91fd87e
common/failed_to_load_workspaces: 6ee3448097394517dc605074cd4e6ea4 common/failed_to_load_workspaces: 6ee3448097394517dc605074cd4e6ea4
common/filter: 626325a05e4c8800f7ede7012b0cadaf
common/finish: ffa7a10f71182b48fefed7135bee24fa common/finish: ffa7a10f71182b48fefed7135bee24fa
common/first_name: cf040a5d6a9fd696be400380cc99f54b
common/follow_these: 3a730b242bb17a3f95e01bf0dae86885 common/follow_these: 3a730b242bb17a3f95e01bf0dae86885
common/formbricks_version: d9967c797f3e49ca0cae78bc0ebd19cb common/formbricks_version: d9967c797f3e49ca0cae78bc0ebd19cb
common/full_name: f45991923345e8322c9ff8cd6b7e2b16 common/full_name: f45991923345e8322c9ff8cd6b7e2b16
@@ -219,7 +210,6 @@ checksums:
common/hidden_field: 3ed5c58d0ed359e558cdf7bd33606d2d common/hidden_field: 3ed5c58d0ed359e558cdf7bd33606d2d
common/hidden_fields: 3de6cfd308293a826cb8679fd1d49972 common/hidden_fields: 3de6cfd308293a826cb8679fd1d49972
common/hide_column: 23ce94db148f2d8e4a0923defead6cf1 common/hide_column: 23ce94db148f2d8e4a0923defead6cf1
common/id: c8886d38aeea2ed5f785aba4fc96784b
common/image: 048ba7a239de0fbd883ade8558415830 common/image: 048ba7a239de0fbd883ade8558415830
common/images: 9305827c28694866f49db42b4c51831f common/images: 9305827c28694866f49db42b4c51831f
common/import: 348b8ab981de5b7f1fca6d7302263bbd common/import: 348b8ab981de5b7f1fca6d7302263bbd
@@ -237,7 +227,6 @@ checksums:
common/key: 3d1065ab98a1c2f1210507fd5c7bf515 common/key: 3d1065ab98a1c2f1210507fd5c7bf515
common/label: a5c71bf158481233f8215dbd38cc196b common/label: a5c71bf158481233f8215dbd38cc196b
common/language: 277fd1a41cc237a437cd1d5e4a80463b common/language: 277fd1a41cc237a437cd1d5e4a80463b
common/last_name: 2c9a7de7738ca007ba9023c385149c26
common/learn_more: e598091d132f890c37a6d4ed94f6d794 common/learn_more: e598091d132f890c37a6d4ed94f6d794
common/license_expired: 7af13535e320e4197989472c01387d2c common/license_expired: 7af13535e320e4197989472c01387d2c
common/light_overlay: 0499907ea7b8405f4267b117998b5a78 common/light_overlay: 0499907ea7b8405f4267b117998b5a78
@@ -252,6 +241,7 @@ checksums:
common/look_and_feel: 9125503712626d495cedec7a79f1418c common/look_and_feel: 9125503712626d495cedec7a79f1418c
common/manage: a3d40c0267b81ae53c9598eaeb05087d common/manage: a3d40c0267b81ae53c9598eaeb05087d
common/marketing: fcf0f06f8b64b458c7ca6d95541a3cc8 common/marketing: fcf0f06f8b64b458c7ca6d95541a3cc8
common/member: 1606dc30b369856b9dba1fe9aec425d2
common/members: 0932e80cba1e3e0a7f52bb67ff31da32 common/members: 0932e80cba1e3e0a7f52bb67ff31da32
common/members_and_teams: bf5c3fadcb9fc23533ec1532b805ac08 common/members_and_teams: bf5c3fadcb9fc23533ec1532b805ac08
common/membership_not_found: 7ac63584af23396aace9992ad919ffd4 common/membership_not_found: 7ac63584af23396aace9992ad919ffd4
@@ -263,7 +253,6 @@ checksums:
common/move_down: 4f4de55743043355ad4a839aff2c48ff common/move_down: 4f4de55743043355ad4a839aff2c48ff
common/move_up: 69f25b205c677abdb26cbb69d97cd10b common/move_up: 69f25b205c677abdb26cbb69d97cd10b
common/multiple_languages: 7d8ddd4b40d32fcd7bd6f7bac6485b1f common/multiple_languages: 7d8ddd4b40d32fcd7bd6f7bac6485b1f
common/my_product: ad022177062f9ef6e9acf33b13e889aa
common/name: 9368b5a047572b6051f334af5aa76819 common/name: 9368b5a047572b6051f334af5aa76819
common/new: 126d036fae5fb6b629728ecb97e6195b common/new: 126d036fae5fb6b629728ecb97e6195b
common/new_version_available: 399ddfc4232712e18ddab2587356b3dc common/new_version_available: 399ddfc4232712e18ddab2587356b3dc
@@ -359,6 +348,8 @@ checksums:
common/select_teams: ae5d451929846ae6367562bc671a1af9 common/select_teams: ae5d451929846ae6367562bc671a1af9
common/selected: 9f09e059ba20c88ed34e2b4e8e032d56 common/selected: 9f09e059ba20c88ed34e2b4e8e032d56
common/selected_questions: beffe92d5272d99a0022f004e6a6ad73 common/selected_questions: beffe92d5272d99a0022f004e6a6ad73
common/selection: 25b570dc6339916a7aada2142aca0cd1
common/selections: 82f0681bf0208e25d7efedc23c556b8f
common/send_test_email: 2fd3ea40199b9589132ac826a5b0f3f5 common/send_test_email: 2fd3ea40199b9589132ac826a5b0f3f5
common/session_not_found: e9622df3170dbfd9636403bb0c22295b common/session_not_found: e9622df3170dbfd9636403bb0c22295b
common/settings: 8df6777277469c1fd88cc18dde2f1cc3 common/settings: 8df6777277469c1fd88cc18dde2f1cc3
@@ -367,7 +358,6 @@ checksums:
common/show_response_count: 609e5dc7c074d57e711a728fa2f8eb79 common/show_response_count: 609e5dc7c074d57e711a728fa2f8eb79
common/shown: 63e4ffb245c05e04b636446c3dbdd8df common/shown: 63e4ffb245c05e04b636446c3dbdd8df
common/size: 227fadeeff951e041ff42031a11a4626 common/size: 227fadeeff951e041ff42031a11a4626
common/skip: b7f28dfa2f58b80b149bb82b392d0291
common/skipped: d496f0f667e1b4364b954db71335d4ef common/skipped: d496f0f667e1b4364b954db71335d4ef
common/skips: 99de7579122a3fa6ec5e2a47f3fd8b34 common/skips: 99de7579122a3fa6ec5e2a47f3fd8b34
common/some_files_failed_to_upload: a0e26efeb29ae905257ecf93b112dff0 common/some_files_failed_to_upload: a0e26efeb29ae905257ecf93b112dff0
@@ -411,7 +401,6 @@ checksums:
common/top_right: 241f95c923846911aaf13af6109333e5 common/top_right: 241f95c923846911aaf13af6109333e5
common/try_again: 33dd8820e743e35a66e6977f69e9d3b5 common/try_again: 33dd8820e743e35a66e6977f69e9d3b5
common/type: f04471a7ddac844b9ad145eb9911ef75 common/type: f04471a7ddac844b9ad145eb9911ef75
common/unknown_survey: dd8f6985e17ccf19fac1776e18b2c498
common/unlock_more_workspaces_with_a_higher_plan: fe1590075b855bb4306c9388b65143b0 common/unlock_more_workspaces_with_a_higher_plan: fe1590075b855bb4306c9388b65143b0
common/update: 079fc039262fd31b10532929685c2d1b common/update: 079fc039262fd31b10532929685c2d1b
common/updated: 8aa8ff2dc2977ca4b269e80a513100b4 common/updated: 8aa8ff2dc2977ca4b269e80a513100b4
@@ -437,7 +426,6 @@ checksums:
common/website_survey: 17513d25a07b6361768a15ec622b021b common/website_survey: 17513d25a07b6361768a15ec622b021b
common/weeks: 545de30df4f44d3f6d1d344af6a10815 common/weeks: 545de30df4f44d3f6d1d344af6a10815
common/welcome_card: 76081ebd5b2e35da9b0f080323704ae7 common/welcome_card: 76081ebd5b2e35da9b0f080323704ae7
common/workflows: b0c9c8615a9ba7d9cb73e767290a7f72
common/workspace_configuration: d0a5812d6a97d7724d565b1017c34387 common/workspace_configuration: d0a5812d6a97d7724d565b1017c34387
common/workspace_created_successfully: bf401ae83da954f1db48724e2a8e40f1 common/workspace_created_successfully: bf401ae83da954f1db48724e2a8e40f1
common/workspace_creation_description: aea2f480ba0c54c5cabac72c9c900ddf common/workspace_creation_description: aea2f480ba0c54c5cabac72c9c900ddf
@@ -611,27 +599,27 @@ checksums:
environments/contacts/attribute_value: 34b0eaa85808b15cbc4be94c64d0146b environments/contacts/attribute_value: 34b0eaa85808b15cbc4be94c64d0146b
environments/contacts/attribute_value_placeholder: 90fb17015de807031304d7a650a6cb8c environments/contacts/attribute_value_placeholder: 90fb17015de807031304d7a650a6cb8c
environments/contacts/attributes_msg_attribute_limit_exceeded: a6c430860f307f9cc90c449f96a1284f environments/contacts/attributes_msg_attribute_limit_exceeded: a6c430860f307f9cc90c449f96a1284f
environments/contacts/attributes_msg_attribute_type_validation_error: bd70f9773ae873240d4cdb26a662334c environments/contacts/attributes_msg_attribute_type_validation_error: ed177ce83bd174ed6be7e889664f93a1
environments/contacts/attributes_msg_email_already_exists: a3ea1265e3db885f53d0e589aecf6260 environments/contacts/attributes_msg_email_already_exists: a3ea1265e3db885f53d0e589aecf6260
environments/contacts/attributes_msg_email_or_userid_required: febc8b0cda4dd45d2c3cdb1ac2d45dcb environments/contacts/attributes_msg_email_or_userid_required: 3be0e745cd3500c9a23bad2e25ad3147
environments/contacts/attributes_msg_new_attribute_created: 5cba6158c4305c05104814ec1479267c environments/contacts/attributes_msg_new_attribute_created: c4c7b27523058f43b70411d7aa6510e5
environments/contacts/attributes_msg_userid_already_exists: 9c695538befc152806c460f52a73821a environments/contacts/attributes_msg_userid_already_exists: d2d95ece4b06507be18c9ba240b0a26b
environments/contacts/contact_deleted_successfully: c5b64a42a50e055f9e27ec49e20e03fa environments/contacts/contact_deleted_successfully: c5b64a42a50e055f9e27ec49e20e03fa
environments/contacts/contact_not_found: 045396f0b13fafd43612a286263737c0 environments/contacts/contact_not_found: 045396f0b13fafd43612a286263737c0
environments/contacts/contacts_table_refresh: 6a959475991dd4ab28ad881bae569a09 environments/contacts/contacts_table_refresh: 6a959475991dd4ab28ad881bae569a09
environments/contacts/contacts_table_refresh_success: 40951396e88e5c8fdafa0b3bb4fadca8 environments/contacts/contacts_table_refresh_success: 40951396e88e5c8fdafa0b3bb4fadca8
environments/contacts/create_attribute: 87320615901f95b4f35ee83c290a3a6c environments/contacts/create_attribute: 87320615901f95b4f35ee83c290a3a6c
environments/contacts/create_key: 0d385c354af8963acbe35cd646710f86
environments/contacts/create_new_attribute: c17d407dacd0b90f360f9f5e899d662f environments/contacts/create_new_attribute: c17d407dacd0b90f360f9f5e899d662f
environments/contacts/create_new_attribute_description: cc19d76bb6940537bbe3461191f25d26 environments/contacts/create_new_attribute_description: cc19d76bb6940537bbe3461191f25d26
environments/contacts/custom_attributes: fffc7722742d1291b102dc737cf2fc9e environments/contacts/custom_attributes: fffc7722742d1291b102dc737cf2fc9e
environments/contacts/data_type: 1ea127ba2c18d0d91fb0361cc6747e2b environments/contacts/data_type: 1ea127ba2c18d0d91fb0361cc6747e2b
environments/contacts/data_type_cannot_be_changed: 22603f6193fdac3784eeef8315df70de environments/contacts/data_type_cannot_be_changed: 22603f6193fdac3784eeef8315df70de
environments/contacts/data_type_description: 800bf4935df15e6cb14269e1b60c506e environments/contacts/data_type_description: 800bf4935df15e6cb14269e1b60c506e
environments/contacts/date_value_required: f70908e22f962731635c0bc9026da1c4 environments/contacts/date_value_required: e0e75b75ae4e8c02f03284954756adc9
environments/contacts/delete_attribute_confirmation: 01d99b89eb3d27ff468d0db1b4aeb394 environments/contacts/delete_attribute_confirmation: 01d99b89eb3d27ff468d0db1b4aeb394
environments/contacts/delete_contact_confirmation: 2d45579e0bb4bc40fb1ee75b43c0e7a4 environments/contacts/delete_contact_confirmation: 2d45579e0bb4bc40fb1ee75b43c0e7a4
environments/contacts/delete_contact_confirmation_with_quotas: d3d17f13ae46ce04c126c82bf01299ac environments/contacts/delete_contact_confirmation_with_quotas: d3d17f13ae46ce04c126c82bf01299ac
environments/contacts/displays: fcc4527002bd045021882be463b8ac72
environments/contacts/edit_attribute: 92a83c96a5d850e7d39002e8fd5898f4 environments/contacts/edit_attribute: 92a83c96a5d850e7d39002e8fd5898f4
environments/contacts/edit_attribute_description: 073a3084bb2f3b34ed1320ed1cd6db3c environments/contacts/edit_attribute_description: 073a3084bb2f3b34ed1320ed1cd6db3c
environments/contacts/edit_attribute_values: 44e4e7a661cc1b59200bb07c710072a7 environments/contacts/edit_attribute_values: 44e4e7a661cc1b59200bb07c710072a7
@@ -643,7 +631,6 @@ checksums:
environments/contacts/invalid_csv_column_names: dcb8534e7d4c00b9ea7bdaf389f72328 environments/contacts/invalid_csv_column_names: dcb8534e7d4c00b9ea7bdaf389f72328
environments/contacts/invalid_date_format: 5bad9730ac5a5bacd0792098f712b1c4 environments/contacts/invalid_date_format: 5bad9730ac5a5bacd0792098f712b1c4
environments/contacts/invalid_number_format: bd0422507385f671c3046730a6febc64 environments/contacts/invalid_number_format: bd0422507385f671c3046730a6febc64
environments/contacts/no_activity_yet: f88897ac05afd6bf8af0d4834ad24ffc
environments/contacts/no_published_link_surveys_available: 9c1abc5b21aba827443cdf87dd6c8bfe environments/contacts/no_published_link_surveys_available: 9c1abc5b21aba827443cdf87dd6c8bfe
environments/contacts/no_published_surveys: bd945b0e2e2328c17615c94143bdd62b environments/contacts/no_published_surveys: bd945b0e2e2328c17615c94143bdd62b
environments/contacts/no_responses_found: f10190cffdda4ca1bed479acbb89b13f environments/contacts/no_responses_found: f10190cffdda4ca1bed479acbb89b13f
@@ -658,12 +645,10 @@ checksums:
environments/contacts/select_a_survey: 1f49086dfb874307aae1136e88c3d514 environments/contacts/select_a_survey: 1f49086dfb874307aae1136e88c3d514
environments/contacts/select_attribute: d93fb60eb4fbb42bf13a22f6216fbd79 environments/contacts/select_attribute: d93fb60eb4fbb42bf13a22f6216fbd79
environments/contacts/select_attribute_key: 673a6683fab41b387d921841cded7e38 environments/contacts/select_attribute_key: 673a6683fab41b387d921841cded7e38
environments/contacts/survey_viewed: 646d413218626787b0373ffd71cb7451
environments/contacts/survey_viewed_at: 2ab535237af5c3c3f33acc792a7e70a4
environments/contacts/system_attributes: eadb6a8888c7b32c0e68881f945ae9b6 environments/contacts/system_attributes: eadb6a8888c7b32c0e68881f945ae9b6
environments/contacts/unlock_contacts_description: c5572047f02b4c39e5109f9de715499d environments/contacts/unlock_contacts_description: c5572047f02b4c39e5109f9de715499d
environments/contacts/unlock_contacts_title: a8b3d7db03eb404d9267fd5cdd6d5ddb environments/contacts/unlock_contacts_title: a8b3d7db03eb404d9267fd5cdd6d5ddb
environments/contacts/upload_contacts_error_attribute_type_mismatch: 5f1ba60a88f166c5d387f49f1040c675 environments/contacts/upload_contacts_error_attribute_type_mismatch: 70a60f0886ce767c00defa7d4aad0f93
environments/contacts/upload_contacts_error_duplicate_mappings: 9c1e1f07e476226bad98ccfa07979fec environments/contacts/upload_contacts_error_duplicate_mappings: 9c1e1f07e476226bad98ccfa07979fec
environments/contacts/upload_contacts_error_file_too_large: 0c1837286c55d18049277465bc2444c1 environments/contacts/upload_contacts_error_file_too_large: 0c1837286c55d18049277465bc2444c1
environments/contacts/upload_contacts_error_generic: 3a8d35a421b377198361af9972392693 environments/contacts/upload_contacts_error_generic: 3a8d35a421b377198361af9972392693
@@ -726,12 +711,7 @@ checksums:
environments/integrations/google_sheets/link_google_sheet: fa78146ae26ce5b1d2aaf2678f628943 environments/integrations/google_sheets/link_google_sheet: fa78146ae26ce5b1d2aaf2678f628943
environments/integrations/google_sheets/link_new_sheet: 8ad2ea8708f50ed184c00b84577b325e environments/integrations/google_sheets/link_new_sheet: 8ad2ea8708f50ed184c00b84577b325e
environments/integrations/google_sheets/no_integrations_yet: ea46f7747937baf48a47a4c1b1776aee environments/integrations/google_sheets/no_integrations_yet: ea46f7747937baf48a47a4c1b1776aee
environments/integrations/google_sheets/reconnect_button: 8992a0f250278c116cb26be448b68ba2
environments/integrations/google_sheets/reconnect_button_description: 851fd2fda57211293090f371d5b2c734
environments/integrations/google_sheets/reconnect_button_tooltip: 210dd97470fde8264d2c076db3c98fde
environments/integrations/google_sheets/spreadsheet_permission_error: 24a55163c657593e88c28fdc8628acdd
environments/integrations/google_sheets/spreadsheet_url: b1665f96e6ecce23ea2d9196f4a3e5dd environments/integrations/google_sheets/spreadsheet_url: b1665f96e6ecce23ea2d9196f4a3e5dd
environments/integrations/google_sheets/token_expired_error: 555d34c18c554ec8ac66614f21bd44fc
environments/integrations/include_created_at: 8011355b13e28e638d74e6f3d68a2bbf environments/integrations/include_created_at: 8011355b13e28e638d74e6f3d68a2bbf
environments/integrations/include_hidden_fields: 25f0ea5ca1c6ead2cd121f8754cb8d72 environments/integrations/include_hidden_fields: 25f0ea5ca1c6ead2cd121f8754cb8d72
environments/integrations/include_metadata: 750091d965d7cc8d02468b5239816dc5 environments/integrations/include_metadata: 750091d965d7cc8d02468b5239816dc5
@@ -926,7 +906,7 @@ checksums:
environments/settings/billing/current_plan: 7497746eb3b4897ff953b1aa8c7c9f63 environments/settings/billing/current_plan: 7497746eb3b4897ff953b1aa8c7c9f63
environments/settings/billing/current_tier_limit: a6875905f376953b12fdf5ae8fc7e051 environments/settings/billing/current_tier_limit: a6875905f376953b12fdf5ae8fc7e051
environments/settings/billing/custom: fee41bfbe59e71721d8648e7a95ec9c5 environments/settings/billing/custom: fee41bfbe59e71721d8648e7a95ec9c5
environments/settings/billing/custom_contacts_limit: c9c10a51c470d9b5661d47317eb8f94e environments/settings/billing/custom_contacts_limit: 380f13656e3b9e1a6cb3fffd1a1cc263
environments/settings/billing/custom_response_limit: 96ef34d587001a7b479f3f6f7c9e66dc environments/settings/billing/custom_response_limit: 96ef34d587001a7b479f3f6f7c9e66dc
environments/settings/billing/custom_workspace_limit: 3f6f7f901dfc245028ce938e3d9aa2c6 environments/settings/billing/custom_workspace_limit: 3f6f7f901dfc245028ce938e3d9aa2c6
environments/settings/billing/email_embedded_surveys: bb1f558f9061287666041c08384ad1d4 environments/settings/billing/email_embedded_surveys: bb1f558f9061287666041c08384ad1d4
@@ -1000,7 +980,7 @@ checksums:
environments/settings/enterprise/recheck_license_invalid: 58f41bc208692b7d53b975dfcf9f4ad8 environments/settings/enterprise/recheck_license_invalid: 58f41bc208692b7d53b975dfcf9f4ad8
environments/settings/enterprise/recheck_license_success: 700ddd805be904a415f614de3df1da78 environments/settings/enterprise/recheck_license_success: 700ddd805be904a415f614de3df1da78
environments/settings/enterprise/recheck_license_unreachable: 0ca81bd89595a9da24bc94dcef132175 environments/settings/enterprise/recheck_license_unreachable: 0ca81bd89595a9da24bc94dcef132175
environments/settings/enterprise/rechecking: 68dacda4ccacfc808c5105e99dad4b71 environments/settings/enterprise/rechecking: 54c454aa8e4d27363543349b7c2a28bc
environments/settings/enterprise/request_30_day_trial_license: 8d5a1b5d9f0790783693122ac30c16ef environments/settings/enterprise/request_30_day_trial_license: 8d5a1b5d9f0790783693122ac30c16ef
environments/settings/enterprise/saml_sso: 86b76024524fc585b2c3950126ef6f62 environments/settings/enterprise/saml_sso: 86b76024524fc585b2c3950126ef6f62
environments/settings/enterprise/service_level_agreement: e31e74f66f5c7c79e82878f4f68abc37 environments/settings/enterprise/service_level_agreement: e31e74f66f5c7c79e82878f4f68abc37
@@ -1026,7 +1006,7 @@ checksums:
environments/settings/general/email_customization_preview_email_heading: 8b798cb8438b3dd356c02dab33b4c897 environments/settings/general/email_customization_preview_email_heading: 8b798cb8438b3dd356c02dab33b4c897
environments/settings/general/email_customization_preview_email_text: fa6ae92403cc8f3c35c03e6c94cbde51 environments/settings/general/email_customization_preview_email_text: fa6ae92403cc8f3c35c03e6c94cbde51
environments/settings/general/error_deleting_organization_please_try_again: 7f0fe257d4a0b40bff025408a7766706 environments/settings/general/error_deleting_organization_please_try_again: 7f0fe257d4a0b40bff025408a7766706
environments/settings/general/from_your_organization: 9ebd6dcd79f7bfad3fea46ed2e3133d2 environments/settings/general/from_your_organization: 4b7970431edb3d0f13c394dbd755a055
environments/settings/general/invitation_sent_once_more: e6e5ea066810f9dcb65788aa4f05d6e2 environments/settings/general/invitation_sent_once_more: e6e5ea066810f9dcb65788aa4f05d6e2
environments/settings/general/invite_deleted_successfully: 1c7dca6d0f6870d945288e38cfd2f943 environments/settings/general/invite_deleted_successfully: 1c7dca6d0f6870d945288e38cfd2f943
environments/settings/general/invite_expires_on: 6fd2356ad91a5f189070c43855904bb4 environments/settings/general/invite_expires_on: 6fd2356ad91a5f189070c43855904bb4
@@ -1073,7 +1053,7 @@ checksums:
environments/settings/notifications/notification_settings_updated: 948fb98c8a9225ba6b17c43365576df9 environments/settings/notifications/notification_settings_updated: 948fb98c8a9225ba6b17c43365576df9
environments/settings/notifications/set_up_an_alert_to_get_an_email_on_new_responses: 4168ae6f5962244a4894a22b56fbb55f environments/settings/notifications/set_up_an_alert_to_get_an_email_on_new_responses: 4168ae6f5962244a4894a22b56fbb55f
environments/settings/notifications/use_the_integration: 765ec5b4959cb096234e8b9a0d6b01f5 environments/settings/notifications/use_the_integration: 765ec5b4959cb096234e8b9a0d6b01f5
environments/settings/notifications/want_to_loop_in_organization_mates: 46b06df5be0d610c2c3b552a5553ee2b environments/settings/notifications/want_to_loop_in_organization_mates: 147cb4d1ebcc957e5e965914ae9d7d4d
environments/settings/notifications/you_will_not_be_auto_subscribed_to_this_organizations_surveys_anymore: e158e20fc4fa511ce457d99d0c865c3c environments/settings/notifications/you_will_not_be_auto_subscribed_to_this_organizations_surveys_anymore: e158e20fc4fa511ce457d99d0c865c3c
environments/settings/notifications/you_will_not_receive_any_more_emails_for_responses_on_this_survey: 7e2a88fe0d0489ebdc8f2621584b1c29 environments/settings/notifications/you_will_not_receive_any_more_emails_for_responses_on_this_survey: 7e2a88fe0d0489ebdc8f2621584b1c29
environments/settings/profile/account_deletion_consequences_warning: b87b9eebe0a373eb6debe52db73e5aba environments/settings/profile/account_deletion_consequences_warning: b87b9eebe0a373eb6debe52db73e5aba
@@ -1181,7 +1161,6 @@ checksums:
environments/surveys/edit/add_fallback_placeholder: 0e77ea487ddd7bc7fc2f1574b018dc08 environments/surveys/edit/add_fallback_placeholder: 0e77ea487ddd7bc7fc2f1574b018dc08
environments/surveys/edit/add_hidden_field_id: a8f55b51b790cf5f4d898af7770ad1ed environments/surveys/edit/add_hidden_field_id: a8f55b51b790cf5f4d898af7770ad1ed
environments/surveys/edit/add_highlight_border: 66f52b21fbb9aa6561c98a090abaaf8f environments/surveys/edit/add_highlight_border: 66f52b21fbb9aa6561c98a090abaaf8f
environments/surveys/edit/add_highlight_border_description: fe548fe03ea10ef5cd9e553d6812b3c2
environments/surveys/edit/add_logic: f234c9f1393a9ed4792dfbd15838c951 environments/surveys/edit/add_logic: f234c9f1393a9ed4792dfbd15838c951
environments/surveys/edit/add_none_of_the_above: dbe1ada4512d6c3f80c54c8fac107ec6 environments/surveys/edit/add_none_of_the_above: dbe1ada4512d6c3f80c54c8fac107ec6
environments/surveys/edit/add_option: 143c54f0b201067fe5159284d6daeca2 environments/surveys/edit/add_option: 143c54f0b201067fe5159284d6daeca2
@@ -1198,14 +1177,12 @@ checksums:
environments/surveys/edit/adjust_survey_closed_message: ae6f38c9daf08656362bd84459a312fa environments/surveys/edit/adjust_survey_closed_message: ae6f38c9daf08656362bd84459a312fa
environments/surveys/edit/adjust_survey_closed_message_description: e906aebd9af6451a2a39c73287927299 environments/surveys/edit/adjust_survey_closed_message_description: e906aebd9af6451a2a39c73287927299
environments/surveys/edit/adjust_the_theme_in_the: bccdafda8af5871513266f668b55d690 environments/surveys/edit/adjust_the_theme_in_the: bccdafda8af5871513266f668b55d690
environments/surveys/edit/all_are_true: 05d02c5afac857da530b73dcf18dd8e4
environments/surveys/edit/all_other_answers_will_continue_to: 9a5d09eea42ff5fd1c18cc58a14dcabd environments/surveys/edit/all_other_answers_will_continue_to: 9a5d09eea42ff5fd1c18cc58a14dcabd
environments/surveys/edit/allow_multi_select: 7b4b83f7a0205e2a0a8971671a69a174 environments/surveys/edit/allow_multi_select: 7b4b83f7a0205e2a0a8971671a69a174
environments/surveys/edit/allow_multiple_files: dbd99f9d1026e4f7c5a5d03f71ba379d environments/surveys/edit/allow_multiple_files: dbd99f9d1026e4f7c5a5d03f71ba379d
environments/surveys/edit/allow_users_to_select_more_than_one_image: d683e0b538d1366400292a771f3fbd08 environments/surveys/edit/allow_users_to_select_more_than_one_image: d683e0b538d1366400292a771f3fbd08
environments/surveys/edit/and_launch_surveys_in_your_website_or_app: a3edcdb4aea792a27d90aad1930f001a environments/surveys/edit/and_launch_surveys_in_your_website_or_app: a3edcdb4aea792a27d90aad1930f001a
environments/surveys/edit/animation: 66a18eacfb92fc9fc9db188d2dde4f81 environments/surveys/edit/animation: 66a18eacfb92fc9fc9db188d2dde4f81
environments/surveys/edit/any_is_true: 32c9f3998984fd32a2b5bc53f2d97429
environments/surveys/edit/app_survey_description: bdfacfce478e97f70b700a1382dfa687 environments/surveys/edit/app_survey_description: bdfacfce478e97f70b700a1382dfa687
environments/surveys/edit/assign: e80715ab64bf7cf463abb3a9fd1ad516 environments/surveys/edit/assign: e80715ab64bf7cf463abb3a9fd1ad516
environments/surveys/edit/audience: a4d9fab4214a641e2d358fbb28f010e0 environments/surveys/edit/audience: a4d9fab4214a641e2d358fbb28f010e0
@@ -1236,7 +1213,7 @@ checksums:
environments/surveys/edit/calculate: c5fcf8d3a38706ae2071b6f78339ec68 environments/surveys/edit/calculate: c5fcf8d3a38706ae2071b6f78339ec68
environments/surveys/edit/capture_a_new_action_to_trigger_a_survey_on: 73410e9665a37bc4a9747db5d683d36c environments/surveys/edit/capture_a_new_action_to_trigger_a_survey_on: 73410e9665a37bc4a9747db5d683d36c
environments/surveys/edit/capture_ip_address: e950f924f1c0b52f8c5b06ca118e049f environments/surveys/edit/capture_ip_address: e950f924f1c0b52f8c5b06ca118e049f
environments/surveys/edit/capture_ip_address_description: f6bf6046db16667237511265e9fba5f0 environments/surveys/edit/capture_ip_address_description: 932d1b4ad68594d06d4eaf0212f9570c
environments/surveys/edit/capture_new_action: 0aa2a3c399b62b1a52307deedf4922e8 environments/surveys/edit/capture_new_action: 0aa2a3c399b62b1a52307deedf4922e8
environments/surveys/edit/card_arrangement_for_survey_type_derived: c06b9aaebcc11bc16e57a445b62361fc environments/surveys/edit/card_arrangement_for_survey_type_derived: c06b9aaebcc11bc16e57a445b62361fc
environments/surveys/edit/card_background_color: acd5d023e1d1a4471b053dce504c7a83 environments/surveys/edit/card_background_color: acd5d023e1d1a4471b053dce504c7a83
@@ -1247,7 +1224,7 @@ checksums:
environments/surveys/edit/casual: 6534fe68718fade470a9031f7390409e environments/surveys/edit/casual: 6534fe68718fade470a9031f7390409e
environments/surveys/edit/caution_edit_duplicate: ee93bccb34fcd707e1ef4735f1c2fc31 environments/surveys/edit/caution_edit_duplicate: ee93bccb34fcd707e1ef4735f1c2fc31
environments/surveys/edit/caution_edit_published_survey: faf7fc57c776f2a9104d143e20044486 environments/surveys/edit/caution_edit_published_survey: faf7fc57c776f2a9104d143e20044486
environments/surveys/edit/caution_explanation_intro: 143ed2e04bf5bf0bcc0752cb87acda51 environments/surveys/edit/caution_explanation_intro: d6bd8cfdca654353cdced650c1416834
environments/surveys/edit/caution_explanation_new_responses_separated: 387286482b7ecd725ce20521dacbc485 environments/surveys/edit/caution_explanation_new_responses_separated: 387286482b7ecd725ce20521dacbc485
environments/surveys/edit/caution_explanation_only_new_responses_in_summary: 1ad5f649c7784a58bfa9d62304780514 environments/surveys/edit/caution_explanation_only_new_responses_in_summary: 1ad5f649c7784a58bfa9d62304780514
environments/surveys/edit/caution_explanation_responses_are_safe: 090ff00b7922a49c273e67c5f364730d environments/surveys/edit/caution_explanation_responses_are_safe: 090ff00b7922a49c273e67c5f364730d
@@ -1382,6 +1359,7 @@ checksums:
environments/surveys/edit/follow_ups_modal_updated_successfull_toast: 61204fada3231f4f1fe3866e87e1130a environments/surveys/edit/follow_ups_modal_updated_successfull_toast: 61204fada3231f4f1fe3866e87e1130a
environments/surveys/edit/follow_ups_new: 224c779d252b3e75086e4ed456ba2548 environments/surveys/edit/follow_ups_new: 224c779d252b3e75086e4ed456ba2548
environments/surveys/edit/follow_ups_upgrade_button_text: 4cd167527fc6cdb5b0bfc9b486b142a8 environments/surveys/edit/follow_ups_upgrade_button_text: 4cd167527fc6cdb5b0bfc9b486b142a8
environments/surveys/edit/form_styling: 1278a2db4257b5500474161133acc857
environments/surveys/edit/formbricks_sdk_is_not_connected: 35165b0cac182a98408007a378cc677e environments/surveys/edit/formbricks_sdk_is_not_connected: 35165b0cac182a98408007a378cc677e
environments/surveys/edit/four_points: b289628a6b8a6cd0f7d17a14ca6cd7bf environments/surveys/edit/four_points: b289628a6b8a6cd0f7d17a14ca6cd7bf
environments/surveys/edit/heading: 79e9dfa461f38a239d34b9833ca103f1 environments/surveys/edit/heading: 79e9dfa461f38a239d34b9833ca103f1
@@ -1471,7 +1449,7 @@ checksums:
environments/surveys/edit/option_idx: 9eb69cb3d19797daf1300699b441ef9d environments/surveys/edit/option_idx: 9eb69cb3d19797daf1300699b441ef9d
environments/surveys/edit/option_used_in_logic_error: c682ac2cfd286c3cc07dd21ac863dd4c environments/surveys/edit/option_used_in_logic_error: c682ac2cfd286c3cc07dd21ac863dd4c
environments/surveys/edit/optional: 396fb9a0472daf401c392bdc3e248943 environments/surveys/edit/optional: 396fb9a0472daf401c392bdc3e248943
environments/surveys/edit/options: 39ba06e709561761d922ca1254155d5f environments/surveys/edit/options: 59156082418d80acb211f973b1218f11
environments/surveys/edit/options_used_in_logic_bulk_error: 1720e7a01a0bcb67c152cfe6a68c5355 environments/surveys/edit/options_used_in_logic_bulk_error: 1720e7a01a0bcb67c152cfe6a68c5355
environments/surveys/edit/override_theme_with_individual_styles_for_this_survey: edffc97f5d3372419fe0444de0a5aa3f environments/surveys/edit/override_theme_with_individual_styles_for_this_survey: edffc97f5d3372419fe0444de0a5aa3f
environments/surveys/edit/overwrite_global_waiting_time: bf39ba91e35742e1ff3a281a18e9159c environments/surveys/edit/overwrite_global_waiting_time: bf39ba91e35742e1ff3a281a18e9159c
@@ -1496,7 +1474,6 @@ checksums:
environments/surveys/edit/question_deleted: ecdeb22b81ae2d732656a7742c1eec7b environments/surveys/edit/question_deleted: ecdeb22b81ae2d732656a7742c1eec7b
environments/surveys/edit/question_duplicated: 3f02439fd0a8b818bc84c1b1b473898c environments/surveys/edit/question_duplicated: 3f02439fd0a8b818bc84c1b1b473898c
environments/surveys/edit/question_id_updated: e8d94dbefcbad00c7464b3d1fb0ee81a environments/surveys/edit/question_id_updated: e8d94dbefcbad00c7464b3d1fb0ee81a
environments/surveys/edit/question_number: 742636e9d2d5dcc7ee6ca1b3016bcee7
environments/surveys/edit/question_used_in_logic_warning_text: ec78767a7cf335222d41b98cb5baa6be environments/surveys/edit/question_used_in_logic_warning_text: ec78767a7cf335222d41b98cb5baa6be
environments/surveys/edit/question_used_in_logic_warning_title: 4bb8528cdc3b8649c194487067737f6d environments/surveys/edit/question_used_in_logic_warning_title: 4bb8528cdc3b8649c194487067737f6d
environments/surveys/edit/question_used_in_quota: ceb5e88f6916e4863e589c6be030bb3b environments/surveys/edit/question_used_in_quota: ceb5e88f6916e4863e589c6be030bb3b
@@ -1553,7 +1530,7 @@ checksums:
environments/surveys/edit/response_limits_redirections_and_more: e4f1cf94e56ad0e1b08701158d688802 environments/surveys/edit/response_limits_redirections_and_more: e4f1cf94e56ad0e1b08701158d688802
environments/surveys/edit/response_options: 2988136d5248d7726583108992dcbaee environments/surveys/edit/response_options: 2988136d5248d7726583108992dcbaee
environments/surveys/edit/roundness: 5a161c8f5f258defb57ed1d551737cc4 environments/surveys/edit/roundness: 5a161c8f5f258defb57ed1d551737cc4
environments/surveys/edit/roundness_description: 03940a6871ae43efa4810cba7cadb74b environments/surveys/edit/roundness_description: bde131aa5674836416dcdf2ff517d899
environments/surveys/edit/row_used_in_logic_error: f89453ff1b6db77ad84af840fedd9813 environments/surveys/edit/row_used_in_logic_error: f89453ff1b6db77ad84af840fedd9813
environments/surveys/edit/rows: 8f41f34e6ca28221cf1ebd948af4c151 environments/surveys/edit/rows: 8f41f34e6ca28221cf1ebd948af4c151
environments/surveys/edit/save_and_close: 6ede705b3f82f30269ff3054a5049e34 environments/surveys/edit/save_and_close: 6ede705b3f82f30269ff3054a5049e34
@@ -1599,7 +1576,6 @@ checksums:
environments/surveys/edit/survey_completed_subheading: db537c356c3ab6564d24de0d11a0fee2 environments/surveys/edit/survey_completed_subheading: db537c356c3ab6564d24de0d11a0fee2
environments/surveys/edit/survey_display_settings: 8ed19e6a8e1376f7a1ba037d82c4ae11 environments/surveys/edit/survey_display_settings: 8ed19e6a8e1376f7a1ba037d82c4ae11
environments/surveys/edit/survey_placement: 083c10f257337f9648bf9d435b18ec2c environments/surveys/edit/survey_placement: 083c10f257337f9648bf9d435b18ec2c
environments/surveys/edit/survey_styling: 7f96d6563e934e65687b74374a33b1dc
environments/surveys/edit/survey_trigger: f0c7014a684ca566698b87074fad5579 environments/surveys/edit/survey_trigger: f0c7014a684ca566698b87074fad5579
environments/surveys/edit/switch_multi_language_on_to_get_started: cca0ef91ee49095da30cd1e3f26c406f environments/surveys/edit/switch_multi_language_on_to_get_started: cca0ef91ee49095da30cd1e3f26c406f
environments/surveys/edit/target_block_not_found: 0a0c401017ab32364fec2fcbf815d832 environments/surveys/edit/target_block_not_found: 0a0c401017ab32364fec2fcbf815d832
@@ -1661,7 +1637,7 @@ checksums:
environments/surveys/edit/validation/pattern: c6f01d7bc9baa21a40ea38fa986bd5a0 environments/surveys/edit/validation/pattern: c6f01d7bc9baa21a40ea38fa986bd5a0
environments/surveys/edit/validation/phone: bcd7bd37a475ab1f80ea4c5b4d4d0bb5 environments/surveys/edit/validation/phone: bcd7bd37a475ab1f80ea4c5b4d4d0bb5
environments/surveys/edit/validation/rank_all_options: a885523e9d7820c9b0529bca37e48ccc environments/surveys/edit/validation/rank_all_options: a885523e9d7820c9b0529bca37e48ccc
environments/surveys/edit/validation/select_file_extensions: cbe67b01658ff5f654e4e18fd6fa34ec environments/surveys/edit/validation/select_file_extensions: 208ccb7bd4dde20b0d79bdd1fa763076
environments/surveys/edit/validation/select_option: 53ba37697cca1f6c7d57ecca53ea063e environments/surveys/edit/validation/select_option: 53ba37697cca1f6c7d57ecca53ea063e
environments/surveys/edit/validation/start_date: 881de78c79b56f5ceb9b7103bf23cb2c environments/surveys/edit/validation/start_date: 881de78c79b56f5ceb9b7103bf23cb2c
environments/surveys/edit/validation/url: 4006a4d8dfac013758f0053f6aa67cdd environments/surveys/edit/validation/url: 4006a4d8dfac013758f0053f6aa67cdd
@@ -1686,9 +1662,9 @@ checksums:
environments/surveys/edit/waiting_time_across_surveys: 6873c18d51830e2cadef67cce6a2c95c environments/surveys/edit/waiting_time_across_surveys: 6873c18d51830e2cadef67cce6a2c95c
environments/surveys/edit/waiting_time_across_surveys_description: 6edafaeb3ccd8cadde81175776636c8e environments/surveys/edit/waiting_time_across_surveys_description: 6edafaeb3ccd8cadde81175776636c8e
environments/surveys/edit/welcome_message: 986a434e3895c8ee0b267df95cc40051 environments/surveys/edit/welcome_message: 986a434e3895c8ee0b267df95cc40051
environments/surveys/edit/when: a40ad3eed1b75e76226290eeb9bb20cd
environments/surveys/edit/without_a_filter_all_of_your_users_can_be_surveyed: 451990569c61f25d01044cc45b1ce122 environments/surveys/edit/without_a_filter_all_of_your_users_can_be_surveyed: 451990569c61f25d01044cc45b1ce122
environments/surveys/edit/you_have_not_created_a_segment_yet: c6658bd1cee9c5c957c675db044708dd environments/surveys/edit/you_have_not_created_a_segment_yet: c6658bd1cee9c5c957c675db044708dd
environments/surveys/edit/you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations: 04241177ba989ef4c1d8c01e1a7b8541
environments/surveys/edit/your_description_here_recall_information_with: 60f73a3cc9bdb9afea2166a7db8fd618 environments/surveys/edit/your_description_here_recall_information_with: 60f73a3cc9bdb9afea2166a7db8fd618
environments/surveys/edit/your_question_here_recall_information_with: 6395bd54f5167830c9d662ba403da167 environments/surveys/edit/your_question_here_recall_information_with: 6395bd54f5167830c9d662ba403da167
environments/surveys/edit/your_web_app: 07234bed03a33330dc50ae9fcf0174f3 environments/surveys/edit/your_web_app: 07234bed03a33330dc50ae9fcf0174f3
@@ -1751,7 +1727,7 @@ checksums:
environments/surveys/share/anonymous_links/disable_single_use_link_modal_button: e26f8f680c7c21dcade0b96543d7fb0b environments/surveys/share/anonymous_links/disable_single_use_link_modal_button: e26f8f680c7c21dcade0b96543d7fb0b
environments/surveys/share/anonymous_links/disable_single_use_link_modal_description: 30e41451528ee1ec80cad04ffd90d64b environments/surveys/share/anonymous_links/disable_single_use_link_modal_description: 30e41451528ee1ec80cad04ffd90d64b
environments/surveys/share/anonymous_links/generate_and_download_links: bb28f5a4f6d7cc4c8b4ca94b923b2574 environments/surveys/share/anonymous_links/generate_and_download_links: bb28f5a4f6d7cc4c8b4ca94b923b2574
environments/surveys/share/anonymous_links/generate_links_error: ef2d0c17d261ca498defe91c6f5e8901 environments/surveys/share/anonymous_links/generate_links_error: cc37320a3b3e63274982eb37c06569dd
environments/surveys/share/anonymous_links/multi_use_link: 99d94762889174aa0643416accaa2427 environments/surveys/share/anonymous_links/multi_use_link: 99d94762889174aa0643416accaa2427
environments/surveys/share/anonymous_links/multi_use_link_description: 6ac5e527e647ebecdf795c5acfd744e9 environments/surveys/share/anonymous_links/multi_use_link_description: 6ac5e527e647ebecdf795c5acfd744e9
environments/surveys/share/anonymous_links/multi_use_powers_other_channels_description: b3758f9e7a8488d5ef3101584715ef9f environments/surveys/share/anonymous_links/multi_use_powers_other_channels_description: b3758f9e7a8488d5ef3101584715ef9f
@@ -1843,7 +1819,7 @@ checksums:
environments/surveys/share/social_media/source_tracking_enabled: 3c0a4f7aecda5f1ca8c1991f7b36c05b environments/surveys/share/social_media/source_tracking_enabled: 3c0a4f7aecda5f1ca8c1991f7b36c05b
environments/surveys/share/social_media/source_tracking_enabled_alert_description: 4f4929310e307e946281b96943e834e1 environments/surveys/share/social_media/source_tracking_enabled_alert_description: 4f4929310e307e946281b96943e834e1
environments/surveys/share/social_media/title: 1bf4899b063ee8f02f7188576555828b environments/surveys/share/social_media/title: 1bf4899b063ee8f02f7188576555828b
environments/surveys/summary/added_filter_for_responses_where_answer_to_question: ca743504c3f34b4e5fa98e3e79100624 environments/surveys/summary/added_filter_for_responses_where_answer_to_question: 5bddf0d4f771efd06d58441d11fa5091
environments/surveys/summary/added_filter_for_responses_where_answer_to_question_is_skipped: 74ca713c491cfc33751a5db3de972821 environments/surveys/summary/added_filter_for_responses_where_answer_to_question_is_skipped: 74ca713c491cfc33751a5db3de972821
environments/surveys/summary/aggregated: 9d4e77225d5952abed414fffd828c078 environments/surveys/summary/aggregated: 9d4e77225d5952abed414fffd828c078
environments/surveys/summary/all_responses_csv: 16c0c211853f0839a79f1127ec679ca2 environments/surveys/summary/all_responses_csv: 16c0c211853f0839a79f1127ec679ca2
@@ -1870,7 +1846,6 @@ checksums:
environments/surveys/summary/filtered_responses_excel: 06e57bae9e41979fd7fc4b8bfe3466f9 environments/surveys/summary/filtered_responses_excel: 06e57bae9e41979fd7fc4b8bfe3466f9
environments/surveys/summary/generating_qr_code: 5026d4a76f995db458195e5215d9bbd9 environments/surveys/summary/generating_qr_code: 5026d4a76f995db458195e5215d9bbd9
environments/surveys/summary/impressions: 7fe38d42d68a64d3fd8436a063751584 environments/surveys/summary/impressions: 7fe38d42d68a64d3fd8436a063751584
environments/surveys/summary/impressions_identified_only: 10f8c491463c73b8e6534314ee00d165
environments/surveys/summary/impressions_tooltip: 4d0823cbf360304770c7c5913e33fdc8 environments/surveys/summary/impressions_tooltip: 4d0823cbf360304770c7c5913e33fdc8
environments/surveys/summary/in_app/connection_description: 9710bbf8048a8a5c3b2b56db9d946b73 environments/surveys/summary/in_app/connection_description: 9710bbf8048a8a5c3b2b56db9d946b73
environments/surveys/summary/in_app/connection_title: 29e8a40ad6a7fdb5af5ee9451a70a9aa environments/surveys/summary/in_app/connection_title: 29e8a40ad6a7fdb5af5ee9451a70a9aa
@@ -1911,7 +1886,6 @@ checksums:
environments/surveys/summary/last_quarter: 2e565a81de9b3d7b1ee709ebb6f6eda1 environments/surveys/summary/last_quarter: 2e565a81de9b3d7b1ee709ebb6f6eda1
environments/surveys/summary/last_year: fe7c268a48bf85bc40da000e6e437637 environments/surveys/summary/last_year: fe7c268a48bf85bc40da000e6e437637
environments/surveys/summary/limit: 347051f1a068e01e8c4e4f6744d8e727 environments/surveys/summary/limit: 347051f1a068e01e8c4e4f6744d8e727
environments/surveys/summary/no_identified_impressions: c3bc42e6feb9010ced905ded51c5afc4
environments/surveys/summary/no_responses_found: f10190cffdda4ca1bed479acbb89b13f environments/surveys/summary/no_responses_found: f10190cffdda4ca1bed479acbb89b13f
environments/surveys/summary/other_values_found: 48a74ee68c05f7fb162072b50c683b6a environments/surveys/summary/other_values_found: 48a74ee68c05f7fb162072b50c683b6a
environments/surveys/summary/overall: 6c6d6533013d4739766af84b2871bca6 environments/surveys/summary/overall: 6c6d6533013d4739766af84b2871bca6
@@ -1934,7 +1908,6 @@ checksums:
environments/surveys/summary/starts: 3153990a4ade414f501a7e63ab771362 environments/surveys/summary/starts: 3153990a4ade414f501a7e63ab771362
environments/surveys/summary/starts_tooltip: 0a7dd01320490dbbea923053fa1ccad6 environments/surveys/summary/starts_tooltip: 0a7dd01320490dbbea923053fa1ccad6
environments/surveys/summary/survey_reset_successfully: f53db36a28980ef4766215cf13f01e51 environments/surveys/summary/survey_reset_successfully: f53db36a28980ef4766215cf13f01e51
environments/surveys/summary/survey_results: b7d86f636beaee2b4d5746bdda058d07
environments/surveys/summary/this_month: 50845a38865204a97773c44dcd2ebb90 environments/surveys/summary/this_month: 50845a38865204a97773c44dcd2ebb90
environments/surveys/summary/this_quarter: 9c77d94783dff2269c069389122cd7bd environments/surveys/summary/this_quarter: 9c77d94783dff2269c069389122cd7bd
environments/surveys/summary/this_year: 1e69651c2ac722f8ce138f43cf2e02f9 environments/surveys/summary/this_year: 1e69651c2ac722f8ce138f43cf2e02f9
@@ -2040,7 +2013,7 @@ checksums:
environments/workspace/look/add_background_color_description: adb6fcb392862b3d0e9420d9b5405ddb environments/workspace/look/add_background_color_description: adb6fcb392862b3d0e9420d9b5405ddb
environments/workspace/look/advanced_styling_field_border_radius: 63b8f3541a9792d705e67d5aca7b6451 environments/workspace/look/advanced_styling_field_border_radius: 63b8f3541a9792d705e67d5aca7b6451
environments/workspace/look/advanced_styling_field_button_bg: fc103ab926721e6213d39cc1f913c018 environments/workspace/look/advanced_styling_field_button_bg: fc103ab926721e6213d39cc1f913c018
environments/workspace/look/advanced_styling_field_button_bg_description: b8195c91717d8ba6e14cd83396caebe5 environments/workspace/look/advanced_styling_field_button_bg_description: 9f14ec79ed40c0d3eb168cc46a9e0a14
environments/workspace/look/advanced_styling_field_button_border_radius_description: 5677ee84511896ab9c369c0aced4c352 environments/workspace/look/advanced_styling_field_button_border_radius_description: 5677ee84511896ab9c369c0aced4c352
environments/workspace/look/advanced_styling_field_button_font_size_description: 59508854b0101a89fab8250f79c0f3ba environments/workspace/look/advanced_styling_field_button_font_size_description: 59508854b0101a89fab8250f79c0f3ba
environments/workspace/look/advanced_styling_field_button_font_weight_description: d3dab571b0f1bc09d645be66c6686a06 environments/workspace/look/advanced_styling_field_button_font_weight_description: d3dab571b0f1bc09d645be66c6686a06
@@ -2068,7 +2041,7 @@ checksums:
environments/workspace/look/advanced_styling_field_indicator_bg_description: 7eb3b54a8b331354ec95c0dc1545c620 environments/workspace/look/advanced_styling_field_indicator_bg_description: 7eb3b54a8b331354ec95c0dc1545c620
environments/workspace/look/advanced_styling_field_input_border_radius_description: 0007f1bb572b35d9a3720daeb7a55617 environments/workspace/look/advanced_styling_field_input_border_radius_description: 0007f1bb572b35d9a3720daeb7a55617
environments/workspace/look/advanced_styling_field_input_font_size_description: 5311f95dcbd083623e35c98ea5374c3b environments/workspace/look/advanced_styling_field_input_font_size_description: 5311f95dcbd083623e35c98ea5374c3b
environments/workspace/look/advanced_styling_field_input_height_description: eff99285b2ce1141c3c9743f7c96f63c environments/workspace/look/advanced_styling_field_input_height_description: e19ec0dc432478def0fd1199ad765e38
environments/workspace/look/advanced_styling_field_input_padding_x_description: 10e14296468321c13fda77fd1ba58dfd environments/workspace/look/advanced_styling_field_input_padding_x_description: 10e14296468321c13fda77fd1ba58dfd
environments/workspace/look/advanced_styling_field_input_padding_y_description: 98b4aeff2940516d05ea61bdc1211d0d environments/workspace/look/advanced_styling_field_input_padding_y_description: 98b4aeff2940516d05ea61bdc1211d0d
environments/workspace/look/advanced_styling_field_input_placeholder_opacity_description: f55a6700884d24014404e58876121ddf environments/workspace/look/advanced_styling_field_input_placeholder_opacity_description: f55a6700884d24014404e58876121ddf
@@ -2077,8 +2050,6 @@ checksums:
environments/workspace/look/advanced_styling_field_input_text_description: 460450df24ea0cc902710118a5000feb environments/workspace/look/advanced_styling_field_input_text_description: 460450df24ea0cc902710118a5000feb
environments/workspace/look/advanced_styling_field_option_bg: 0ceaed10d99ed4ad83cb0934ab970174 environments/workspace/look/advanced_styling_field_option_bg: 0ceaed10d99ed4ad83cb0934ab970174
environments/workspace/look/advanced_styling_field_option_bg_description: 6cd6ccecbbb9f2f19439d7c682eb67c1 environments/workspace/look/advanced_styling_field_option_bg_description: 6cd6ccecbbb9f2f19439d7c682eb67c1
environments/workspace/look/advanced_styling_field_option_border: aa478eb148515b6a2637fb144ff72028
environments/workspace/look/advanced_styling_field_option_border_description: 8f75b740e8dcb7f6cfeff2e5d5ca7c92
environments/workspace/look/advanced_styling_field_option_border_radius_description: 23f81c25b2681a7c9e2c4f2e7d2e0656 environments/workspace/look/advanced_styling_field_option_border_radius_description: 23f81c25b2681a7c9e2c4f2e7d2e0656
environments/workspace/look/advanced_styling_field_option_font_size_description: 5430fd9b08819972f0a613bf3fa659da environments/workspace/look/advanced_styling_field_option_font_size_description: 5430fd9b08819972f0a613bf3fa659da
environments/workspace/look/advanced_styling_field_option_label: 2767a5db32742073a01aac16488e93dc environments/workspace/look/advanced_styling_field_option_label: 2767a5db32742073a01aac16488e93dc
@@ -2133,7 +2104,7 @@ checksums:
environments/workspace/look/show_powered_by_formbricks: a0e96edadec8ef326423feccc9d06be7 environments/workspace/look/show_powered_by_formbricks: a0e96edadec8ef326423feccc9d06be7
environments/workspace/look/styling_updated_successfully: b8b74b50dde95abcd498633e9d0c891f environments/workspace/look/styling_updated_successfully: b8b74b50dde95abcd498633e9d0c891f
environments/workspace/look/suggest_colors: ddc4543b416ab774007b10a3434343cd environments/workspace/look/suggest_colors: ddc4543b416ab774007b10a3434343cd
environments/workspace/look/suggested_colors_applied_please_save: a440b8e29a327822a94d9bbf8c52e2ed environments/workspace/look/suggested_colors_applied_please_save: 226fa70af5efc8ffa0a3755909c8163e
environments/workspace/look/theme: 21fe00b7a518089576fb83c08631107a environments/workspace/look/theme: 21fe00b7a518089576fb83c08631107a
environments/workspace/look/theme_settings_description: 9fc45322818c3774ab4a44ea14d7836e environments/workspace/look/theme_settings_description: 9fc45322818c3774ab4a44ea14d7836e
environments/workspace/tags/add: 87c4a663507f2bcbbf79934af8164e13 environments/workspace/tags/add: 87c4a663507f2bcbbf79934af8164e13
@@ -2193,7 +2164,7 @@ checksums:
s/check_inbox_or_spam: c48ac1f7b76052881bb3b6d10615152d s/check_inbox_or_spam: c48ac1f7b76052881bb3b6d10615152d
s/completed: 98a9cd97b409933edf1991e7d022bea9 s/completed: 98a9cd97b409933edf1991e7d022bea9
s/create_your_own: 27976ec69029d6dd52d146a9b5765bc6 s/create_your_own: 27976ec69029d6dd52d146a9b5765bc6
s/enter_pin: 90bccdc2e51ee2550842287d1f02c999 s/enter_pin: 1d902362d8063ca1442bebabaab5115b
s/just_curious: d5e5c40e97fcfdab563707ab0de10862 s/just_curious: d5e5c40e97fcfdab563707ab0de10862
s/link_invalid: bd63a73fa5eecad2dfd0687cfed02114 s/link_invalid: bd63a73fa5eecad2dfd0687cfed02114
s/paused: 65ec150837ca033f5a0fb5d4482e0e4b s/paused: 65ec150837ca033f5a0fb5d4482e0e4b
@@ -2250,16 +2221,6 @@ checksums:
templates/alignment_and_engagement_survey_question_4_headline: e36be56ce8aad1d0ca04939bea4e39b7 templates/alignment_and_engagement_survey_question_4_headline: e36be56ce8aad1d0ca04939bea4e39b7
templates/alignment_and_engagement_survey_question_4_placeholder: 37ee9c84f3777b9220d4faec1e1c78ee templates/alignment_and_engagement_survey_question_4_placeholder: 37ee9c84f3777b9220d4faec1e1c78ee
templates/back: f541015a827e37cb3b1234e56bc2aa3c templates/back: f541015a827e37cb3b1234e56bc2aa3c
templates/block_1: 5e1b4dce0cb70662441b663507a69454
templates/block_10: 09a42e99b34b45700e734730acfe37ed
templates/block_2: f50d8aab8b44f168a2ab00526d4f9a2c
templates/block_3: 78d84f8e4763a95710543c5368ce8a41
templates/block_4: 2c346374f245a6821940c061b855ac69
templates/block_5: 975abfc66e8e377478ff691a040dda0b
templates/block_6: 2bd10f1edb210243c5ab459c59e02d30
templates/block_7: 13f0f680c09c96081e125123ad2f6786
templates/block_8: 1be1b18e159e8c8d11d2fb1082ea5d98
templates/block_9: 2da3894d05e4415fa043ba18d11d60e2
templates/book_interview: 1cc9c72d1c088b28e5dfa5ec7d7b78c4 templates/book_interview: 1cc9c72d1c088b28e5dfa5ec7d7b78c4
templates/build_product_roadmap_description: 6ca163ed3b0095cedcbc11822a0d502a templates/build_product_roadmap_description: 6ca163ed3b0095cedcbc11822a0d502a
templates/build_product_roadmap_name: 8c216b183c3539c0340ce87465a391cc templates/build_product_roadmap_name: 8c216b183c3539c0340ce87465a391cc
@@ -2415,7 +2376,7 @@ checksums:
templates/csat_question_3_choice_8: 6abbde1174a09d7b9d61de382f7f9677 templates/csat_question_3_choice_8: 6abbde1174a09d7b9d61de382f7f9677
templates/csat_question_3_choice_9: 4c3429e68c6fbd9a8808285838054e0c templates/csat_question_3_choice_9: 4c3429e68c6fbd9a8808285838054e0c
templates/csat_question_3_headline: 8c6ffa4d4c0fef480bf22f61dbea5ce6 templates/csat_question_3_headline: 8c6ffa4d4c0fef480bf22f61dbea5ce6
templates/csat_question_3_subheader: ed43eb9723166fd6a8d8e44356def97e templates/csat_question_3_subheader: 8169b8e0ff6958f3d617b397f991f7af
templates/csat_question_4_choice_1: 3af1ce39d4f133a26c4272c8245ded07 templates/csat_question_4_choice_1: 3af1ce39d4f133a26c4272c8245ded07
templates/csat_question_4_choice_2: 78f2c2b121ffbb4633452f7da4e9c338 templates/csat_question_4_choice_2: 78f2c2b121ffbb4633452f7da4e9c338
templates/csat_question_4_choice_3: 315f0d4c1497f69ef252d86e486fc715 templates/csat_question_4_choice_3: 315f0d4c1497f69ef252d86e486fc715
@@ -2467,6 +2428,7 @@ checksums:
templates/csat_survey_question_3_headline: 25974b7f1692cad41908fe305830b6c0 templates/csat_survey_question_3_headline: 25974b7f1692cad41908fe305830b6c0
templates/csat_survey_question_3_placeholder: 37ee9c84f3777b9220d4faec1e1c78ee templates/csat_survey_question_3_placeholder: 37ee9c84f3777b9220d4faec1e1c78ee
templates/cta_description: bc94a2ddc965b286a8677b0642696c7e templates/cta_description: bc94a2ddc965b286a8677b0642696c7e
templates/custom_survey_block_1_name: 5e1b4dce0cb70662441b663507a69454
templates/custom_survey_description: 0492afdea2ef1bd683eaf48a2bad2caa templates/custom_survey_description: 0492afdea2ef1bd683eaf48a2bad2caa
templates/custom_survey_name: 6fc756927ca9ea22c26368cccd64a67e templates/custom_survey_name: 6fc756927ca9ea22c26368cccd64a67e
templates/custom_survey_question_1_headline: 0abf9d41e0b5c5567c3833fd63048398 templates/custom_survey_question_1_headline: 0abf9d41e0b5c5567c3833fd63048398
@@ -2869,9 +2831,6 @@ checksums:
templates/preview_survey_question_2_choice_2_label: 1af148222f327f28cf0db6513de5989e templates/preview_survey_question_2_choice_2_label: 1af148222f327f28cf0db6513de5989e
templates/preview_survey_question_2_headline: 5cfb173d156555227fbc2c97ad921e72 templates/preview_survey_question_2_headline: 5cfb173d156555227fbc2c97ad921e72
templates/preview_survey_question_2_subheader: 2e652d8acd68d072e5a0ae686c4011c0 templates/preview_survey_question_2_subheader: 2e652d8acd68d072e5a0ae686c4011c0
templates/preview_survey_question_open_text_headline: a9509a47e0456ae98ec3ddac3d6fad2c
templates/preview_survey_question_open_text_placeholder: 37ee9c84f3777b9220d4faec1e1c78ee
templates/preview_survey_question_open_text_subheader: 3c7bf09f3f17b02bc2fbbbdb347a5830
templates/preview_survey_welcome_card_headline: 8778dc41547a2778d0f9482da989fc00 templates/preview_survey_welcome_card_headline: 8778dc41547a2778d0f9482da989fc00
templates/prioritize_features_description: 1eae41fad0e3947f803d8539081e59ec templates/prioritize_features_description: 1eae41fad0e3947f803d8539081e59ec
templates/prioritize_features_name: 4ca59ff1f9c319aaa68c3106d820fd6a templates/prioritize_features_name: 4ca59ff1f9c319aaa68c3106d820fd6a
@@ -2941,7 +2900,7 @@ checksums:
templates/professional_development_survey_question_2_choice_5: cc3b193ad5efcdd4f088745011d8b914 templates/professional_development_survey_question_2_choice_5: cc3b193ad5efcdd4f088745011d8b914
templates/professional_development_survey_question_2_choice_6: 79acaa6cd481262bea4e743a422529d2 templates/professional_development_survey_question_2_choice_6: 79acaa6cd481262bea4e743a422529d2
templates/professional_development_survey_question_2_headline: b561b69cd31ac95fbf8238534d5aadfc templates/professional_development_survey_question_2_headline: b561b69cd31ac95fbf8238534d5aadfc
templates/professional_development_survey_question_2_subheader: ed43eb9723166fd6a8d8e44356def97e templates/professional_development_survey_question_2_subheader: a660423e16f78ad344493388602ac7a7
templates/professional_development_survey_question_3_choice_1: ec580fd11a45779b039466f1e35eed2a templates/professional_development_survey_question_3_choice_1: ec580fd11a45779b039466f1e35eed2a
templates/professional_development_survey_question_3_choice_2: 8c708225830b06df2d1141c536f2a0d6 templates/professional_development_survey_question_3_choice_2: 8c708225830b06df2d1141c536f2a0d6
templates/professional_development_survey_question_3_headline: 6cdb6730878966a48bab260aa5da9973 templates/professional_development_survey_question_3_headline: 6cdb6730878966a48bab260aa5da9973
@@ -3120,14 +3079,3 @@ checksums:
templates/usability_question_9_headline: 5850229e97ae97698ce90b330ea49682 templates/usability_question_9_headline: 5850229e97ae97698ce90b330ea49682
templates/usability_rating_description: 8c4f3818fe830ae544611f816265f1a1 templates/usability_rating_description: 8c4f3818fe830ae544611f816265f1a1
templates/usability_score_name: 5cbf1172d24dfcb17d979dff6dfdf7e2 templates/usability_score_name: 5cbf1172d24dfcb17d979dff6dfdf7e2
workflows/coming_soon_description: 1e0621d287924d84fb539afab7372b23
workflows/coming_soon_title: d79be80559c70c828cf20811d2ed5039
workflows/follow_up_label: 8cafe669370271035aeac8e8cab0f123
workflows/follow_up_placeholder: f680918bec28192282e229c3d4b5e80a
workflows/generate_button: b194b6172a49af8374a19dd2cf39cfdc
workflows/heading: a98a6b14d3e955f38cc16386df9a4111
workflows/placeholder: f5d943582bf25e8734930844e598457b
workflows/subheading: ebf5e3b3aeb85e13e843358cc5476f42
workflows/submit_button: 7a062f2de02ce60b1d73e510ff1ca094
workflows/thank_you_description: 7623c1ba4f059c8d9e68aae3360b20b1
workflows/thank_you_title: 07edd8c50685a52c0969d711df26d768
+2 -3
View File
@@ -63,8 +63,7 @@ export const INVITE_DISABLED = env.INVITE_DISABLED === "1";
export const SLACK_CLIENT_SECRET = env.SLACK_CLIENT_SECRET; export const SLACK_CLIENT_SECRET = env.SLACK_CLIENT_SECRET;
export const SLACK_CLIENT_ID = env.SLACK_CLIENT_ID; export const SLACK_CLIENT_ID = env.SLACK_CLIENT_ID;
export const SLACK_REDIRECT_URI = `${WEBAPP_URL}/api/v1/integrations/slack/callback`; export const SLACK_AUTH_URL = `https://slack.com/oauth/v2/authorize?client_id=${env.SLACK_CLIENT_ID}&scope=channels:read,chat:write,chat:write.public,chat:write.customize,groups:read`;
export const SLACK_AUTH_URL = `https://slack.com/oauth/v2/authorize?client_id=${env.SLACK_CLIENT_ID}&scope=channels:read,chat:write,chat:write.public,chat:write.customize,groups:read&redirect_uri=${SLACK_REDIRECT_URI}`;
export const GOOGLE_SHEETS_CLIENT_ID = env.GOOGLE_SHEETS_CLIENT_ID; export const GOOGLE_SHEETS_CLIENT_ID = env.GOOGLE_SHEETS_CLIENT_ID;
export const GOOGLE_SHEETS_CLIENT_SECRET = env.GOOGLE_SHEETS_CLIENT_SECRET; export const GOOGLE_SHEETS_CLIENT_SECRET = env.GOOGLE_SHEETS_CLIENT_SECRET;
@@ -159,7 +158,7 @@ export const BREVO_LIST_ID = env.BREVO_LIST_ID;
export const UNSPLASH_ACCESS_KEY = env.UNSPLASH_ACCESS_KEY; export const UNSPLASH_ACCESS_KEY = env.UNSPLASH_ACCESS_KEY;
export const UNSPLASH_ALLOWED_DOMAINS = ["api.unsplash.com"]; export const UNSPLASH_ALLOWED_DOMAINS = ["api.unsplash.com"];
export const STRIPE_API_VERSION = "2026-02-25.clover"; export const STRIPE_API_VERSION = "2024-06-20";
// Maximum number of attribute classes allowed: // Maximum number of attribute classes allowed:
export const MAX_ATTRIBUTE_CLASSES_PER_ENVIRONMENT = 150; export const MAX_ATTRIBUTE_CLASSES_PER_ENVIRONMENT = 150;
+8 -99
View File
@@ -1,10 +1,9 @@
import "server-only"; import "server-only";
import { Prisma } from "@prisma/client"; import { Prisma } from "@prisma/client";
import { cache as reactCache } from "react"; import { cache as reactCache } from "react";
import { z } from "zod";
import { prisma } from "@formbricks/database"; import { prisma } from "@formbricks/database";
import { ZId } from "@formbricks/types/common"; import { ZId } from "@formbricks/types/common";
import { TDisplay, TDisplayFilters, TDisplayWithContact } from "@formbricks/types/displays"; import { TDisplay, TDisplayFilters } from "@formbricks/types/displays";
import { DatabaseError } from "@formbricks/types/errors"; import { DatabaseError } from "@formbricks/types/errors";
import { validateInputs } from "../utils/validate"; import { validateInputs } from "../utils/validate";
@@ -24,12 +23,13 @@ export const getDisplayCountBySurveyId = reactCache(
const displayCount = await prisma.display.count({ const displayCount = await prisma.display.count({
where: { where: {
surveyId: surveyId, surveyId: surveyId,
...(filters?.createdAt && { ...(filters &&
createdAt: { filters.createdAt && {
gte: filters.createdAt.min, createdAt: {
lte: filters.createdAt.max, gte: filters.createdAt.min,
}, lte: filters.createdAt.max,
}), },
}),
}, },
}); });
return displayCount; return displayCount;
@@ -42,97 +42,6 @@ export const getDisplayCountBySurveyId = reactCache(
} }
); );
export const getDisplaysByContactId = reactCache(
async (contactId: string): Promise<Pick<TDisplay, "id" | "createdAt" | "surveyId">[]> => {
validateInputs([contactId, ZId]);
try {
const displays = await prisma.display.findMany({
where: { contactId },
select: {
id: true,
createdAt: true,
surveyId: true,
},
orderBy: { createdAt: "desc" },
});
return displays;
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
throw new DatabaseError(error.message);
}
throw error;
}
}
);
export const getDisplaysBySurveyIdWithContact = reactCache(
async (surveyId: string, limit?: number, offset?: number): Promise<TDisplayWithContact[]> => {
validateInputs(
[surveyId, ZId],
[limit, z.int().min(1).optional()],
[offset, z.int().nonnegative().optional()]
);
try {
const displays = await prisma.display.findMany({
where: {
surveyId,
contactId: { not: null },
},
select: {
id: true,
createdAt: true,
surveyId: true,
contact: {
select: {
id: true,
attributes: {
where: {
attributeKey: {
key: { in: ["email", "userId"] },
},
},
select: {
attributeKey: { select: { key: true } },
value: true,
},
},
},
},
},
orderBy: { createdAt: "desc" },
take: limit,
skip: offset,
});
return displays.map((display) => ({
id: display.id,
createdAt: display.createdAt,
surveyId: display.surveyId,
contact: display.contact
? {
id: display.contact.id,
attributes: display.contact.attributes.reduce(
(acc, attr) => {
acc[attr.attributeKey.key] = attr.value;
return acc;
},
{} as Record<string, string>
),
}
: null,
}));
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
throw new DatabaseError(error.message);
}
throw error;
}
}
);
export const deleteDisplay = async (displayId: string, tx?: Prisma.TransactionClient): Promise<TDisplay> => { export const deleteDisplay = async (displayId: string, tx?: Prisma.TransactionClient): Promise<TDisplay> => {
validateInputs([displayId, ZId]); validateInputs([displayId, ZId]);
try { try {
@@ -1,219 +0,0 @@
import { mockDisplayId, mockSurveyId } from "./__mocks__/data.mock";
import { prisma } from "@/lib/__mocks__/database";
import { Prisma } from "@prisma/client";
import { describe, expect, test, vi } from "vitest";
import { PrismaErrorType } from "@formbricks/database/types/error";
import { DatabaseError, ValidationError } from "@formbricks/types/errors";
import { getDisplaysByContactId, getDisplaysBySurveyIdWithContact } from "../service";
const mockContactId = "clqnj99r9000008lebgf8734j";
const mockDisplaysForContact = [
{
id: mockDisplayId,
createdAt: new Date("2024-01-15T10:00:00Z"),
surveyId: mockSurveyId,
},
{
id: "clqkr5smu000208jy50v6g5k5",
createdAt: new Date("2024-01-14T10:00:00Z"),
surveyId: "clqkr8dlv000308jybb08evgs",
},
];
const mockDisplaysWithContact = [
{
id: mockDisplayId,
createdAt: new Date("2024-01-15T10:00:00Z"),
surveyId: mockSurveyId,
contact: {
id: mockContactId,
attributes: [
{ attributeKey: { key: "email" }, value: "test@example.com" },
{ attributeKey: { key: "userId" }, value: "user-123" },
],
},
},
{
id: "clqkr5smu000208jy50v6g5k5",
createdAt: new Date("2024-01-14T10:00:00Z"),
surveyId: "clqkr8dlv000308jybb08evgs",
contact: {
id: "clqnj99r9000008lebgf8734k",
attributes: [{ attributeKey: { key: "userId" }, value: "user-456" }],
},
},
];
describe("getDisplaysByContactId", () => {
describe("Happy Path", () => {
test("returns displays for a contact ordered by createdAt desc", async () => {
vi.mocked(prisma.display.findMany).mockResolvedValue(mockDisplaysForContact as any);
const result = await getDisplaysByContactId(mockContactId);
expect(result).toEqual(mockDisplaysForContact);
expect(prisma.display.findMany).toHaveBeenCalledWith({
where: { contactId: mockContactId },
select: {
id: true,
createdAt: true,
surveyId: true,
},
orderBy: { createdAt: "desc" },
});
});
test("returns empty array when contact has no displays", async () => {
vi.mocked(prisma.display.findMany).mockResolvedValue([]);
const result = await getDisplaysByContactId(mockContactId);
expect(result).toEqual([]);
});
});
describe("Sad Path", () => {
test("throws a ValidationError if the contactId is invalid", async () => {
await expect(getDisplaysByContactId("not-a-cuid")).rejects.toThrow(ValidationError);
});
test("throws DatabaseError on PrismaClientKnownRequestError", async () => {
const errToThrow = new Prisma.PrismaClientKnownRequestError("Mock error", {
code: PrismaErrorType.UniqueConstraintViolation,
clientVersion: "0.0.1",
});
vi.mocked(prisma.display.findMany).mockRejectedValue(errToThrow);
await expect(getDisplaysByContactId(mockContactId)).rejects.toThrow(DatabaseError);
});
test("throws generic Error for other exceptions", async () => {
vi.mocked(prisma.display.findMany).mockRejectedValue(new Error("Mock error"));
await expect(getDisplaysByContactId(mockContactId)).rejects.toThrow(Error);
});
});
});
describe("getDisplaysBySurveyIdWithContact", () => {
describe("Happy Path", () => {
test("returns displays with contact attributes transformed", async () => {
vi.mocked(prisma.display.findMany).mockResolvedValue(mockDisplaysWithContact as any);
const result = await getDisplaysBySurveyIdWithContact(mockSurveyId, 15, 0);
expect(result).toEqual([
{
id: mockDisplayId,
createdAt: new Date("2024-01-15T10:00:00Z"),
surveyId: mockSurveyId,
contact: {
id: mockContactId,
attributes: { email: "test@example.com", userId: "user-123" },
},
},
{
id: "clqkr5smu000208jy50v6g5k5",
createdAt: new Date("2024-01-14T10:00:00Z"),
surveyId: "clqkr8dlv000308jybb08evgs",
contact: {
id: "clqnj99r9000008lebgf8734k",
attributes: { userId: "user-456" },
},
},
]);
});
test("calls prisma with correct where clause and pagination", async () => {
vi.mocked(prisma.display.findMany).mockResolvedValue([]);
await getDisplaysBySurveyIdWithContact(mockSurveyId, 15, 0);
expect(prisma.display.findMany).toHaveBeenCalledWith({
where: {
surveyId: mockSurveyId,
contactId: { not: null },
},
select: {
id: true,
createdAt: true,
surveyId: true,
contact: {
select: {
id: true,
attributes: {
where: {
attributeKey: {
key: { in: ["email", "userId"] },
},
},
select: {
attributeKey: { select: { key: true } },
value: true,
},
},
},
},
},
orderBy: { createdAt: "desc" },
take: 15,
skip: 0,
});
});
test("returns empty array when no displays found", async () => {
vi.mocked(prisma.display.findMany).mockResolvedValue([]);
const result = await getDisplaysBySurveyIdWithContact(mockSurveyId);
expect(result).toEqual([]);
});
test("handles display with null contact", async () => {
vi.mocked(prisma.display.findMany).mockResolvedValue([
{
id: mockDisplayId,
createdAt: new Date("2024-01-15T10:00:00Z"),
surveyId: mockSurveyId,
contact: null,
},
] as any);
const result = await getDisplaysBySurveyIdWithContact(mockSurveyId);
expect(result).toEqual([
{
id: mockDisplayId,
createdAt: new Date("2024-01-15T10:00:00Z"),
surveyId: mockSurveyId,
contact: null,
},
]);
});
});
describe("Sad Path", () => {
test("throws a ValidationError if the surveyId is invalid", async () => {
await expect(getDisplaysBySurveyIdWithContact("not-a-cuid")).rejects.toThrow(ValidationError);
});
test("throws DatabaseError on PrismaClientKnownRequestError", async () => {
const errToThrow = new Prisma.PrismaClientKnownRequestError("Mock error", {
code: PrismaErrorType.UniqueConstraintViolation,
clientVersion: "0.0.1",
});
vi.mocked(prisma.display.findMany).mockRejectedValue(errToThrow);
await expect(getDisplaysBySurveyIdWithContact(mockSurveyId)).rejects.toThrow(DatabaseError);
});
test("throws generic Error for other exceptions", async () => {
vi.mocked(prisma.display.findMany).mockRejectedValue(new Error("Mock error"));
await expect(getDisplaysBySurveyIdWithContact(mockSurveyId)).rejects.toThrow(Error);
});
});
});
+14 -10
View File
@@ -14,7 +14,7 @@ export const env = createEnv({
CRON_SECRET: z.string().optional(), CRON_SECRET: z.string().optional(),
BREVO_API_KEY: z.string().optional(), BREVO_API_KEY: z.string().optional(),
BREVO_LIST_ID: z.string().optional(), BREVO_LIST_ID: z.string().optional(),
DATABASE_URL: z.url(), DATABASE_URL: z.string().url(),
DEBUG: z.enum(["1", "0"]).optional(), DEBUG: z.enum(["1", "0"]).optional(),
AUTH_DEFAULT_TEAM_ID: z.string().optional(), AUTH_DEFAULT_TEAM_ID: z.string().optional(),
AUTH_SKIP_INVITE_FOR_SSO: z.enum(["1", "0"]).optional(), AUTH_SKIP_INVITE_FOR_SSO: z.enum(["1", "0"]).optional(),
@@ -23,7 +23,7 @@ export const env = createEnv({
EMAIL_VERIFICATION_DISABLED: z.enum(["1", "0"]).optional(), EMAIL_VERIFICATION_DISABLED: z.enum(["1", "0"]).optional(),
ENCRYPTION_KEY: z.string(), ENCRYPTION_KEY: z.string(),
ENTERPRISE_LICENSE_KEY: z.string().optional(), ENTERPRISE_LICENSE_KEY: z.string().optional(),
ENVIRONMENT: z.enum(["production", "staging"]).prefault("production"), ENVIRONMENT: z.enum(["production", "staging"]).default("production"),
GITHUB_ID: z.string().optional(), GITHUB_ID: z.string().optional(),
GITHUB_SECRET: z.string().optional(), GITHUB_SECRET: z.string().optional(),
GOOGLE_CLIENT_ID: z.string().optional(), GOOGLE_CLIENT_ID: z.string().optional(),
@@ -31,20 +31,21 @@ export const env = createEnv({
GOOGLE_SHEETS_CLIENT_ID: z.string().optional(), GOOGLE_SHEETS_CLIENT_ID: z.string().optional(),
GOOGLE_SHEETS_CLIENT_SECRET: z.string().optional(), GOOGLE_SHEETS_CLIENT_SECRET: z.string().optional(),
GOOGLE_SHEETS_REDIRECT_URL: z.string().optional(), GOOGLE_SHEETS_REDIRECT_URL: z.string().optional(),
HTTP_PROXY: z.url().optional(), HTTP_PROXY: z.string().url().optional(),
HTTPS_PROXY: z.url().optional(), HTTPS_PROXY: z.string().url().optional(),
IMPRINT_URL: z IMPRINT_URL: z
.string()
.url() .url()
.optional() .optional()
.or(z.string().refine((str) => str === "")), .or(z.string().refine((str) => str === "")),
IMPRINT_ADDRESS: z.string().optional(), IMPRINT_ADDRESS: z.string().optional(),
INVITE_DISABLED: z.enum(["1", "0"]).optional(), INVITE_DISABLED: z.enum(["1", "0"]).optional(),
CHATWOOT_WEBSITE_TOKEN: z.string().optional(), CHATWOOT_WEBSITE_TOKEN: z.string().optional(),
CHATWOOT_BASE_URL: z.url().optional(), CHATWOOT_BASE_URL: z.string().url().optional(),
IS_FORMBRICKS_CLOUD: z.enum(["1", "0"]).optional(), IS_FORMBRICKS_CLOUD: z.enum(["1", "0"]).optional(),
LOG_LEVEL: z.enum(["debug", "info", "warn", "error", "fatal"]).optional(), LOG_LEVEL: z.enum(["debug", "info", "warn", "error", "fatal"]).optional(),
MAIL_FROM: z.email().optional(), MAIL_FROM: z.string().email().optional(),
NEXTAUTH_URL: z.url().optional(), NEXTAUTH_URL: z.string().url().optional(),
NEXTAUTH_SECRET: z.string().optional(), NEXTAUTH_SECRET: z.string().optional(),
MAIL_FROM_NAME: z.string().optional(), MAIL_FROM_NAME: z.string().optional(),
NOTION_OAUTH_CLIENT_ID: z.string().optional(), NOTION_OAUTH_CLIENT_ID: z.string().optional(),
@@ -57,9 +58,10 @@ export const env = createEnv({
REDIS_URL: REDIS_URL:
process.env.NODE_ENV === "test" process.env.NODE_ENV === "test"
? z.string().optional() ? z.string().optional()
: z.url("REDIS_URL is required for caching, rate limiting, and audit logging"), : z.string().url("REDIS_URL is required for caching, rate limiting, and audit logging"),
PASSWORD_RESET_DISABLED: z.enum(["1", "0"]).optional(), PASSWORD_RESET_DISABLED: z.enum(["1", "0"]).optional(),
PRIVACY_URL: z PRIVACY_URL: z
.string()
.url() .url()
.optional() .optional()
.or(z.string().refine((str) => str === "")), .or(z.string().refine((str) => str === "")),
@@ -84,6 +86,7 @@ export const env = createEnv({
STRIPE_SECRET_KEY: z.string().optional(), STRIPE_SECRET_KEY: z.string().optional(),
STRIPE_WEBHOOK_SECRET: z.string().optional(), STRIPE_WEBHOOK_SECRET: z.string().optional(),
PUBLIC_URL: z PUBLIC_URL: z
.string()
.url() .url()
.refine( .refine(
(url) => { (url) => {
@@ -95,11 +98,12 @@ export const env = createEnv({
} }
}, },
{ {
error: "PUBLIC_URL must be a valid URL with a proper host (e.g., https://example.com)", message: "PUBLIC_URL must be a valid URL with a proper host (e.g., https://example.com)",
} }
) )
.optional(), .optional(),
TERMS_URL: z TERMS_URL: z
.string()
.url() .url()
.optional() .optional()
.or(z.string().refine((str) => str === "")), .or(z.string().refine((str) => str === "")),
@@ -108,7 +112,7 @@ export const env = createEnv({
RECAPTCHA_SITE_KEY: z.string().optional(), RECAPTCHA_SITE_KEY: z.string().optional(),
RECAPTCHA_SECRET_KEY: z.string().optional(), RECAPTCHA_SECRET_KEY: z.string().optional(),
VERCEL_URL: z.string().optional(), VERCEL_URL: z.string().optional(),
WEBAPP_URL: z.url().optional(), WEBAPP_URL: z.string().url().optional(),
UNSPLASH_ACCESS_KEY: z.string().optional(), UNSPLASH_ACCESS_KEY: z.string().optional(),
NODE_ENV: z.enum(["development", "production", "test"]).optional(), NODE_ENV: z.enum(["development", "production", "test"]).optional(),
+26 -48
View File
@@ -1,66 +1,44 @@
import { beforeEach, describe, expect, test, vi } from "vitest"; import { beforeEach, describe, expect, test, vi } from "vitest";
// Mock constants module
const envMock = { const envMock = {
WEBAPP_URL: undefined as string | undefined, env: {
VERCEL_URL: undefined as string | undefined, WEBAPP_URL: "http://localhost:3000",
PUBLIC_URL: undefined as string | undefined, PUBLIC_URL: undefined as string | undefined,
},
}; };
vi.mock("./env", () => ({ vi.mock("@/lib/env", () => envMock);
env: envMock,
}));
const loadGetPublicDomain = async () => {
vi.resetModules();
const { getPublicDomain } = await import("./getPublicUrl");
return getPublicDomain;
};
describe("getPublicDomain", () => { describe("getPublicDomain", () => {
beforeEach(() => { beforeEach(() => {
envMock.WEBAPP_URL = undefined; vi.resetModules();
envMock.VERCEL_URL = undefined;
envMock.PUBLIC_URL = undefined;
}); });
test("returns trimmed WEBAPP_URL when configured", async () => { test("should return WEBAPP_URL when PUBLIC_URL is not set", async () => {
envMock.WEBAPP_URL = " https://app.formbricks.com "; const { getPublicDomain } = await import("./getPublicUrl");
const domain = getPublicDomain();
const getPublicDomain = await loadGetPublicDomain(); expect(domain).toBe("http://localhost:3000");
expect(getPublicDomain()).toBe("https://app.formbricks.com");
}); });
test("falls back to VERCEL_URL when WEBAPP_URL is empty", async () => { test("should return PUBLIC_URL when it is set", async () => {
envMock.WEBAPP_URL = " "; envMock.env.PUBLIC_URL = "https://surveys.example.com";
envMock.VERCEL_URL = "preview.formbricks.com"; const { getPublicDomain } = await import("./getPublicUrl");
const domain = getPublicDomain();
const getPublicDomain = await loadGetPublicDomain(); expect(domain).toBe("https://surveys.example.com");
expect(getPublicDomain()).toBe("https://preview.formbricks.com");
}); });
test("falls back to localhost when WEBAPP_URL and VERCEL_URL are not set", async () => { test("should handle empty string PUBLIC_URL by returning WEBAPP_URL", async () => {
const getPublicDomain = await loadGetPublicDomain(); envMock.env.PUBLIC_URL = "";
const { getPublicDomain } = await import("./getPublicUrl");
expect(getPublicDomain()).toBe("http://localhost:3000"); const domain = getPublicDomain();
expect(domain).toBe("http://localhost:3000");
}); });
test("returns PUBLIC_URL when set", async () => { test("should handle undefined PUBLIC_URL by returning WEBAPP_URL", async () => {
envMock.WEBAPP_URL = "https://app.formbricks.com"; envMock.env.PUBLIC_URL = undefined;
envMock.PUBLIC_URL = "https://surveys.formbricks.com"; const { getPublicDomain } = await import("./getPublicUrl");
const domain = getPublicDomain();
const getPublicDomain = await loadGetPublicDomain(); expect(domain).toBe("http://localhost:3000");
expect(getPublicDomain()).toBe("https://surveys.formbricks.com");
});
test("falls back to WEBAPP_URL when PUBLIC_URL is empty", async () => {
envMock.WEBAPP_URL = "https://app.formbricks.com";
envMock.PUBLIC_URL = " ";
const getPublicDomain = await loadGetPublicDomain();
expect(getPublicDomain()).toBe("https://app.formbricks.com");
}); });
}); });
+2 -12
View File
@@ -1,18 +1,8 @@
import "server-only"; import "server-only";
import { env } from "./env"; import { env } from "./env";
const configuredWebappUrl = env.WEBAPP_URL?.trim() ?? ""; const WEBAPP_URL =
const WEBAPP_URL = (() => { env.WEBAPP_URL ?? (env.VERCEL_URL ? `https://${env.VERCEL_URL}` : "") ?? "http://localhost:3000";
if (configuredWebappUrl !== "") {
return configuredWebappUrl;
}
if (env.VERCEL_URL) {
return `https://${env.VERCEL_URL}`;
}
return "http://localhost:3000";
})();
/** /**
* Returns the public domain URL * Returns the public domain URL
-6
View File
@@ -1,6 +0,0 @@
/**
* Error codes returned by Google Sheets integration.
* Use these constants when comparing error responses to avoid typos and enable reuse.
*/
export const GOOGLE_SHEET_INTEGRATION_INVALID_GRANT = "invalid_grant";
export const GOOGLE_SHEET_INTEGRATION_INSUFFICIENT_PERMISSION = "insufficient_permission";
+18 -92
View File
@@ -2,12 +2,7 @@ import "server-only";
import { Prisma } from "@prisma/client"; import { Prisma } from "@prisma/client";
import { z } from "zod"; import { z } from "zod";
import { ZString } from "@formbricks/types/common"; import { ZString } from "@formbricks/types/common";
import { import { DatabaseError, UnknownError } from "@formbricks/types/errors";
AuthenticationError,
DatabaseError,
OperationNotAllowedError,
UnknownError,
} from "@formbricks/types/errors";
import { import {
TIntegrationGoogleSheets, TIntegrationGoogleSheets,
ZIntegrationGoogleSheets, ZIntegrationGoogleSheets,
@@ -16,12 +11,8 @@ import {
GOOGLE_SHEETS_CLIENT_ID, GOOGLE_SHEETS_CLIENT_ID,
GOOGLE_SHEETS_CLIENT_SECRET, GOOGLE_SHEETS_CLIENT_SECRET,
GOOGLE_SHEETS_REDIRECT_URL, GOOGLE_SHEETS_REDIRECT_URL,
GOOGLE_SHEET_MESSAGE_LIMIT,
} from "@/lib/constants"; } from "@/lib/constants";
import { import { GOOGLE_SHEET_MESSAGE_LIMIT } from "@/lib/constants";
GOOGLE_SHEET_INTEGRATION_INSUFFICIENT_PERMISSION,
GOOGLE_SHEET_INTEGRATION_INVALID_GRANT,
} from "@/lib/googleSheet/constants";
import { createOrUpdateIntegration } from "@/lib/integration/service"; import { createOrUpdateIntegration } from "@/lib/integration/service";
import { truncateText } from "../utils/strings"; import { truncateText } from "../utils/strings";
import { validateInputs } from "../utils/validate"; import { validateInputs } from "../utils/validate";
@@ -90,17 +81,6 @@ export const writeData = async (
} }
}; };
export const validateGoogleSheetsConnection = async (
googleSheetIntegrationData: TIntegrationGoogleSheets
): Promise<void> => {
validateInputs([googleSheetIntegrationData, ZIntegrationGoogleSheets]);
const integrationData = structuredClone(googleSheetIntegrationData);
integrationData.config.data.forEach((data) => {
data.createdAt = new Date(data.createdAt);
});
await authorize(integrationData);
};
export const getSpreadsheetNameById = async ( export const getSpreadsheetNameById = async (
googleSheetIntegrationData: TIntegrationGoogleSheets, googleSheetIntegrationData: TIntegrationGoogleSheets,
spreadsheetId: string spreadsheetId: string
@@ -114,17 +94,7 @@ export const getSpreadsheetNameById = async (
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
sheets.spreadsheets.get({ spreadsheetId }, (err, response) => { sheets.spreadsheets.get({ spreadsheetId }, (err, response) => {
if (err) { if (err) {
const msg = err.message?.toLowerCase() ?? ""; reject(new UnknownError(`Error while fetching spreadsheet data: ${err.message}`));
const isPermissionError =
msg.includes("permission") ||
msg.includes("caller does not have") ||
msg.includes("insufficient permission") ||
msg.includes("access denied");
if (isPermissionError) {
reject(new OperationNotAllowedError(GOOGLE_SHEET_INTEGRATION_INSUFFICIENT_PERMISSION));
} else {
reject(new UnknownError(`Error while fetching spreadsheet data: ${err.message}`));
}
return; return;
} }
const spreadsheetTitle = response.data.properties.title; const spreadsheetTitle = response.data.properties.title;
@@ -139,70 +109,26 @@ export const getSpreadsheetNameById = async (
} }
}; };
const isInvalidGrantError = (error: unknown): boolean => {
const err = error as { message?: string; response?: { data?: { error?: string } } };
return (
typeof err?.message === "string" &&
err.message.toLowerCase().includes(GOOGLE_SHEET_INTEGRATION_INVALID_GRANT)
);
};
/** Buffer in ms before expiry_date to consider token near-expired (5 minutes). */
const TOKEN_EXPIRY_BUFFER_MS = 5 * 60 * 1000;
const GOOGLE_TOKENINFO_URL = "https://www.googleapis.com/oauth2/v1/tokeninfo";
/**
* Verifies that the access token is still valid and not revoked (e.g. user removed app access).
* Returns true if token is valid, false if invalid/revoked.
*/
const isAccessTokenValid = async (accessToken: string): Promise<boolean> => {
try {
const res = await fetch(`${GOOGLE_TOKENINFO_URL}?access_token=${encodeURIComponent(accessToken)}`);
return res.ok;
} catch {
return false;
}
};
const authorize = async (googleSheetIntegrationData: TIntegrationGoogleSheets) => { const authorize = async (googleSheetIntegrationData: TIntegrationGoogleSheets) => {
const client_id = GOOGLE_SHEETS_CLIENT_ID; const client_id = GOOGLE_SHEETS_CLIENT_ID;
const client_secret = GOOGLE_SHEETS_CLIENT_SECRET; const client_secret = GOOGLE_SHEETS_CLIENT_SECRET;
const redirect_uri = GOOGLE_SHEETS_REDIRECT_URL; const redirect_uri = GOOGLE_SHEETS_REDIRECT_URL;
const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uri); const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uri);
const key = googleSheetIntegrationData.config.key; const refresh_token = googleSheetIntegrationData.config.key.refresh_token;
oAuth2Client.setCredentials({
refresh_token,
});
const { credentials } = await oAuth2Client.refreshAccessToken();
await createOrUpdateIntegration(googleSheetIntegrationData.environmentId, {
type: "googleSheets",
config: {
data: googleSheetIntegrationData.config?.data ?? [],
email: googleSheetIntegrationData.config?.email ?? "",
key: credentials,
},
});
const hasStoredCredentials = oAuth2Client.setCredentials(credentials);
key.access_token && key.expiry_date && key.expiry_date > Date.now() + TOKEN_EXPIRY_BUFFER_MS;
if (hasStoredCredentials && (await isAccessTokenValid(key.access_token))) { return oAuth2Client;
oAuth2Client.setCredentials(key);
return oAuth2Client;
}
oAuth2Client.setCredentials({ refresh_token: key.refresh_token });
try {
const { credentials } = await oAuth2Client.refreshAccessToken();
const mergedCredentials = {
...credentials,
refresh_token: credentials.refresh_token ?? key.refresh_token,
};
await createOrUpdateIntegration(googleSheetIntegrationData.environmentId, {
type: "googleSheets",
config: {
data: googleSheetIntegrationData.config?.data ?? [],
email: googleSheetIntegrationData.config?.email ?? "",
key: mergedCredentials,
},
});
oAuth2Client.setCredentials(mergedCredentials);
return oAuth2Client;
} catch (error) {
if (isInvalidGrantError(error)) {
throw new AuthenticationError(GOOGLE_SHEET_INTEGRATION_INVALID_GRANT);
}
throw error;
}
}; };
-18
View File
@@ -130,102 +130,84 @@ export const appLanguages = [
code: "de-DE", code: "de-DE",
label: { label: {
"en-US": "German", "en-US": "German",
native: "Deutsch",
}, },
}, },
{ {
code: "en-US", code: "en-US",
label: { label: {
"en-US": "English (US)", "en-US": "English (US)",
native: "English (US)",
}, },
}, },
{ {
code: "es-ES", code: "es-ES",
label: { label: {
"en-US": "Spanish", "en-US": "Spanish",
native: "Español",
}, },
}, },
{ {
code: "fr-FR", code: "fr-FR",
label: { label: {
"en-US": "French", "en-US": "French",
native: "Français",
}, },
}, },
{ {
code: "hu-HU", code: "hu-HU",
label: { label: {
"en-US": "Hungarian", "en-US": "Hungarian",
native: "Magyar",
}, },
}, },
{ {
code: "ja-JP", code: "ja-JP",
label: { label: {
"en-US": "Japanese", "en-US": "Japanese",
native: "日本語",
}, },
}, },
{ {
code: "nl-NL", code: "nl-NL",
label: { label: {
"en-US": "Dutch", "en-US": "Dutch",
native: "Nederlands",
}, },
}, },
{ {
code: "pt-BR", code: "pt-BR",
label: { label: {
"en-US": "Portuguese (Brazil)", "en-US": "Portuguese (Brazil)",
native: "Português (Brasil)",
}, },
}, },
{ {
code: "pt-PT", code: "pt-PT",
label: { label: {
"en-US": "Portuguese (Portugal)", "en-US": "Portuguese (Portugal)",
native: "Português (Portugal)",
}, },
}, },
{ {
code: "ro-RO", code: "ro-RO",
label: { label: {
"en-US": "Romanian", "en-US": "Romanian",
native: "Română",
}, },
}, },
{ {
code: "ru-RU", code: "ru-RU",
label: { label: {
"en-US": "Russian", "en-US": "Russian",
native: "Русский",
}, },
}, },
{ {
code: "sv-SE", code: "sv-SE",
label: { label: {
"en-US": "Swedish", "en-US": "Swedish",
native: "Svenska",
}, },
}, },
{ {
code: "zh-Hans-CN", code: "zh-Hans-CN",
label: { label: {
"en-US": "Chinese (Simplified)", "en-US": "Chinese (Simplified)",
native: "简体中文",
}, },
}, },
{ {
code: "zh-Hant-TW", code: "zh-Hant-TW",
label: { label: {
"en-US": "Chinese (Traditional)", "en-US": "Chinese (Traditional)",
native: "繁體中文",
}, },
}, },
]; ];
export const sortedAppLanguages = [...appLanguages].sort((a, b) =>
a.label["en-US"].localeCompare(b.label["en-US"])
);
+2 -1
View File
@@ -267,7 +267,7 @@ export const getResponses = reactCache(
[limit, ZOptionalNumber], [limit, ZOptionalNumber],
[offset, ZOptionalNumber], [offset, ZOptionalNumber],
[filterCriteria, ZResponseFilterCriteria.optional()], [filterCriteria, ZResponseFilterCriteria.optional()],
[cursor, z.cuid2().optional()] [cursor, z.string().cuid2().optional()]
); );
limit = limit ?? RESPONSES_PER_PAGE; limit = limit ?? RESPONSES_PER_PAGE;
@@ -397,6 +397,7 @@ export const getResponseDownloadFile = async (
"Survey ID", "Survey ID",
"Formbricks ID (internal)", "Formbricks ID (internal)",
"User ID", "User ID",
"Notes",
"Tags", "Tags",
...metaDataFields, ...metaDataFields,
...elements.flat(), ...elements.flat(),
+2 -6
View File
@@ -60,7 +60,6 @@ export const getSuggestedColors = (brandColor: string = DEFAULT_BRAND_COLOR) =>
// Options (Radio / Checkbox) // Options (Radio / Checkbox)
"optionBgColor.light": inputBg, "optionBgColor.light": inputBg,
"optionLabelColor.light": questionColor, "optionLabelColor.light": questionColor,
"optionBorderColor.light": inputBorder,
// Card // Card
"cardBackgroundColor.light": cardBg, "cardBackgroundColor.light": cardBg,
@@ -139,7 +138,6 @@ export const STYLE_DEFAULTS: TProjectStyling = {
// Options // Options
optionBgColor: { light: _colors["optionBgColor.light"] }, optionBgColor: { light: _colors["optionBgColor.light"] },
optionLabelColor: { light: _colors["optionLabelColor.light"] }, optionLabelColor: { light: _colors["optionLabelColor.light"] },
optionBorderColor: { light: _colors["optionBorderColor.light"] },
optionBorderRadius: 8, optionBorderRadius: 8,
optionPaddingX: 16, optionPaddingX: 16,
optionPaddingY: 16, optionPaddingY: 16,
@@ -171,7 +169,6 @@ export const deriveNewFieldsFromLegacy = (saved: Record<string, unknown>): Recor
const q = light("questionColor"); const q = light("questionColor");
const b = light("brandColor"); const b = light("brandColor");
const i = light("inputColor"); const i = light("inputColor");
const inputBorder = light("inputBorderColor");
return { return {
...(q && !saved.elementHeadlineColor && { elementHeadlineColor: { light: q } }), ...(q && !saved.elementHeadlineColor && { elementHeadlineColor: { light: q } }),
@@ -182,9 +179,9 @@ export const deriveNewFieldsFromLegacy = (saved: Record<string, unknown>): Recor
...(b && !saved.buttonBgColor && { buttonBgColor: { light: b } }), ...(b && !saved.buttonBgColor && { buttonBgColor: { light: b } }),
...(b && !saved.buttonTextColor && { buttonTextColor: { light: isLight(b) ? "#0f172a" : "#ffffff" } }), ...(b && !saved.buttonTextColor && { buttonTextColor: { light: isLight(b) ? "#0f172a" : "#ffffff" } }),
...(i && !saved.optionBgColor && { optionBgColor: { light: i } }), ...(i && !saved.optionBgColor && { optionBgColor: { light: i } }),
...(inputBorder && !saved.optionBorderColor && { optionBorderColor: { light: inputBorder } }),
...(b && !saved.progressIndicatorBgColor && { progressIndicatorBgColor: { light: b } }), ...(b && !saved.progressIndicatorBgColor && { progressIndicatorBgColor: { light: b } }),
...(b && !saved.progressTrackBgColor && { progressTrackBgColor: { light: mixColor(b, "#ffffff", 0.8) } }), ...(b &&
!saved.progressTrackBgColor && { progressTrackBgColor: { light: mixColor(b, "#ffffff", 0.8) } }),
}; };
}; };
@@ -214,7 +211,6 @@ export const buildStylingFromBrandColor = (brandColor: string = DEFAULT_BRAND_CO
inputTextColor: { light: colors["inputTextColor.light"] }, inputTextColor: { light: colors["inputTextColor.light"] },
optionBgColor: { light: colors["optionBgColor.light"] }, optionBgColor: { light: colors["optionBgColor.light"] },
optionLabelColor: { light: colors["optionLabelColor.light"] }, optionLabelColor: { light: colors["optionLabelColor.light"] },
optionBorderColor: { light: colors["optionBorderColor.light"] },
cardBackgroundColor: { light: colors["cardBackgroundColor.light"] }, cardBackgroundColor: { light: colors["cardBackgroundColor.light"] },
cardBorderColor: { light: colors["cardBorderColor.light"] }, cardBorderColor: { light: colors["cardBorderColor.light"] },
highlightBorderColor: { light: colors["highlightBorderColor.light"] }, highlightBorderColor: { light: colors["highlightBorderColor.light"] },
+44 -40
View File
@@ -11,7 +11,6 @@ import {
getOrganizationByEnvironmentId, getOrganizationByEnvironmentId,
subscribeOrganizationMembersToSurveyResponses, subscribeOrganizationMembersToSurveyResponses,
} from "@/lib/organization/service"; } from "@/lib/organization/service";
import { TriggerUpdate } from "@/modules/survey/editor/types/survey-trigger";
import { getActionClasses } from "../actionClass/service"; import { getActionClasses } from "../actionClass/service";
import { ITEMS_PER_PAGE } from "../constants"; import { ITEMS_PER_PAGE } from "../constants";
import { validateInputs } from "../utils/validate"; import { validateInputs } from "../utils/validate";
@@ -23,6 +22,15 @@ import {
validateMediaAndPrepareBlocks, validateMediaAndPrepareBlocks,
} from "./utils"; } from "./utils";
interface TriggerUpdate {
create?: Array<{ actionClassId: string }>;
deleteMany?: {
actionClassId: {
in: string[];
};
};
}
export const selectSurvey = { export const selectSurvey = {
id: true, id: true,
createdAt: true, createdAt: true,
@@ -106,32 +114,19 @@ export const selectSurvey = {
slug: true, slug: true,
} satisfies Prisma.SurveySelect; } satisfies Prisma.SurveySelect;
const getTriggerIds = (triggers: TSurvey["triggers"]): string[] | null => {
if (!triggers) return null;
if (!Array.isArray(triggers)) {
throw new InvalidInputError("Invalid trigger id");
}
return triggers.map((trigger) => {
const actionClassId = trigger?.actionClass?.id;
if (typeof actionClassId !== "string") {
throw new InvalidInputError("Invalid trigger id");
}
return actionClassId;
});
};
export const checkTriggersValidity = (triggers: TSurvey["triggers"], actionClasses: ActionClass[]) => { export const checkTriggersValidity = (triggers: TSurvey["triggers"], actionClasses: ActionClass[]) => {
const triggerIds = getTriggerIds(triggers); if (!triggers) return;
if (!triggerIds) return;
// check if all the triggers are valid // check if all the triggers are valid
triggerIds.forEach((triggerId) => { triggers.forEach((trigger) => {
if (!actionClasses.find((actionClass) => actionClass.id === triggerId)) { if (!actionClasses.find((actionClass) => actionClass.id === trigger.actionClass.id)) {
throw new InvalidInputError("Invalid trigger id"); throw new InvalidInputError("Invalid trigger id");
} }
}); });
// check if all the triggers are unique
const triggerIds = triggers.map((trigger) => trigger.actionClass.id);
if (new Set(triggerIds).size !== triggerIds.length) { if (new Set(triggerIds).size !== triggerIds.length) {
throw new InvalidInputError("Duplicate trigger id"); throw new InvalidInputError("Duplicate trigger id");
} }
@@ -142,33 +137,36 @@ export const handleTriggerUpdates = (
currentTriggers: TSurvey["triggers"], currentTriggers: TSurvey["triggers"],
actionClasses: ActionClass[] actionClasses: ActionClass[]
) => { ) => {
const updatedTriggerIds = getTriggerIds(updatedTriggers); if (!updatedTriggers) return {};
if (!updatedTriggerIds) return {};
checkTriggersValidity(updatedTriggers, actionClasses); checkTriggersValidity(updatedTriggers, actionClasses);
const currentTriggerIds = getTriggerIds(currentTriggers) ?? []; const currentTriggerIds = currentTriggers.map((trigger) => trigger.actionClass.id);
const updatedTriggerIds = updatedTriggers.map((trigger) => trigger.actionClass.id);
// added triggers are triggers that are not in the current triggers and are there in the new triggers // added triggers are triggers that are not in the current triggers and are there in the new triggers
const addedTriggerIds = updatedTriggerIds.filter((triggerId) => !currentTriggerIds.includes(triggerId)); const addedTriggers = updatedTriggers.filter(
(trigger) => !currentTriggerIds.includes(trigger.actionClass.id)
);
// deleted triggers are triggers that are not in the new triggers and are there in the current triggers // deleted triggers are triggers that are not in the new triggers and are there in the current triggers
const deletedTriggerIds = currentTriggerIds.filter((triggerId) => !updatedTriggerIds.includes(triggerId)); const deletedTriggers = currentTriggers.filter(
(trigger) => !updatedTriggerIds.includes(trigger.actionClass.id)
);
// Construct the triggers update object // Construct the triggers update object
const triggersUpdate: TriggerUpdate = {}; const triggersUpdate: TriggerUpdate = {};
if (addedTriggerIds.length > 0) { if (addedTriggers.length > 0) {
triggersUpdate.create = addedTriggerIds.map((triggerId) => ({ triggersUpdate.create = addedTriggers.map((trigger) => ({
actionClassId: triggerId, actionClassId: trigger.actionClass.id,
})); }));
} }
if (deletedTriggerIds.length > 0) { if (deletedTriggers.length > 0) {
// disconnect the public triggers from the survey // disconnect the public triggers from the survey
triggersUpdate.deleteMany = { triggersUpdate.deleteMany = {
actionClassId: { actionClassId: {
in: deletedTriggerIds, in: deletedTriggers.map((trigger) => trigger.actionClass.id),
}, },
}; };
} }
@@ -510,7 +508,6 @@ export const updateSurveyInternal = async (
newFollowUps.length > 0 newFollowUps.length > 0
? { ? {
data: newFollowUps.map((followUp) => ({ data: newFollowUps.map((followUp) => ({
id: followUp.id,
name: followUp.name, name: followUp.name,
trigger: followUp.trigger, trigger: followUp.trigger,
action: followUp.action, action: followUp.action,
@@ -602,16 +599,21 @@ export const createSurvey = async (
); );
try { try {
const { createdBy, languages, ...restSurveyBody } = parsedSurveyBody; const { createdBy, ...restSurveyBody } = parsedSurveyBody;
// empty languages array
if (!restSurveyBody.languages?.length) {
delete restSurveyBody.languages;
}
const actionClasses = await getActionClasses(parsedEnvironmentId); const actionClasses = await getActionClasses(parsedEnvironmentId);
// @ts-expect-error
let data: Omit<Prisma.SurveyCreateInput, "environment"> = { let data: Omit<Prisma.SurveyCreateInput, "environment"> = {
...restSurveyBody, ...restSurveyBody,
// @ts-expect-error - languages would be undefined in case of empty array // TODO: Create with attributeFilters
languages: languages?.length ? languages : undefined,
triggers: restSurveyBody.triggers triggers: restSurveyBody.triggers
? // @ts-expect-error - triggers' createdAt and updatedAt are actually dates ? handleTriggerUpdates(restSurveyBody.triggers, [], actionClasses)
handleTriggerUpdates(restSurveyBody.triggers, [], actionClasses)
: undefined, : undefined,
attributeFilters: undefined, attributeFilters: undefined,
}; };
@@ -780,13 +782,15 @@ export const loadNewSegmentInSurvey = async (surveyId: string, newSegmentId: str
}; };
} }
const modifiedSurvey = { // TODO: Fix this, this happens because the survey type "web" is no longer in the zod types but its required in the schema for migration
...prismaSurvey, // @ts-expect-error
const modifiedSurvey: TSurvey = {
...prismaSurvey, // Properties from prismaSurvey
segment: surveySegment, segment: surveySegment,
customHeadScriptsMode: prismaSurvey.customHeadScriptsMode, customHeadScriptsMode: prismaSurvey.customHeadScriptsMode,
}; };
return modifiedSurvey as TSurvey; return modifiedSurvey;
} catch (error) { } catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) { if (error instanceof Prisma.PrismaClientKnownRequestError) {
throw new DatabaseError(error.message); throw new DatabaseError(error.message);
+1 -1
View File
@@ -52,7 +52,7 @@ export const getUser = reactCache(async (id: string): Promise<TUser | null> => {
}); });
export const getUserByEmail = reactCache(async (email: string): Promise<TUser | null> => { export const getUserByEmail = reactCache(async (email: string): Promise<TUser | null> => {
validateInputs([email, z.email()]); validateInputs([email, z.string().email()]);
try { try {
const user = await prisma.user.findFirst({ const user = await prisma.user.findFirst({
@@ -1,4 +1,4 @@
import * as cuid2 from "@paralleldrive/cuid2"; import cuid2 from "@paralleldrive/cuid2";
import { beforeEach, describe, expect, test, vi } from "vitest"; import { beforeEach, describe, expect, test, vi } from "vitest";
import * as crypto from "@/lib/crypto"; import * as crypto from "@/lib/crypto";
import { env } from "@/lib/env"; import { env } from "@/lib/env";
+2 -2
View File
@@ -1,10 +1,10 @@
import { createId } from "@paralleldrive/cuid2"; import cuid2 from "@paralleldrive/cuid2";
import { symmetricEncrypt } from "@/lib/crypto"; import { symmetricEncrypt } from "@/lib/crypto";
import { env } from "@/lib/env"; import { env } from "@/lib/env";
// generate encrypted single use id for the survey // generate encrypted single use id for the survey
export const generateSurveySingleUseId = (isEncrypted: boolean): string => { export const generateSurveySingleUseId = (isEncrypted: boolean): string => {
const cuid = createId(); const cuid = cuid2.createId();
if (!isEncrypted) { if (!isEncrypted) {
return cuid; return cuid;
} }
@@ -1,297 +0,0 @@
import dns from "node:dns";
import { afterEach, describe, expect, test, vi } from "vitest";
import { validateWebhookUrl } from "./validate-webhook-url";
vi.mock("node:dns", () => ({
default: {
resolve: vi.fn(),
resolve6: vi.fn(),
},
}));
const mockResolve = vi.mocked(dns.resolve);
const mockResolve6 = vi.mocked(dns.resolve6);
type DnsCallback = (err: NodeJS.ErrnoException | null, addresses: string[]) => void;
const setupDnsResolution = (ipv4: string[] | null, ipv6: string[] | null = null): void => {
// dns.resolve/resolve6 have overloaded signatures; we only mock the (hostname, callback) form
mockResolve.mockImplementation(((_hostname: string, callback: DnsCallback) => {
if (ipv4) {
callback(null, ipv4);
} else {
callback(new Error("ENOTFOUND"), []);
}
}) as never);
mockResolve6.mockImplementation(((_hostname: string, callback: DnsCallback) => {
if (ipv6) {
callback(null, ipv6);
} else {
callback(new Error("ENOTFOUND"), []);
}
}) as never);
};
afterEach(() => {
vi.clearAllMocks();
});
describe("validateWebhookUrl", () => {
describe("valid public URLs", () => {
test("accepts HTTPS URL resolving to a public IPv4 address", async () => {
setupDnsResolution(["93.184.216.34"]);
await expect(validateWebhookUrl("https://example.com/webhook")).resolves.toBeUndefined();
});
test("accepts HTTP URL resolving to a public IPv4 address", async () => {
setupDnsResolution(["93.184.216.34"]);
await expect(validateWebhookUrl("http://example.com/webhook")).resolves.toBeUndefined();
});
test("accepts URL with port and path segments", async () => {
setupDnsResolution(["93.184.216.34"]);
await expect(validateWebhookUrl("https://example.com:8443/api/v1/webhook")).resolves.toBeUndefined();
});
test("accepts URL resolving to a public IPv6 address", async () => {
setupDnsResolution(null, ["2606:2800:220:1:248:1893:25c8:1946"]);
await expect(validateWebhookUrl("https://example.com/webhook")).resolves.toBeUndefined();
});
test("accepts a public IPv4 address as hostname", async () => {
await expect(validateWebhookUrl("https://93.184.216.34/webhook")).resolves.toBeUndefined();
});
});
describe("URL format validation", () => {
test("rejects a completely malformed string", async () => {
await expect(validateWebhookUrl("not-a-url")).rejects.toThrow("Invalid webhook URL format");
});
test("rejects an empty string", async () => {
await expect(validateWebhookUrl("")).rejects.toThrow("Invalid webhook URL format");
});
});
describe("protocol validation", () => {
test("rejects FTP protocol", async () => {
await expect(validateWebhookUrl("ftp://example.com/file")).rejects.toThrow(
"Webhook URL must use HTTPS or HTTP protocol"
);
});
test("rejects file:// protocol", async () => {
await expect(validateWebhookUrl("file:///etc/passwd")).rejects.toThrow(
"Webhook URL must use HTTPS or HTTP protocol"
);
});
test("rejects javascript: protocol", async () => {
await expect(validateWebhookUrl("javascript:alert(1)")).rejects.toThrow(
"Webhook URL must use HTTPS or HTTP protocol"
);
});
});
describe("blocked hostname validation", () => {
test("rejects localhost", async () => {
await expect(validateWebhookUrl("http://localhost/admin")).rejects.toThrow(
"Webhook URL must not point to localhost or internal services"
);
});
test("rejects localhost.localdomain", async () => {
await expect(validateWebhookUrl("https://localhost.localdomain/path")).rejects.toThrow(
"Webhook URL must not point to localhost or internal services"
);
});
test("rejects metadata.google.internal", async () => {
await expect(validateWebhookUrl("http://metadata.google.internal/computeMetadata/v1/")).rejects.toThrow(
"Webhook URL must not point to localhost or internal services"
);
});
});
describe("private IPv4 literal blocking", () => {
test("rejects 127.0.0.1 (loopback)", async () => {
await expect(validateWebhookUrl("http://127.0.0.1/metadata")).rejects.toThrow(
"Webhook URL must not point to private or internal IP addresses"
);
});
test("rejects 127.0.0.53 (loopback range)", async () => {
await expect(validateWebhookUrl("http://127.0.0.53/")).rejects.toThrow(
"Webhook URL must not point to private or internal IP addresses"
);
});
test("rejects 10.0.0.1 (Class A private)", async () => {
await expect(validateWebhookUrl("http://10.0.0.1/internal")).rejects.toThrow(
"Webhook URL must not point to private or internal IP addresses"
);
});
test("rejects 172.16.0.1 (Class B private)", async () => {
await expect(validateWebhookUrl("http://172.16.0.1/internal")).rejects.toThrow(
"Webhook URL must not point to private or internal IP addresses"
);
});
test("rejects 172.31.255.255 (Class B private upper bound)", async () => {
await expect(validateWebhookUrl("http://172.31.255.255/internal")).rejects.toThrow(
"Webhook URL must not point to private or internal IP addresses"
);
});
test("rejects 192.168.1.1 (Class C private)", async () => {
await expect(validateWebhookUrl("http://192.168.1.1/internal")).rejects.toThrow(
"Webhook URL must not point to private or internal IP addresses"
);
});
test("rejects 169.254.169.254 (AWS/GCP/Azure metadata endpoint)", async () => {
await expect(validateWebhookUrl("http://169.254.169.254/latest/meta-data/")).rejects.toThrow(
"Webhook URL must not point to private or internal IP addresses"
);
});
test("rejects 0.0.0.0 ('this' network)", async () => {
await expect(validateWebhookUrl("http://0.0.0.0/")).rejects.toThrow(
"Webhook URL must not point to private or internal IP addresses"
);
});
test("rejects 100.64.0.1 (CGNAT / shared address space)", async () => {
await expect(validateWebhookUrl("http://100.64.0.1/")).rejects.toThrow(
"Webhook URL must not point to private or internal IP addresses"
);
});
});
describe("DNS resolution with private IP results", () => {
test("rejects hostname resolving to loopback address", async () => {
setupDnsResolution(["127.0.0.1"]);
await expect(validateWebhookUrl("https://evil.com/steal")).rejects.toThrow(
"Webhook URL must not point to private or internal IP addresses"
);
});
test("rejects hostname resolving to cloud metadata endpoint IP", async () => {
setupDnsResolution(["169.254.169.254"]);
await expect(validateWebhookUrl("https://attacker.com/ssrf")).rejects.toThrow(
"Webhook URL must not point to private or internal IP addresses"
);
});
test("rejects hostname resolving to Class A private network", async () => {
setupDnsResolution(["10.0.0.5"]);
await expect(validateWebhookUrl("https://internal.service/api")).rejects.toThrow(
"Webhook URL must not point to private or internal IP addresses"
);
});
test("rejects hostname resolving to Class C private network", async () => {
setupDnsResolution(["192.168.0.1"]);
await expect(validateWebhookUrl("https://sneaky.example.com/webhook")).rejects.toThrow(
"Webhook URL must not point to private or internal IP addresses"
);
});
test("rejects hostname resolving to IPv6 loopback", async () => {
setupDnsResolution(null, ["::1"]);
await expect(validateWebhookUrl("https://sneaky.com/webhook")).rejects.toThrow(
"Webhook URL must not point to private or internal IP addresses"
);
});
test("rejects hostname resolving to IPv6 link-local", async () => {
setupDnsResolution(null, ["fe80::1"]);
await expect(validateWebhookUrl("https://link-local.example.com/webhook")).rejects.toThrow(
"Webhook URL must not point to private or internal IP addresses"
);
});
test("rejects hostname resolving to IPv6 unique local address", async () => {
setupDnsResolution(null, ["fd12:3456:789a::1"]);
await expect(validateWebhookUrl("https://ula.example.com/webhook")).rejects.toThrow(
"Webhook URL must not point to private or internal IP addresses"
);
});
test("rejects hostname resolving to IPv4-mapped IPv6 private address (dotted)", async () => {
setupDnsResolution(null, ["::ffff:192.168.1.1"]);
await expect(validateWebhookUrl("https://mapped.example.com/webhook")).rejects.toThrow(
"Webhook URL must not point to private or internal IP addresses"
);
});
test("rejects hostname resolving to IPv4-mapped IPv6 private address (hex-encoded)", async () => {
setupDnsResolution(null, ["::ffff:c0a8:0101"]); // 192.168.1.1 in hex
await expect(validateWebhookUrl("https://hex-mapped.example.com/webhook")).rejects.toThrow(
"Webhook URL must not point to private or internal IP addresses"
);
});
test("rejects hex-encoded IPv4-mapped loopback (::ffff:7f00:0001)", async () => {
setupDnsResolution(null, ["::ffff:7f00:0001"]); // 127.0.0.1 in hex
await expect(validateWebhookUrl("https://hex-loopback.example.com/webhook")).rejects.toThrow(
"Webhook URL must not point to private or internal IP addresses"
);
});
test("rejects hex-encoded IPv4-mapped metadata endpoint (::ffff:a9fe:a9fe)", async () => {
setupDnsResolution(null, ["::ffff:a9fe:a9fe"]); // 169.254.169.254 in hex
await expect(validateWebhookUrl("https://hex-metadata.example.com/webhook")).rejects.toThrow(
"Webhook URL must not point to private or internal IP addresses"
);
});
test("accepts hex-encoded IPv4-mapped public address", async () => {
setupDnsResolution(null, ["::ffff:5db8:d822"]); // 93.184.216.34 in hex
await expect(validateWebhookUrl("https://hex-public.example.com/webhook")).resolves.toBeUndefined();
});
test("rejects when any resolved IP is private (mixed public + private)", async () => {
setupDnsResolution(["93.184.216.34", "192.168.1.1"]);
await expect(validateWebhookUrl("https://dual.example.com/webhook")).rejects.toThrow(
"Webhook URL must not point to private or internal IP addresses"
);
});
test("rejects unresolvable hostname", async () => {
setupDnsResolution(null, null);
await expect(validateWebhookUrl("https://nonexistent.invalid/path")).rejects.toThrow(
"Could not resolve webhook URL hostname"
);
});
test("rejects with timeout error when DNS resolution hangs", async () => {
vi.useFakeTimers();
mockResolve.mockImplementation((() => {
// never calls callback — simulates a hanging DNS server
}) as never);
const promise = validateWebhookUrl("https://slow-dns.example.com/webhook");
const assertion = expect(promise).rejects.toThrow(
"DNS resolution timed out for webhook URL hostname: slow-dns.example.com"
);
await vi.advanceTimersByTimeAsync(3000);
await assertion;
vi.useRealTimers();
});
});
describe("error type", () => {
test("throws InvalidInputError (not generic Error)", async () => {
await expect(validateWebhookUrl("http://127.0.0.1/")).rejects.toMatchObject({
name: "InvalidInputError",
});
});
});
});
-176
View File
@@ -1,176 +0,0 @@
import "server-only";
import dns from "node:dns";
import { InvalidInputError } from "@formbricks/types/errors";
const BLOCKED_HOSTNAMES = new Set([
"localhost",
"localhost.localdomain",
"ip6-localhost",
"ip6-loopback",
"metadata.google.internal",
]);
const PRIVATE_IPV4_PATTERNS: RegExp[] = [
/^127\./, // 127.0.0.0/8 Loopback
/^10\./, // 10.0.0.0/8 Class A private
/^172\.(1[6-9]|2\d|3[01])\./, // 172.16.0.0/12 Class B private
/^192\.168\./, // 192.168.0.0/16 Class C private
/^169\.254\./, // 169.254.0.0/16 Link-local (AWS/GCP/Azure metadata)
/^0\./, // 0.0.0.0/8 "This" network
/^100\.(6[4-9]|[7-9]\d|1[0-2]\d)\./, // 100.64.0.0/10 Shared address space (RFC 6598)
/^192\.0\.0\./, // 192.0.0.0/24 IETF protocol assignments
/^192\.0\.2\./, // 192.0.2.0/24 TEST-NET-1 (documentation)
/^198\.51\.100\./, // 198.51.100.0/24 TEST-NET-2 (documentation)
/^203\.0\.113\./, // 203.0.113.0/24 TEST-NET-3 (documentation)
/^198\.1[89]\./, // 198.18.0.0/15 Benchmarking
/^224\./, // 224.0.0.0/4 Multicast
/^240\./, // 240.0.0.0/4 Reserved for future use
/^255\.255\.255\.255$/, // Limited broadcast
];
const PRIVATE_IPV6_PREFIXES = [
"::1", // Loopback
"fe80:", // Link-local
"fc", // Unique local address (ULA, fc00::/7 — covers fc00:: through fdff::)
"fd", // Unique local address (ULA, fc00::/7 — covers fc00:: through fdff::)
];
const isPrivateIPv4 = (ip: string): boolean => {
return PRIVATE_IPV4_PATTERNS.some((pattern) => pattern.test(ip));
};
const hexMappedToIPv4 = (hexPart: string): string | null => {
const groups = hexPart.split(":");
if (groups.length !== 2) return null;
const high = Number.parseInt(groups[0], 16);
const low = Number.parseInt(groups[1], 16);
if (Number.isNaN(high) || Number.isNaN(low) || high > 0xffff || low > 0xffff) return null;
return `${(high >> 8) & 0xff}.${high & 0xff}.${(low >> 8) & 0xff}.${low & 0xff}`;
};
const isIPv4Mapped = (normalized: string): boolean => {
if (!normalized.startsWith("::ffff:")) return false;
const suffix = normalized.slice(7); // strip "::ffff:"
if (suffix.includes(".")) {
return isPrivateIPv4(suffix);
}
const dotted = hexMappedToIPv4(suffix);
return dotted !== null && isPrivateIPv4(dotted);
};
const isPrivateIPv6 = (ip: string): boolean => {
const normalized = ip.toLowerCase();
if (normalized === "::") return true;
if (isIPv4Mapped(normalized)) return true;
return PRIVATE_IPV6_PREFIXES.some((prefix) => normalized.startsWith(prefix));
};
const isPrivateIP = (ip: string): boolean => {
return isPrivateIPv4(ip) || isPrivateIPv6(ip);
};
const DNS_TIMEOUT_MS = 3000;
const resolveHostnameToIPs = (hostname: string): Promise<string[]> => {
return new Promise((resolve, reject) => {
let settled = false;
const settle = <T>(fn: (value: T) => void, value: T): void => {
if (settled) return;
settled = true;
clearTimeout(timer);
fn(value);
};
const timer = setTimeout(() => {
settle(reject, new Error(`DNS resolution timed out for hostname: ${hostname}`));
}, DNS_TIMEOUT_MS);
dns.resolve(hostname, (errV4, ipv4Addresses) => {
const ipv4 = errV4 ? [] : ipv4Addresses;
dns.resolve6(hostname, (errV6, ipv6Addresses) => {
const ipv6 = errV6 ? [] : ipv6Addresses;
const allAddresses = [...ipv4, ...ipv6];
if (allAddresses.length === 0) {
settle(reject, new Error(`DNS resolution failed for hostname: ${hostname}`));
} else {
settle(resolve, allAddresses);
}
});
});
});
};
const stripIPv6Brackets = (hostname: string): string => {
if (hostname.startsWith("[") && hostname.endsWith("]")) {
return hostname.slice(1, -1);
}
return hostname;
};
const IPV4_LITERAL = /^\d{1,3}(?:\.\d{1,3}){3}$/;
/**
* Validates a webhook URL to prevent Server-Side Request Forgery (SSRF).
*
* Checks performed:
* 1. URL must be well-formed
* 2. Protocol must be HTTPS or HTTP
* 3. Hostname must not be a known internal name (localhost, metadata endpoints)
* 4. IP literal hostnames are checked directly against private ranges
* 5. Domain hostnames are resolved via DNS; all resulting IPs must be public
*
* @throws {InvalidInputError} when the URL fails any validation check
*/
export const validateWebhookUrl = async (url: string): Promise<void> => {
let parsed: URL;
try {
parsed = new URL(url);
} catch {
throw new InvalidInputError("Invalid webhook URL format");
}
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
throw new InvalidInputError("Webhook URL must use HTTPS or HTTP protocol");
}
const hostname = parsed.hostname;
if (BLOCKED_HOSTNAMES.has(hostname.toLowerCase())) {
throw new InvalidInputError("Webhook URL must not point to localhost or internal services");
}
// Direct IP literal — validate without DNS resolution
const isIPv4Literal = IPV4_LITERAL.test(hostname);
const isIPv6Literal = hostname.startsWith("[");
if (isIPv4Literal || isIPv6Literal) {
const ip = isIPv6Literal ? stripIPv6Brackets(hostname) : hostname;
if (isPrivateIP(ip)) {
throw new InvalidInputError("Webhook URL must not point to private or internal IP addresses");
}
return;
}
// Domain name — resolve DNS and validate every resolved IP
let resolvedIPs: string[];
try {
resolvedIPs = await resolveHostnameToIPs(hostname);
} catch (error) {
const isTimeout = error instanceof Error && error.message.includes("timed out");
throw new InvalidInputError(
isTimeout
? `DNS resolution timed out for webhook URL hostname: ${hostname}`
: `Could not resolve webhook URL hostname: ${hostname}`
);
}
for (const ip of resolvedIPs) {
if (isPrivateIP(ip)) {
throw new InvalidInputError("Webhook URL must not point to private or internal IP addresses");
}
}
};
+34 -88
View File
@@ -175,12 +175,9 @@
"copy": "Kopieren", "copy": "Kopieren",
"copy_code": "Code kopieren", "copy_code": "Code kopieren",
"copy_link": "Link kopieren", "copy_link": "Link kopieren",
"count_attributes": "{count, plural, one {{count} Attribut} other {{count} Attribute}}", "count_attributes": "{value, plural, one {{value} Attribut} other {{value} Attribute}}",
"count_contacts": "{count, plural, one {{count} Kontakt} other {{count} Kontakte}}", "count_contacts": "{value, plural, one {{value} Kontakt} other {{value} Kontakte}}",
"count_members": "{count, plural, one {{count} Mitglied} other {{count} Mitglieder}}", "count_responses": "{value, plural, one {{value} Antwort} other {{value} Antworten}}",
"count_questions": "{count, plural, one {{count} Frage} other {{count} Fragen}}",
"count_responses": "{count, plural, one {{count} Antwort} other {{count} Antworten}}",
"count_selections": "{count, plural, one {{count} Auswahl} other {{count} Auswahlen}}",
"create_new_organization": "Neue Organisation erstellen", "create_new_organization": "Neue Organisation erstellen",
"create_segment": "Segment erstellen", "create_segment": "Segment erstellen",
"create_survey": "Umfrage erstellen", "create_survey": "Umfrage erstellen",
@@ -194,7 +191,6 @@
"days": "Tage", "days": "Tage",
"default": "Standard", "default": "Standard",
"delete": "Löschen", "delete": "Löschen",
"delete_what": "{deleteWhat} löschen",
"description": "Beschreibung", "description": "Beschreibung",
"dev_env": "Entwicklungsumgebung", "dev_env": "Entwicklungsumgebung",
"development": "Entwicklung", "development": "Entwicklung",
@@ -210,8 +206,6 @@
"download": "Herunterladen", "download": "Herunterladen",
"draft": "Entwurf", "draft": "Entwurf",
"duplicate": "Duplikat", "duplicate": "Duplikat",
"duplicate_copy": "(Kopie)",
"duplicate_copy_number": "(Kopie {copyNumber})",
"e_commerce": "E-Commerce", "e_commerce": "E-Commerce",
"edit": "Bearbeiten", "edit": "Bearbeiten",
"email": "E-Mail", "email": "E-Mail",
@@ -224,16 +218,13 @@
"error": "Fehler", "error": "Fehler",
"error_component_description": "Diese Ressource existiert nicht oder Du hast nicht die notwendigen Rechte, um darauf zuzugreifen.", "error_component_description": "Diese Ressource existiert nicht oder Du hast nicht die notwendigen Rechte, um darauf zuzugreifen.",
"error_component_title": "Fehler beim Laden der Ressourcen", "error_component_title": "Fehler beim Laden der Ressourcen",
"error_loading_data": "Fehler beim Laden der Daten",
"error_rate_limit_description": "Maximale Anzahl an Anfragen erreicht. Bitte später erneut versuchen.", "error_rate_limit_description": "Maximale Anzahl an Anfragen erreicht. Bitte später erneut versuchen.",
"error_rate_limit_title": "Rate Limit Überschritten", "error_rate_limit_title": "Rate Limit Überschritten",
"expand_rows": "Zeilen erweitern", "expand_rows": "Zeilen erweitern",
"failed_to_copy_to_clipboard": "Fehler beim Kopieren in die Zwischenablage", "failed_to_copy_to_clipboard": "Fehler beim Kopieren in die Zwischenablage",
"failed_to_load_organizations": "Fehler beim Laden der Organisationen", "failed_to_load_organizations": "Fehler beim Laden der Organisationen",
"failed_to_load_workspaces": "Projekte konnten nicht geladen werden", "failed_to_load_workspaces": "Projekte konnten nicht geladen werden",
"filter": "Filter",
"finish": "Fertigstellen", "finish": "Fertigstellen",
"first_name": "Vorname",
"follow_these": "Folge diesen", "follow_these": "Folge diesen",
"formbricks_version": "Formbricks Version", "formbricks_version": "Formbricks Version",
"full_name": "Name", "full_name": "Name",
@@ -246,7 +237,6 @@
"hidden_field": "Verstecktes Feld", "hidden_field": "Verstecktes Feld",
"hidden_fields": "Versteckte Felder", "hidden_fields": "Versteckte Felder",
"hide_column": "Spalte ausblenden", "hide_column": "Spalte ausblenden",
"id": "ID",
"image": "Bild", "image": "Bild",
"images": "Bilder", "images": "Bilder",
"import": "Importieren", "import": "Importieren",
@@ -264,7 +254,6 @@
"key": "Schlüssel", "key": "Schlüssel",
"label": "Bezeichnung", "label": "Bezeichnung",
"language": "Sprache", "language": "Sprache",
"last_name": "Nachname",
"learn_more": "Mehr erfahren", "learn_more": "Mehr erfahren",
"license_expired": "License Expired", "license_expired": "License Expired",
"light_overlay": "Helle Überlagerung", "light_overlay": "Helle Überlagerung",
@@ -279,6 +268,7 @@
"look_and_feel": "Darstellung", "look_and_feel": "Darstellung",
"manage": "Verwalten", "manage": "Verwalten",
"marketing": "Marketing", "marketing": "Marketing",
"member": "Mitglied",
"members": "Mitglieder", "members": "Mitglieder",
"members_and_teams": "Mitglieder & Teams", "members_and_teams": "Mitglieder & Teams",
"membership_not_found": "Mitgliedschaft nicht gefunden", "membership_not_found": "Mitgliedschaft nicht gefunden",
@@ -290,7 +280,6 @@
"move_down": "Nach unten bewegen", "move_down": "Nach unten bewegen",
"move_up": "Nach oben bewegen", "move_up": "Nach oben bewegen",
"multiple_languages": "Mehrsprachigkeit", "multiple_languages": "Mehrsprachigkeit",
"my_product": "mein Produkt",
"name": "Name", "name": "Name",
"new": "Neu", "new": "Neu",
"new_version_available": "Formbricks {version} ist da. Jetzt aktualisieren!", "new_version_available": "Formbricks {version} ist da. Jetzt aktualisieren!",
@@ -386,6 +375,8 @@
"select_teams": "Teams auswählen", "select_teams": "Teams auswählen",
"selected": "Ausgewählt", "selected": "Ausgewählt",
"selected_questions": "Ausgewählte Fragen", "selected_questions": "Ausgewählte Fragen",
"selection": "Auswahl",
"selections": "Auswahlen",
"send_test_email": "Test-E-Mail senden", "send_test_email": "Test-E-Mail senden",
"session_not_found": "Sitzung nicht gefunden", "session_not_found": "Sitzung nicht gefunden",
"settings": "Einstellungen", "settings": "Einstellungen",
@@ -394,7 +385,6 @@
"show_response_count": "Antwortanzahl anzeigen", "show_response_count": "Antwortanzahl anzeigen",
"shown": "Angezeigt", "shown": "Angezeigt",
"size": "Größe", "size": "Größe",
"skip": "Überspringen",
"skipped": "Übersprungen", "skipped": "Übersprungen",
"skips": "Übersprungen", "skips": "Übersprungen",
"some_files_failed_to_upload": "Einige Dateien konnten nicht hochgeladen werden", "some_files_failed_to_upload": "Einige Dateien konnten nicht hochgeladen werden",
@@ -438,7 +428,6 @@
"top_right": "Oben rechts", "top_right": "Oben rechts",
"try_again": "Versuch's nochmal", "try_again": "Versuch's nochmal",
"type": "Typ", "type": "Typ",
"unknown_survey": "Unbekannte Umfrage",
"unlock_more_workspaces_with_a_higher_plan": "Schalten Sie mehr Projekte mit einem höheren Tarif frei.", "unlock_more_workspaces_with_a_higher_plan": "Schalten Sie mehr Projekte mit einem höheren Tarif frei.",
"update": "Aktualisierung", "update": "Aktualisierung",
"updated": "Aktualisiert", "updated": "Aktualisiert",
@@ -464,7 +453,6 @@
"website_survey": "Website-Umfrage", "website_survey": "Website-Umfrage",
"weeks": "Wochen", "weeks": "Wochen",
"welcome_card": "Willkommenskarte", "welcome_card": "Willkommenskarte",
"workflows": "Workflows",
"workspace_configuration": "Projektkonfiguration", "workspace_configuration": "Projektkonfiguration",
"workspace_created_successfully": "Projekt erfolgreich erstellt", "workspace_created_successfully": "Projekt erfolgreich erstellt",
"workspace_creation_description": "Organisieren Sie Umfragen in Projekten für eine bessere Zugriffskontrolle.", "workspace_creation_description": "Organisieren Sie Umfragen in Projekten für eine bessere Zugriffskontrolle.",
@@ -647,27 +635,27 @@
"attribute_value": "Wert", "attribute_value": "Wert",
"attribute_value_placeholder": "Attributwert", "attribute_value_placeholder": "Attributwert",
"attributes_msg_attribute_limit_exceeded": "Es konnten {count} neue Attribute nicht erstellt werden, da dies das maximale Limit von {limit} Attributklassen überschreiten würde. Bestehende Attribute wurden erfolgreich aktualisiert.", "attributes_msg_attribute_limit_exceeded": "Es konnten {count} neue Attribute nicht erstellt werden, da dies das maximale Limit von {limit} Attributklassen überschreiten würde. Bestehende Attribute wurden erfolgreich aktualisiert.",
"attributes_msg_attribute_type_validation_error": "{error} (Attribut {key} hat dataType: {dataType})", "attributes_msg_attribute_type_validation_error": "{error} (Attribut '{key}' hat dataType: {dataType})",
"attributes_msg_email_already_exists": "Die E-Mail existiert bereits für diese Umgebung und wurde nicht aktualisiert.", "attributes_msg_email_already_exists": "Die E-Mail existiert bereits für diese Umgebung und wurde nicht aktualisiert.",
"attributes_msg_email_or_userid_required": "Entweder E-Mail oder Benutzer-ID ist erforderlich. Die vorhandenen Werte wurden beibehalten.", "attributes_msg_email_or_userid_required": "Entweder E-Mail oder userId ist erforderlich. Die bestehenden Werte wurden beibehalten.",
"attributes_msg_new_attribute_created": "Neues Attribut {key} mit Typ {dataType} erstellt", "attributes_msg_new_attribute_created": "Neues Attribut '{key}' mit Typ '{dataType}' erstellt",
"attributes_msg_userid_already_exists": "Die Benutzer-ID existiert bereits für diese Umgebung und wurde nicht aktualisiert.", "attributes_msg_userid_already_exists": "Die userId existiert bereits für diese Umgebung und wurde nicht aktualisiert.",
"contact_deleted_successfully": "Kontakt erfolgreich gelöscht", "contact_deleted_successfully": "Kontakt erfolgreich gelöscht",
"contact_not_found": "Kein solcher Kontakt gefunden", "contact_not_found": "Kein solcher Kontakt gefunden",
"contacts_table_refresh": "Kontakte aktualisieren", "contacts_table_refresh": "Kontakte aktualisieren",
"contacts_table_refresh_success": "Kontakte erfolgreich aktualisiert", "contacts_table_refresh_success": "Kontakte erfolgreich aktualisiert",
"create_attribute": "Attribut erstellen", "create_attribute": "Attribut erstellen",
"create_key": "Schlüssel erstellen",
"create_new_attribute": "Neues Attribut erstellen", "create_new_attribute": "Neues Attribut erstellen",
"create_new_attribute_description": "Erstellen Sie ein neues Attribut für Segmentierungszwecke.", "create_new_attribute_description": "Erstellen Sie ein neues Attribut für Segmentierungszwecke.",
"custom_attributes": "Benutzerdefinierte Attribute", "custom_attributes": "Benutzerdefinierte Attribute",
"data_type": "Datentyp", "data_type": "Datentyp",
"data_type_cannot_be_changed": "Der Datentyp kann nach der Erstellung nicht mehr geändert werden", "data_type_cannot_be_changed": "Der Datentyp kann nach der Erstellung nicht mehr geändert werden",
"data_type_description": "Wähle aus, wie dieses Attribut gespeichert und gefiltert werden soll", "data_type_description": "Wähle aus, wie dieses Attribut gespeichert und gefiltert werden soll",
"date_value_required": "Datumswert ist erforderlich. Verwende die Löschen-Schaltfläche, um dieses Attribut zu entfernen, wenn du kein Datum festlegen möchtest.", "date_value_required": "Ein Datumswert ist erforderlich. Verwende die Löschen-Schaltfläche, um dieses Attribut zu entfernen, wenn du kein Datum festlegen möchtest.",
"delete_attribute_confirmation": "{value, plural, one {Dadurch wird das ausgewählte Attribut gelöscht. Alle mit diesem Attribut verknüpften Kontaktdaten gehen verloren.} other {Dadurch werden die ausgewählten Attribute gelöscht. Alle mit diesen Attributen verknüpften Kontaktdaten gehen verloren.}}", "delete_attribute_confirmation": "{value, plural, one {Dadurch wird das ausgewählte Attribut gelöscht. Alle mit diesem Attribut verknüpften Kontaktdaten gehen verloren.} other {Dadurch werden die ausgewählten Attribute gelöscht. Alle mit diesen Attributen verknüpften Kontaktdaten gehen verloren.}}",
"delete_contact_confirmation": "Dies wird alle Umfrageantworten und Kontaktattribute löschen, die mit diesem Kontakt verbunden sind. Jegliche zielgerichtete Kommunikation und Personalisierung basierend auf den Daten dieses Kontakts gehen verloren.", "delete_contact_confirmation": "Dies wird alle Umfrageantworten und Kontaktattribute löschen, die mit diesem Kontakt verbunden sind. Jegliche zielgerichtete Kommunikation und Personalisierung basierend auf den Daten dieses Kontakts gehen verloren.",
"delete_contact_confirmation_with_quotas": "{value, plural, one {Dies wird alle Umfrageantworten und Kontaktattribute löschen, die mit diesem Kontakt verbunden sind. Jegliche zielgerichtete Kommunikation und Personalisierung basierend auf den Daten dieses Kontakts gehen verloren. Wenn dieser Kontakt Antworten hat, die zu den Umfragequoten zählen, werden die Quotenstände reduziert, aber die Quotenlimits bleiben unverändert.} other {Dies wird alle Umfrageantworten und Kontaktattribute löschen, die mit diesen Kontakten verbunden sind. Jegliche zielgerichtete Kommunikation und Personalisierung basierend auf den Daten dieses Kontakts gehen verloren. Wenn diesen Kontakten Antworten haben, die zu den Umfragequoten zählen, werden die Quotenstände reduziert, aber die Quotenlimits bleiben unverändert.}}", "delete_contact_confirmation_with_quotas": "{value, plural, one {Dies wird alle Umfrageantworten und Kontaktattribute löschen, die mit diesem Kontakt verbunden sind. Jegliche zielgerichtete Kommunikation und Personalisierung basierend auf den Daten dieses Kontakts gehen verloren. Wenn dieser Kontakt Antworten hat, die zu den Umfragequoten zählen, werden die Quotenstände reduziert, aber die Quotenlimits bleiben unverändert.} other {Dies wird alle Umfrageantworten und Kontaktattribute löschen, die mit diesen Kontakten verbunden sind. Jegliche zielgerichtete Kommunikation und Personalisierung basierend auf den Daten dieses Kontakts gehen verloren. Wenn diesen Kontakten Antworten haben, die zu den Umfragequoten zählen, werden die Quotenstände reduziert, aber die Quotenlimits bleiben unverändert.}}",
"displays": "Anzeigen",
"edit_attribute": "Attribut bearbeiten", "edit_attribute": "Attribut bearbeiten",
"edit_attribute_description": "Aktualisieren Sie die Bezeichnung und Beschreibung für dieses Attribut.", "edit_attribute_description": "Aktualisieren Sie die Bezeichnung und Beschreibung für dieses Attribut.",
"edit_attribute_values": "Attribute bearbeiten", "edit_attribute_values": "Attribute bearbeiten",
@@ -679,7 +667,6 @@
"invalid_csv_column_names": "Ungültige CSV-Spaltennamen: {columns}. Spaltennamen, die zu neuen Attributen werden, dürfen nur Kleinbuchstaben, Zahlen und Unterstriche enthalten und müssen mit einem Buchstaben beginnen.", "invalid_csv_column_names": "Ungültige CSV-Spaltennamen: {columns}. Spaltennamen, die zu neuen Attributen werden, dürfen nur Kleinbuchstaben, Zahlen und Unterstriche enthalten und müssen mit einem Buchstaben beginnen.",
"invalid_date_format": "Ungültiges Datumsformat. Bitte verwende ein gültiges Datum.", "invalid_date_format": "Ungültiges Datumsformat. Bitte verwende ein gültiges Datum.",
"invalid_number_format": "Ungültiges Zahlenformat. Bitte gib eine gültige Zahl ein.", "invalid_number_format": "Ungültiges Zahlenformat. Bitte gib eine gültige Zahl ein.",
"no_activity_yet": "Noch keine Aktivität",
"no_published_link_surveys_available": "Keine veröffentlichten Link-Umfragen verfügbar. Bitte veröffentliche zuerst eine Link-Umfrage.", "no_published_link_surveys_available": "Keine veröffentlichten Link-Umfragen verfügbar. Bitte veröffentliche zuerst eine Link-Umfrage.",
"no_published_surveys": "Keine veröffentlichten Umfragen", "no_published_surveys": "Keine veröffentlichten Umfragen",
"no_responses_found": "Keine Antworten gefunden", "no_responses_found": "Keine Antworten gefunden",
@@ -694,12 +681,10 @@
"select_a_survey": "Wähle eine Umfrage aus", "select_a_survey": "Wähle eine Umfrage aus",
"select_attribute": "Attribut auswählen", "select_attribute": "Attribut auswählen",
"select_attribute_key": "Attributschlüssel auswählen", "select_attribute_key": "Attributschlüssel auswählen",
"survey_viewed": "Umfrage angesehen",
"survey_viewed_at": "Angesehen am",
"system_attributes": "Systemattribute", "system_attributes": "Systemattribute",
"unlock_contacts_description": "Verwalte Kontakte und sende gezielte Umfragen", "unlock_contacts_description": "Verwalte Kontakte und sende gezielte Umfragen",
"unlock_contacts_title": "Kontakte mit einem höheren Plan freischalten", "unlock_contacts_title": "Kontakte mit einem höheren Plan freischalten",
"upload_contacts_error_attribute_type_mismatch": "Attribut {key} ist als {dataType}” typisiert, aber die CSV enthält ungültige Werte: {values}", "upload_contacts_error_attribute_type_mismatch": "Attribut \"{key}\" ist als \"{dataType}\" definiert, aber die CSV-Datei enthält ungültige Werte: {values}",
"upload_contacts_error_duplicate_mappings": "Doppelte Zuordnungen für folgende Attribute gefunden: {attributes}", "upload_contacts_error_duplicate_mappings": "Doppelte Zuordnungen für folgende Attribute gefunden: {attributes}",
"upload_contacts_error_file_too_large": "Dateigröße überschreitet das maximale Limit von 800KB", "upload_contacts_error_file_too_large": "Dateigröße überschreitet das maximale Limit von 800KB",
"upload_contacts_error_generic": "Beim Hochladen der Kontakte ist ein Fehler aufgetreten. Bitte versuche es später erneut.", "upload_contacts_error_generic": "Beim Hochladen der Kontakte ist ein Fehler aufgetreten. Bitte versuche es später erneut.",
@@ -767,12 +752,7 @@
"link_google_sheet": "Tabelle verlinken", "link_google_sheet": "Tabelle verlinken",
"link_new_sheet": "Neues Blatt verknüpfen", "link_new_sheet": "Neues Blatt verknüpfen",
"no_integrations_yet": "Deine verknüpften Tabellen werden hier angezeigt, sobald Du sie hinzufügst ⏲️", "no_integrations_yet": "Deine verknüpften Tabellen werden hier angezeigt, sobald Du sie hinzufügst ⏲️",
"reconnect_button": "Erneut verbinden", "spreadsheet_url": "Tabellen-URL"
"reconnect_button_description": "Deine Google Sheets-Verbindung ist abgelaufen. Bitte verbinde dich erneut, um weiterhin Antworten zu synchronisieren. Deine bestehenden Tabellen-Links und Daten bleiben erhalten.",
"reconnect_button_tooltip": "Verbinde die Integration erneut, um deinen Zugriff zu aktualisieren. Deine bestehenden Tabellen-Links und Daten bleiben erhalten.",
"spreadsheet_permission_error": "Du hast keine Berechtigung, auf diese Tabelle zuzugreifen. Bitte stelle sicher, dass die Tabelle mit deinem Google-Konto geteilt ist und du Schreibzugriff auf die Tabelle hast.",
"spreadsheet_url": "Tabellen-URL",
"token_expired_error": "Das Google Sheets-Aktualisierungstoken ist abgelaufen oder wurde widerrufen. Bitte verbinde die Integration erneut."
}, },
"include_created_at": "Erstellungsdatum einbeziehen", "include_created_at": "Erstellungsdatum einbeziehen",
"include_hidden_fields": "Versteckte Felder (hidden fields) einbeziehen", "include_hidden_fields": "Versteckte Felder (hidden fields) einbeziehen",
@@ -1059,7 +1039,7 @@
"recheck_license_invalid": "Der Lizenzschlüssel ist ungültig. Bitte überprüfe deinen ENTERPRISE_LICENSE_KEY.", "recheck_license_invalid": "Der Lizenzschlüssel ist ungültig. Bitte überprüfe deinen ENTERPRISE_LICENSE_KEY.",
"recheck_license_success": "Lizenzprüfung erfolgreich", "recheck_license_success": "Lizenzprüfung erfolgreich",
"recheck_license_unreachable": "Lizenzserver ist nicht erreichbar. Bitte versuche es später erneut.", "recheck_license_unreachable": "Lizenzserver ist nicht erreichbar. Bitte versuche es später erneut.",
"rechecking": "Wird erneut geprüft", "rechecking": "Wird erneut geprüft...",
"request_30_day_trial_license": "30-Tage-Testlizenz anfordern", "request_30_day_trial_license": "30-Tage-Testlizenz anfordern",
"saml_sso": "SAML-SSO", "saml_sso": "SAML-SSO",
"service_level_agreement": "Service-Level-Vereinbarung", "service_level_agreement": "Service-Level-Vereinbarung",
@@ -1087,7 +1067,7 @@
"email_customization_preview_email_heading": "Hey {userName}", "email_customization_preview_email_heading": "Hey {userName}",
"email_customization_preview_email_text": "Dies ist eine E-Mail-Vorschau, um dir zu zeigen, welches Logo in den E-Mails gerendert wird.", "email_customization_preview_email_text": "Dies ist eine E-Mail-Vorschau, um dir zu zeigen, welches Logo in den E-Mails gerendert wird.",
"error_deleting_organization_please_try_again": "Fehler beim Löschen der Organisation. Bitte versuche es erneut.", "error_deleting_organization_please_try_again": "Fehler beim Löschen der Organisation. Bitte versuche es erneut.",
"from_your_organization": "{memberName} aus Ihrer Organisation", "from_your_organization": "von deiner Organisation",
"invitation_sent_once_more": "Einladung nochmal gesendet.", "invitation_sent_once_more": "Einladung nochmal gesendet.",
"invite_deleted_successfully": "Einladung erfolgreich gelöscht", "invite_deleted_successfully": "Einladung erfolgreich gelöscht",
"invite_expires_on": "Einladung läuft ab am {date}", "invite_expires_on": "Einladung läuft ab am {date}",
@@ -1136,7 +1116,7 @@
"notification_settings_updated": "Benachrichtigungseinstellungen aktualisiert", "notification_settings_updated": "Benachrichtigungseinstellungen aktualisiert",
"set_up_an_alert_to_get_an_email_on_new_responses": "Richte eine Benachrichtigung ein, um eine E-Mail bei neuen Antworten zu erhalten", "set_up_an_alert_to_get_an_email_on_new_responses": "Richte eine Benachrichtigung ein, um eine E-Mail bei neuen Antworten zu erhalten",
"use_the_integration": "Integration nutzen", "use_the_integration": "Integration nutzen",
"want_to_loop_in_organization_mates": "Möchtest du Organisationsmitglieder einbeziehen?", "want_to_loop_in_organization_mates": "Willst Du die Organisationskollegen einbeziehen?",
"you_will_not_be_auto_subscribed_to_this_organizations_surveys_anymore": "Du wirst nicht mehr automatisch zu den Umfragen dieser Organisation angemeldet!", "you_will_not_be_auto_subscribed_to_this_organizations_surveys_anymore": "Du wirst nicht mehr automatisch zu den Umfragen dieser Organisation angemeldet!",
"you_will_not_receive_any_more_emails_for_responses_on_this_survey": "Du wirst keine weiteren E-Mails für Antworten auf diese Umfrage erhalten!" "you_will_not_receive_any_more_emails_for_responses_on_this_survey": "Du wirst keine weiteren E-Mails für Antworten auf diese Umfrage erhalten!"
}, },
@@ -1252,7 +1232,6 @@
"add_fallback_placeholder": "Platzhalter hinzufügen, falls kein Wert zur Verfügung steht.", "add_fallback_placeholder": "Platzhalter hinzufügen, falls kein Wert zur Verfügung steht.",
"add_hidden_field_id": "Verstecktes Feld ID hinzufügen", "add_hidden_field_id": "Verstecktes Feld ID hinzufügen",
"add_highlight_border": "Rahmen hinzufügen", "add_highlight_border": "Rahmen hinzufügen",
"add_highlight_border_description": "Gilt nur für In-Product-Umfragen.",
"add_logic": "Logik hinzufügen", "add_logic": "Logik hinzufügen",
"add_none_of_the_above": "Füge \"Keine der oben genannten Optionen\" hinzu", "add_none_of_the_above": "Füge \"Keine der oben genannten Optionen\" hinzu",
"add_option": "Option hinzufügen", "add_option": "Option hinzufügen",
@@ -1269,14 +1248,12 @@
"adjust_survey_closed_message": "'Umfrage geschlossen'-Nachricht anpassen", "adjust_survey_closed_message": "'Umfrage geschlossen'-Nachricht anpassen",
"adjust_survey_closed_message_description": "Ändere die Nachricht, die Besucher sehen, wenn die Umfrage geschlossen ist.", "adjust_survey_closed_message_description": "Ändere die Nachricht, die Besucher sehen, wenn die Umfrage geschlossen ist.",
"adjust_the_theme_in_the": "Passe das Thema an in den", "adjust_the_theme_in_the": "Passe das Thema an in den",
"all_are_true": "alle sind wahr",
"all_other_answers_will_continue_to": "Alle anderen Antworten werden weiterhin", "all_other_answers_will_continue_to": "Alle anderen Antworten werden weiterhin",
"allow_multi_select": "Mehrfachauswahl erlauben", "allow_multi_select": "Mehrfachauswahl erlauben",
"allow_multiple_files": "Mehrere Dateien zulassen", "allow_multiple_files": "Mehrere Dateien zulassen",
"allow_users_to_select_more_than_one_image": "Erlaube Nutzern, mehr als ein Bild auszuwählen", "allow_users_to_select_more_than_one_image": "Erlaube Nutzern, mehr als ein Bild auszuwählen",
"and_launch_surveys_in_your_website_or_app": "und Umfragen auf deiner Website oder App starten.", "and_launch_surveys_in_your_website_or_app": "und Umfragen auf deiner Website oder App starten.",
"animation": "Animation", "animation": "Animation",
"any_is_true": "mindestens eine ist wahr",
"app_survey_description": "Bette eine Umfrage in deine Web-App oder Website ein, um Antworten zu sammeln.", "app_survey_description": "Bette eine Umfrage in deine Web-App oder Website ein, um Antworten zu sammeln.",
"assign": "Zuweisen =", "assign": "Zuweisen =",
"audience": "Publikum", "audience": "Publikum",
@@ -1307,7 +1284,7 @@
"calculate": "Berechnen", "calculate": "Berechnen",
"capture_a_new_action_to_trigger_a_survey_on": "Erfasse eine neue Aktion, um eine Umfrage auszulösen.", "capture_a_new_action_to_trigger_a_survey_on": "Erfasse eine neue Aktion, um eine Umfrage auszulösen.",
"capture_ip_address": "IP-Adresse erfassen", "capture_ip_address": "IP-Adresse erfassen",
"capture_ip_address_description": "Speichere die IP-Adresse des Befragten in den Antwort-Metadaten zur Duplikaterkennung und für Sicherheitszwecke", "capture_ip_address_description": "Speichern Sie die IP-Adresse des Befragten in den Antwort-Metadaten zur Duplikaterkennung und für Sicherheitszwecke",
"capture_new_action": "Neue Aktion erfassen", "capture_new_action": "Neue Aktion erfassen",
"card_arrangement_for_survey_type_derived": "Kartenanordnung für {surveyTypeDerived} Umfragen", "card_arrangement_for_survey_type_derived": "Kartenanordnung für {surveyTypeDerived} Umfragen",
"card_background_color": "Hintergrundfarbe der Karte", "card_background_color": "Hintergrundfarbe der Karte",
@@ -1318,7 +1295,7 @@
"casual": "Lässig", "casual": "Lässig",
"caution_edit_duplicate": "Duplizieren & bearbeiten", "caution_edit_duplicate": "Duplizieren & bearbeiten",
"caution_edit_published_survey": "Eine veröffentlichte Umfrage bearbeiten?", "caution_edit_published_survey": "Eine veröffentlichte Umfrage bearbeiten?",
"caution_explanation_intro": "Wir verstehen, dass du möglicherweise noch Änderungen vornehmen möchtest. Folgendes passiert, wenn du dies tust:", "caution_explanation_intro": "Wir verstehen, dass du vielleicht noch Änderungen vornehmen möchtest. Hier erfährst du, was passiert, wenn du das tust:",
"caution_explanation_new_responses_separated": "Antworten vor der Änderung werden möglicherweise nicht oder nur teilweise in der Umfragezusammenfassung berücksichtigt.", "caution_explanation_new_responses_separated": "Antworten vor der Änderung werden möglicherweise nicht oder nur teilweise in der Umfragezusammenfassung berücksichtigt.",
"caution_explanation_only_new_responses_in_summary": "Alle Daten, einschließlich früherer Antworten, bleiben auf der Umfrageübersichtsseite als Download verfügbar.", "caution_explanation_only_new_responses_in_summary": "Alle Daten, einschließlich früherer Antworten, bleiben auf der Umfrageübersichtsseite als Download verfügbar.",
"caution_explanation_responses_are_safe": "Ältere und neuere Antworten vermischen sich, was zu irreführenden Datensummen führen kann.", "caution_explanation_responses_are_safe": "Ältere und neuere Antworten vermischen sich, was zu irreführenden Datensummen führen kann.",
@@ -1453,6 +1430,7 @@
"follow_ups_modal_updated_successfull_toast": "Nachverfolgung aktualisiert und wird gespeichert, sobald du die Umfrage speicherst.", "follow_ups_modal_updated_successfull_toast": "Nachverfolgung aktualisiert und wird gespeichert, sobald du die Umfrage speicherst.",
"follow_ups_new": "Neues Follow-up", "follow_ups_new": "Neues Follow-up",
"follow_ups_upgrade_button_text": "Upgrade, um Follow-ups zu aktivieren", "follow_ups_upgrade_button_text": "Upgrade, um Follow-ups zu aktivieren",
"form_styling": "Umfrage Styling",
"formbricks_sdk_is_not_connected": "Formbricks SDK ist nicht verbunden", "formbricks_sdk_is_not_connected": "Formbricks SDK ist nicht verbunden",
"four_points": "4 Punkte", "four_points": "4 Punkte",
"heading": "Überschrift", "heading": "Überschrift",
@@ -1542,7 +1520,7 @@
"option_idx": "Option {choiceIndex}", "option_idx": "Option {choiceIndex}",
"option_used_in_logic_error": "Diese Option wird in der Logik der Frage {questionIndex} verwendet. Bitte entferne sie zuerst aus der Logik.", "option_used_in_logic_error": "Diese Option wird in der Logik der Frage {questionIndex} verwendet. Bitte entferne sie zuerst aus der Logik.",
"optional": "Optional", "optional": "Optional",
"options": "Optionen*", "options": "Optionen",
"options_used_in_logic_bulk_error": "Die folgenden Optionen werden in der Logik verwendet: {questionIndexes}. Bitte entferne sie zuerst aus der Logik.", "options_used_in_logic_bulk_error": "Die folgenden Optionen werden in der Logik verwendet: {questionIndexes}. Bitte entferne sie zuerst aus der Logik.",
"override_theme_with_individual_styles_for_this_survey": "Styling für diese Umfrage überschreiben.", "override_theme_with_individual_styles_for_this_survey": "Styling für diese Umfrage überschreiben.",
"overwrite_global_waiting_time": "Benutzerdefinierte Abkühlphase festlegen", "overwrite_global_waiting_time": "Benutzerdefinierte Abkühlphase festlegen",
@@ -1567,7 +1545,6 @@
"question_deleted": "Frage gelöscht.", "question_deleted": "Frage gelöscht.",
"question_duplicated": "Frage dupliziert.", "question_duplicated": "Frage dupliziert.",
"question_id_updated": "Frage-ID aktualisiert", "question_id_updated": "Frage-ID aktualisiert",
"question_number": "Frage {number}",
"question_used_in_logic_warning_text": "Elemente aus diesem Block werden in einer Logikregel verwendet. Möchten Sie ihn wirklich löschen?", "question_used_in_logic_warning_text": "Elemente aus diesem Block werden in einer Logikregel verwendet. Möchten Sie ihn wirklich löschen?",
"question_used_in_logic_warning_title": "Logikinkonsistenz", "question_used_in_logic_warning_title": "Logikinkonsistenz",
"question_used_in_quota": "Diese Frage wird in der “{quotaName}” Quote verwendet", "question_used_in_quota": "Diese Frage wird in der “{quotaName}” Quote verwendet",
@@ -1626,7 +1603,7 @@
"response_limits_redirections_and_more": "Antwort Limits, Weiterleitungen und mehr.", "response_limits_redirections_and_more": "Antwort Limits, Weiterleitungen und mehr.",
"response_options": "Antwortoptionen", "response_options": "Antwortoptionen",
"roundness": "Rundheit", "roundness": "Rundheit",
"roundness_description": "Steuert, wie abgerundet die Ecken sind.", "roundness_description": "Steuert, wie abgerundet die Kartenecken sind.",
"row_used_in_logic_error": "Diese Zeile wird in der Logik der Frage {questionIndex} verwendet. Bitte entferne sie zuerst aus der Logik.", "row_used_in_logic_error": "Diese Zeile wird in der Logik der Frage {questionIndex} verwendet. Bitte entferne sie zuerst aus der Logik.",
"rows": "Zeilen", "rows": "Zeilen",
"save_and_close": "Speichern & Schließen", "save_and_close": "Speichern & Schließen",
@@ -1672,7 +1649,6 @@
"survey_completed_subheading": "Diese kostenlose und quelloffene Umfrage wurde geschlossen", "survey_completed_subheading": "Diese kostenlose und quelloffene Umfrage wurde geschlossen",
"survey_display_settings": "Einstellungen zur Anzeige der Umfrage", "survey_display_settings": "Einstellungen zur Anzeige der Umfrage",
"survey_placement": "Platzierung der Umfrage", "survey_placement": "Platzierung der Umfrage",
"survey_styling": "Umfrage Styling",
"survey_trigger": "Auslöser der Umfrage", "survey_trigger": "Auslöser der Umfrage",
"switch_multi_language_on_to_get_started": "Aktiviere Mehrsprachigkeit, um loszulegen 👉", "switch_multi_language_on_to_get_started": "Aktiviere Mehrsprachigkeit, um loszulegen 👉",
"target_block_not_found": "Zielblock nicht gefunden", "target_block_not_found": "Zielblock nicht gefunden",
@@ -1735,7 +1711,7 @@
"pattern": "Entspricht Regex-Muster", "pattern": "Entspricht Regex-Muster",
"phone": "Ist gültige Telefonnummer", "phone": "Ist gültige Telefonnummer",
"rank_all_options": "Alle Optionen bewerten", "rank_all_options": "Alle Optionen bewerten",
"select_file_extensions": "Dateierweiterungen auswählen", "select_file_extensions": "Dateierweiterungen auswählen...",
"select_option": "Option auswählen", "select_option": "Option auswählen",
"start_date": "Startdatum", "start_date": "Startdatum",
"url": "Ist gültige URL" "url": "Ist gültige URL"
@@ -1761,9 +1737,9 @@
"waiting_time_across_surveys": "Abkühlphase (umfrageübergreifend)", "waiting_time_across_surveys": "Abkühlphase (umfrageübergreifend)",
"waiting_time_across_surveys_description": "Um Umfragemüdigkeit zu vermeiden, wähle aus, wie diese Umfrage mit der workspace-weiten Abkühlphase interagiert.", "waiting_time_across_surveys_description": "Um Umfragemüdigkeit zu vermeiden, wähle aus, wie diese Umfrage mit der workspace-weiten Abkühlphase interagiert.",
"welcome_message": "Willkommensnachricht", "welcome_message": "Willkommensnachricht",
"when": "Wenn",
"without_a_filter_all_of_your_users_can_be_surveyed": "Ohne Filter können alle deine Nutzer befragt werden.", "without_a_filter_all_of_your_users_can_be_surveyed": "Ohne Filter können alle deine Nutzer befragt werden.",
"you_have_not_created_a_segment_yet": "Du hast noch keinen Segment erstellt.", "you_have_not_created_a_segment_yet": "Du hast noch keinen Segment erstellt.",
"you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "Sie müssen zwei oder mehr Sprachen in Ihrem Workspace eingerichtet haben, um mit Übersetzungen zu arbeiten.",
"your_description_here_recall_information_with": "Deine Beschreibung hier. Informationen abrufen mit @", "your_description_here_recall_information_with": "Deine Beschreibung hier. Informationen abrufen mit @",
"your_question_here_recall_information_with": "Deine Frage hier. Informationen abrufen mit @", "your_question_here_recall_information_with": "Deine Frage hier. Informationen abrufen mit @",
"your_web_app": "Deine Web-App", "your_web_app": "Deine Web-App",
@@ -1833,7 +1809,7 @@
"disable_single_use_link_modal_button": "Einmalige Links deaktivieren", "disable_single_use_link_modal_button": "Einmalige Links deaktivieren",
"disable_single_use_link_modal_description": "Wenn Sie Einweglinks geteilt haben, können die Teilnehmer nicht mehr auf die Umfrage antworten.", "disable_single_use_link_modal_description": "Wenn Sie Einweglinks geteilt haben, können die Teilnehmer nicht mehr auf die Umfrage antworten.",
"generate_and_download_links": "Links generieren und herunterladen", "generate_and_download_links": "Links generieren und herunterladen",
"generate_links_error": "Einmalige Links konnten nicht generiert werden. Bitte arbeiten Sie direkt mit der API.", "generate_links_error": "Einmalige Verlinkungen konnten nicht generiert werden. Bitte arbeiten Sie direkt mit der API.",
"multi_use_link": "Mehrfach verwendet", "multi_use_link": "Mehrfach verwendet",
"multi_use_link_description": "Sammle mehrere Antworten von anonymen Teilnehmern mit einem Link", "multi_use_link_description": "Sammle mehrere Antworten von anonymen Teilnehmern mit einem Link",
"multi_use_powers_other_channels_description": "Wenn du es deaktivierst, werden auch diese anderen Vertriebskanäle deaktiviert.", "multi_use_powers_other_channels_description": "Wenn du es deaktivierst, werden auch diese anderen Vertriebskanäle deaktiviert.",
@@ -1944,7 +1920,7 @@
} }
}, },
"summary": { "summary": {
"added_filter_for_responses_where_answer_to_question": "Filter hinzugefügt für Antworten, bei denen die Antwort auf Frage {questionIdx} {filterComboBoxValue} - {filterValue} ist.", "added_filter_for_responses_where_answer_to_question": "Filter hinzugefügt für Antworten, bei denen die Antwort auf Frage {questionIdx} {filterComboBoxValue} - {filterValue} ist",
"added_filter_for_responses_where_answer_to_question_is_skipped": "Filter hinzugefügt für Antworten, bei denen die Frage {questionIdx} übersprungen wurde", "added_filter_for_responses_where_answer_to_question_is_skipped": "Filter hinzugefügt für Antworten, bei denen die Frage {questionIdx} übersprungen wurde",
"aggregated": "Aggregiert", "aggregated": "Aggregiert",
"all_responses_csv": "Alle Antworten (CSV)", "all_responses_csv": "Alle Antworten (CSV)",
@@ -1971,7 +1947,6 @@
"filtered_responses_excel": "Gefilterte Antworten (Excel)", "filtered_responses_excel": "Gefilterte Antworten (Excel)",
"generating_qr_code": "QR-Code wird generiert", "generating_qr_code": "QR-Code wird generiert",
"impressions": "Eindrücke", "impressions": "Eindrücke",
"impressions_identified_only": "Zeigt nur Impressionen von identifizierten Kontakten",
"impressions_tooltip": "Anzahl der Aufrufe der Umfrage.", "impressions_tooltip": "Anzahl der Aufrufe der Umfrage.",
"in_app": { "in_app": {
"connection_description": "Die Umfrage wird den Nutzern Ihrer Website angezeigt, die den unten aufgeführten Kriterien entsprechen", "connection_description": "Die Umfrage wird den Nutzern Ihrer Website angezeigt, die den unten aufgeführten Kriterien entsprechen",
@@ -2014,7 +1989,6 @@
"last_quarter": "Letztes Quartal", "last_quarter": "Letztes Quartal",
"last_year": "Letztes Jahr", "last_year": "Letztes Jahr",
"limit": "Limit", "limit": "Limit",
"no_identified_impressions": "Keine Impressionen von identifizierten Kontakten",
"no_responses_found": "Keine Antworten gefunden", "no_responses_found": "Keine Antworten gefunden",
"other_values_found": "Andere Werte gefunden", "other_values_found": "Andere Werte gefunden",
"overall": "Insgesamt", "overall": "Insgesamt",
@@ -2037,7 +2011,6 @@
"starts": "Startet", "starts": "Startet",
"starts_tooltip": "So oft wurde die Umfrage gestartet.", "starts_tooltip": "So oft wurde die Umfrage gestartet.",
"survey_reset_successfully": "Umfrage erfolgreich zurückgesetzt! {responseCount} Antworten und {displayCount} Anzeigen wurden gelöscht.", "survey_reset_successfully": "Umfrage erfolgreich zurückgesetzt! {responseCount} Antworten und {displayCount} Anzeigen wurden gelöscht.",
"survey_results": "{surveyName}-Ergebnisse",
"this_month": "Dieser Monat", "this_month": "Dieser Monat",
"this_quarter": "Dieses Quartal", "this_quarter": "Dieses Quartal",
"this_year": "Dieses Jahr", "this_year": "Dieses Jahr",
@@ -2157,7 +2130,7 @@
"add_background_color_description": "Füge dem Logo-Container eine Hintergrundfarbe hinzu.", "add_background_color_description": "Füge dem Logo-Container eine Hintergrundfarbe hinzu.",
"advanced_styling_field_border_radius": "Rahmenradius", "advanced_styling_field_border_radius": "Rahmenradius",
"advanced_styling_field_button_bg": "Button-Hintergrund", "advanced_styling_field_button_bg": "Button-Hintergrund",
"advanced_styling_field_button_bg_description": "Füllt die Schaltfläche “Weiter” / “Absenden.", "advanced_styling_field_button_bg_description": "Füllt den Weiter-/Absenden-Button.",
"advanced_styling_field_button_border_radius_description": "Rundet die Button-Ecken ab.", "advanced_styling_field_button_border_radius_description": "Rundet die Button-Ecken ab.",
"advanced_styling_field_button_font_size_description": "Skaliert den Text der Button-Beschriftung.", "advanced_styling_field_button_font_size_description": "Skaliert den Text der Button-Beschriftung.",
"advanced_styling_field_button_font_weight_description": "Macht den Button-Text heller oder fetter.", "advanced_styling_field_button_font_weight_description": "Macht den Button-Text heller oder fetter.",
@@ -2171,7 +2144,7 @@
"advanced_styling_field_description_size": "Schriftgröße der Beschreibung", "advanced_styling_field_description_size": "Schriftgröße der Beschreibung",
"advanced_styling_field_description_size_description": "Skaliert den Beschreibungstext.", "advanced_styling_field_description_size_description": "Skaliert den Beschreibungstext.",
"advanced_styling_field_description_weight": "Schriftstärke der Beschreibung", "advanced_styling_field_description_weight": "Schriftstärke der Beschreibung",
"advanced_styling_field_description_weight_description": "Macht den Beschreibungstext leichter oder fetter.", "advanced_styling_field_description_weight_description": "Macht den Beschreibungstext heller oder fetter.",
"advanced_styling_field_font_size": "Schriftgröße", "advanced_styling_field_font_size": "Schriftgröße",
"advanced_styling_field_font_weight": "Schriftstärke", "advanced_styling_field_font_weight": "Schriftstärke",
"advanced_styling_field_headline_color": "Überschriftsfarbe", "advanced_styling_field_headline_color": "Überschriftsfarbe",
@@ -2185,7 +2158,7 @@
"advanced_styling_field_indicator_bg_description": "Färbt den gefüllten Teil des Balkens.", "advanced_styling_field_indicator_bg_description": "Färbt den gefüllten Teil des Balkens.",
"advanced_styling_field_input_border_radius_description": "Rundet die Eingabeecken ab.", "advanced_styling_field_input_border_radius_description": "Rundet die Eingabeecken ab.",
"advanced_styling_field_input_font_size_description": "Skaliert den eingegebenen Text in Eingabefeldern.", "advanced_styling_field_input_font_size_description": "Skaliert den eingegebenen Text in Eingabefeldern.",
"advanced_styling_field_input_height_description": "Bestimmt die Mindesthöhe des Eingabefelds.", "advanced_styling_field_input_height_description": "Legt die Mindesthöhe des Eingabefelds fest.",
"advanced_styling_field_input_padding_x_description": "Fügt links und rechts Abstand hinzu.", "advanced_styling_field_input_padding_x_description": "Fügt links und rechts Abstand hinzu.",
"advanced_styling_field_input_padding_y_description": "Fügt oben und unten Abstand hinzu.", "advanced_styling_field_input_padding_y_description": "Fügt oben und unten Abstand hinzu.",
"advanced_styling_field_input_placeholder_opacity_description": "Blendet den Platzhaltertext aus.", "advanced_styling_field_input_placeholder_opacity_description": "Blendet den Platzhaltertext aus.",
@@ -2194,8 +2167,6 @@
"advanced_styling_field_input_text_description": "Färbt den eingegebenen Text in Eingabefeldern.", "advanced_styling_field_input_text_description": "Färbt den eingegebenen Text in Eingabefeldern.",
"advanced_styling_field_option_bg": "Hintergrund", "advanced_styling_field_option_bg": "Hintergrund",
"advanced_styling_field_option_bg_description": "Füllt die Optionselemente.", "advanced_styling_field_option_bg_description": "Füllt die Optionselemente.",
"advanced_styling_field_option_border": "Rahmenfarbe",
"advanced_styling_field_option_border_description": "Umrandet Radio- und Checkbox-Optionen.",
"advanced_styling_field_option_border_radius_description": "Rundet die Ecken der Optionen ab.", "advanced_styling_field_option_border_radius_description": "Rundet die Ecken der Optionen ab.",
"advanced_styling_field_option_font_size_description": "Skaliert den Text der Optionsbeschriftung.", "advanced_styling_field_option_font_size_description": "Skaliert den Text der Optionsbeschriftung.",
"advanced_styling_field_option_label": "Label-Farbe", "advanced_styling_field_option_label": "Label-Farbe",
@@ -2250,7 +2221,7 @@
"show_powered_by_formbricks": "\"Powered by Formbricks\"-Signatur anzeigen", "show_powered_by_formbricks": "\"Powered by Formbricks\"-Signatur anzeigen",
"styling_updated_successfully": "Styling erfolgreich aktualisiert", "styling_updated_successfully": "Styling erfolgreich aktualisiert",
"suggest_colors": "Farben vorschlagen", "suggest_colors": "Farben vorschlagen",
"suggested_colors_applied_please_save": "Vorgeschlagene Farben erfolgreich generiert. Drücke Speichern, um die Änderungen zu übernehmen.", "suggested_colors_applied_please_save": "Vorgeschlagene Farben erfolgreich generiert. Drücke \"Speichern\", um die Änderungen zu übernehmen.",
"theme": "Theme", "theme": "Theme",
"theme_settings_description": "Erstelle ein Style-Theme für alle Umfragen. Du kannst für jede Umfrage individuelles Styling aktivieren." "theme_settings_description": "Erstelle ein Style-Theme für alle Umfragen. Du kannst für jede Umfrage individuelles Styling aktivieren."
}, },
@@ -2334,7 +2305,7 @@
"check_inbox_or_spam": "Bitte überprüfe auch deinen Spam-Ordner, falls Du die E-Mail nicht in deinem Posteingang siehst.", "check_inbox_or_spam": "Bitte überprüfe auch deinen Spam-Ordner, falls Du die E-Mail nicht in deinem Posteingang siehst.",
"completed": "Diese kostenlose und quelloffene Umfrage wurde geschlossen.", "completed": "Diese kostenlose und quelloffene Umfrage wurde geschlossen.",
"create_your_own": "Erstelle deine eigene", "create_your_own": "Erstelle deine eigene",
"enter_pin": "Diese Umfrage ist geschützt. Gib die PIN unten ein.", "enter_pin": "Diese Umfrage ist geschützt. Gib unten die PIN ein",
"just_curious": "Einfach neugierig?", "just_curious": "Einfach neugierig?",
"link_invalid": "Diese Umfrage kann nur auf Einladung durchgeführt werden.", "link_invalid": "Diese Umfrage kann nur auf Einladung durchgeführt werden.",
"paused": "Diese Umfrage ist vorübergehend pausiert.", "paused": "Diese Umfrage ist vorübergehend pausiert.",
@@ -2405,16 +2376,6 @@
"alignment_and_engagement_survey_question_4_headline": "Wie kann das Unternehmen seine Vision und strategische Ausrichtung verbessern?", "alignment_and_engagement_survey_question_4_headline": "Wie kann das Unternehmen seine Vision und strategische Ausrichtung verbessern?",
"alignment_and_engagement_survey_question_4_placeholder": "Tippe deine Antwort hier...", "alignment_and_engagement_survey_question_4_placeholder": "Tippe deine Antwort hier...",
"back": "Zurück", "back": "Zurück",
"block_1": "Block 1",
"block_10": "Block 10",
"block_2": "Block 2",
"block_3": "Block 3",
"block_4": "Block 4",
"block_5": "Block 5",
"block_6": "Block 6",
"block_7": "Block 7",
"block_8": "Block 8",
"block_9": "Block 9",
"book_interview": "Interview buchen", "book_interview": "Interview buchen",
"build_product_roadmap_description": "Finde die EINE Sache heraus, die deine Nutzer am meisten wollen, und baue sie.", "build_product_roadmap_description": "Finde die EINE Sache heraus, die deine Nutzer am meisten wollen, und baue sie.",
"build_product_roadmap_name": "Produkt Roadmap erstellen", "build_product_roadmap_name": "Produkt Roadmap erstellen",
@@ -2570,7 +2531,7 @@
"csat_question_3_choice_8": "Schlechte Qualität", "csat_question_3_choice_8": "Schlechte Qualität",
"csat_question_3_choice_9": "Unzuverlässig", "csat_question_3_choice_9": "Unzuverlässig",
"csat_question_3_headline": "Welches der folgenden Wörter würdest Du verwenden, um unser $[projectName] zu beschreiben?", "csat_question_3_headline": "Welches der folgenden Wörter würdest Du verwenden, um unser $[projectName] zu beschreiben?",
"csat_question_3_subheader": "Bitte wähle alle zutreffenden Optionen aus:", "csat_question_3_subheader": "Wähle alle zutreffenden aus:",
"csat_question_4_choice_1": "Extrem gut", "csat_question_4_choice_1": "Extrem gut",
"csat_question_4_choice_2": "Sehr gut", "csat_question_4_choice_2": "Sehr gut",
"csat_question_4_choice_3": "Ziemlich gut", "csat_question_4_choice_3": "Ziemlich gut",
@@ -2622,6 +2583,7 @@
"csat_survey_question_3_headline": "Ugh, sorry! Können wir irgendwas tun, um deine Erfahrung zu verbessern?", "csat_survey_question_3_headline": "Ugh, sorry! Können wir irgendwas tun, um deine Erfahrung zu verbessern?",
"csat_survey_question_3_placeholder": "Tippe deine Antwort hier...", "csat_survey_question_3_placeholder": "Tippe deine Antwort hier...",
"cta_description": "Information anzeigen und Benutzer auffordern, eine bestimmte Aktion auszuführen", "cta_description": "Information anzeigen und Benutzer auffordern, eine bestimmte Aktion auszuführen",
"custom_survey_block_1_name": "Block 1",
"custom_survey_description": "Erstelle eine Umfrage ohne Vorlage.", "custom_survey_description": "Erstelle eine Umfrage ohne Vorlage.",
"custom_survey_name": "Eigene Umfrage erstellen", "custom_survey_name": "Eigene Umfrage erstellen",
"custom_survey_question_1_headline": "Was möchtest Du wissen?", "custom_survey_question_1_headline": "Was möchtest Du wissen?",
@@ -3024,9 +2986,6 @@
"preview_survey_question_2_choice_2_label": "Nein, danke!", "preview_survey_question_2_choice_2_label": "Nein, danke!",
"preview_survey_question_2_headline": "Möchtest Du auf dem Laufenden bleiben?", "preview_survey_question_2_headline": "Möchtest Du auf dem Laufenden bleiben?",
"preview_survey_question_2_subheader": "Dies ist eine Beispielbeschreibung.", "preview_survey_question_2_subheader": "Dies ist eine Beispielbeschreibung.",
"preview_survey_question_open_text_headline": "Möchtest Du noch etwas teilen?",
"preview_survey_question_open_text_placeholder": "Tippe deine Antwort hier...",
"preview_survey_question_open_text_subheader": "Dein Feedback hilft uns, besser zu werden.",
"preview_survey_welcome_card_headline": "Willkommen!", "preview_survey_welcome_card_headline": "Willkommen!",
"prioritize_features_description": "Identifiziere die Funktionen, die deine Nutzer am meisten und am wenigsten brauchen.", "prioritize_features_description": "Identifiziere die Funktionen, die deine Nutzer am meisten und am wenigsten brauchen.",
"prioritize_features_name": "Funktionen priorisieren", "prioritize_features_name": "Funktionen priorisieren",
@@ -3096,7 +3055,7 @@
"professional_development_survey_question_2_choice_5": "Individuelle Forschung", "professional_development_survey_question_2_choice_5": "Individuelle Forschung",
"professional_development_survey_question_2_choice_6": "Andere", "professional_development_survey_question_2_choice_6": "Andere",
"professional_development_survey_question_2_headline": "Welche Arten von beruflichen Entwicklungsmöglichkeiten denken Sie, wären am wertvollsten für Ihre Weiterentwicklung?", "professional_development_survey_question_2_headline": "Welche Arten von beruflichen Entwicklungsmöglichkeiten denken Sie, wären am wertvollsten für Ihre Weiterentwicklung?",
"professional_development_survey_question_2_subheader": "Bitte wähle alle zutreffenden Optionen aus:", "professional_development_survey_question_2_subheader": "Bitte wählen Sie alle zutreffenden Optionen aus:",
"professional_development_survey_question_3_choice_1": "Ja", "professional_development_survey_question_3_choice_1": "Ja",
"professional_development_survey_question_3_choice_2": "Nein", "professional_development_survey_question_3_choice_2": "Nein",
"professional_development_survey_question_3_headline": "Haben Sie in der Vergangenheit Zeit für Ihre berufliche Weiterentwicklung eingeplant?", "professional_development_survey_question_3_headline": "Haben Sie in der Vergangenheit Zeit für Ihre berufliche Weiterentwicklung eingeplant?",
@@ -3275,18 +3234,5 @@
"usability_question_9_headline": "Ich fühlte mich beim Benutzen des Systems sicher.", "usability_question_9_headline": "Ich fühlte mich beim Benutzen des Systems sicher.",
"usability_rating_description": "Bewerte die wahrgenommene Benutzerfreundlichkeit, indem du die Nutzer bittest, ihre Erfahrung mit deinem Produkt mittels eines standardisierten 10-Fragen-Fragebogens zu bewerten.", "usability_rating_description": "Bewerte die wahrgenommene Benutzerfreundlichkeit, indem du die Nutzer bittest, ihre Erfahrung mit deinem Produkt mittels eines standardisierten 10-Fragen-Fragebogens zu bewerten.",
"usability_score_name": "System Usability Score Survey (SUS)" "usability_score_name": "System Usability Score Survey (SUS)"
},
"workflows": {
"coming_soon_description": "Danke, dass du deine Workflow-Idee mit uns geteilt hast! Wir arbeiten gerade an diesem Feature und dein Feedback hilft uns dabei, genau das zu entwickeln, was du brauchst.",
"coming_soon_title": "Wir sind fast da!",
"follow_up_label": "Gibt es noch etwas, das du hinzufügen möchtest?",
"follow_up_placeholder": "Welche konkreten Aufgaben möchten Sie automatisieren? Gibt es Tools oder Integrationen, die Sie einbinden möchten?",
"generate_button": "Workflow generieren",
"heading": "Welchen Workflow möchtest du erstellen?",
"placeholder": "Beschreiben Sie den Workflow, den Sie erstellen möchten…",
"subheading": "Generiere deinen Workflow in Sekunden.",
"submit_button": "Details hinzufügen",
"thank_you_description": "Ihr Feedback hilft uns, die Workflows-Funktion so zu gestalten, wie Sie sie brauchen. Wir halten Sie über unseren Fortschritt auf dem Laufenden.",
"thank_you_title": "Danke für dein Feedback!"
} }
} }
+34 -88
View File
@@ -175,12 +175,9 @@
"copy": "Copy", "copy": "Copy",
"copy_code": "Copy code", "copy_code": "Copy code",
"copy_link": "Copy Link", "copy_link": "Copy Link",
"count_attributes": "{count, plural, one {{count} attribute} other {{count} attributes}}", "count_attributes": "{value, plural, one {{value} attribute} other {{value} attributes}}",
"count_contacts": "{count, plural, one {{count} contact} other {{count} contacts}}", "count_contacts": "{value, plural, one {{value} contact} other {{value} contacts}}",
"count_members": "{count, plural, one {{count} member} other {{count} members}}", "count_responses": "{value, plural, one {{value} response} other {{value} responses}}",
"count_questions": "{count, plural, one {{count} question} other {{count} questions}}",
"count_responses": "{count, plural, one {{count} response} other {{count} responses}}",
"count_selections": "{count, plural, one {{count} selection} other {{count} selections}}",
"create_new_organization": "Create new organization", "create_new_organization": "Create new organization",
"create_segment": "Create segment", "create_segment": "Create segment",
"create_survey": "Create survey", "create_survey": "Create survey",
@@ -194,7 +191,6 @@
"days": "days", "days": "days",
"default": "Default", "default": "Default",
"delete": "Delete", "delete": "Delete",
"delete_what": "Delete {deleteWhat}",
"description": "Description", "description": "Description",
"dev_env": "Dev Environment", "dev_env": "Dev Environment",
"development": "Development", "development": "Development",
@@ -210,8 +206,6 @@
"download": "Download", "download": "Download",
"draft": "Draft", "draft": "Draft",
"duplicate": "Duplicate", "duplicate": "Duplicate",
"duplicate_copy": "(copy)",
"duplicate_copy_number": "(copy {copyNumber})",
"e_commerce": "E-Commerce", "e_commerce": "E-Commerce",
"edit": "Edit", "edit": "Edit",
"email": "Email", "email": "Email",
@@ -224,16 +218,13 @@
"error": "Error", "error": "Error",
"error_component_description": "This resource does not exist or you do not have the necessary rights to access it.", "error_component_description": "This resource does not exist or you do not have the necessary rights to access it.",
"error_component_title": "Error loading resources", "error_component_title": "Error loading resources",
"error_loading_data": "Error loading data",
"error_rate_limit_description": "Maximum number of requests reached. Please try again later.", "error_rate_limit_description": "Maximum number of requests reached. Please try again later.",
"error_rate_limit_title": "Rate Limit Exceeded", "error_rate_limit_title": "Rate Limit Exceeded",
"expand_rows": "Expand rows", "expand_rows": "Expand rows",
"failed_to_copy_to_clipboard": "Failed to copy to clipboard", "failed_to_copy_to_clipboard": "Failed to copy to clipboard",
"failed_to_load_organizations": "Failed to load organizations", "failed_to_load_organizations": "Failed to load organizations",
"failed_to_load_workspaces": "Failed to load workspaces", "failed_to_load_workspaces": "Failed to load workspaces",
"filter": "Filter",
"finish": "Finish", "finish": "Finish",
"first_name": "First Name",
"follow_these": "Follow these", "follow_these": "Follow these",
"formbricks_version": "Formbricks Version", "formbricks_version": "Formbricks Version",
"full_name": "Full name", "full_name": "Full name",
@@ -246,7 +237,6 @@
"hidden_field": "Hidden field", "hidden_field": "Hidden field",
"hidden_fields": "Hidden fields", "hidden_fields": "Hidden fields",
"hide_column": "Hide column", "hide_column": "Hide column",
"id": "ID",
"image": "Image", "image": "Image",
"images": "Images", "images": "Images",
"import": "Import", "import": "Import",
@@ -264,7 +254,6 @@
"key": "Key", "key": "Key",
"label": "Label", "label": "Label",
"language": "Language", "language": "Language",
"last_name": "Last Name",
"learn_more": "Learn more", "learn_more": "Learn more",
"license_expired": "License Expired", "license_expired": "License Expired",
"light_overlay": "Light overlay", "light_overlay": "Light overlay",
@@ -279,6 +268,7 @@
"look_and_feel": "Look & Feel", "look_and_feel": "Look & Feel",
"manage": "Manage", "manage": "Manage",
"marketing": "Marketing", "marketing": "Marketing",
"member": "Member",
"members": "Members", "members": "Members",
"members_and_teams": "Members & Teams", "members_and_teams": "Members & Teams",
"membership_not_found": "Membership not found", "membership_not_found": "Membership not found",
@@ -290,7 +280,6 @@
"move_down": "Move down", "move_down": "Move down",
"move_up": "Move up", "move_up": "Move up",
"multiple_languages": "Multiple languages", "multiple_languages": "Multiple languages",
"my_product": "my Product",
"name": "Name", "name": "Name",
"new": "New", "new": "New",
"new_version_available": "Formbricks {version} is here. Upgrade now!", "new_version_available": "Formbricks {version} is here. Upgrade now!",
@@ -386,6 +375,8 @@
"select_teams": "Select teams", "select_teams": "Select teams",
"selected": "Selected", "selected": "Selected",
"selected_questions": "Selected questions", "selected_questions": "Selected questions",
"selection": "Selection",
"selections": "Selections",
"send_test_email": "Send test email", "send_test_email": "Send test email",
"session_not_found": "Session not found", "session_not_found": "Session not found",
"settings": "Settings", "settings": "Settings",
@@ -394,7 +385,6 @@
"show_response_count": "Show response count", "show_response_count": "Show response count",
"shown": "Shown", "shown": "Shown",
"size": "Size", "size": "Size",
"skip": "Skip",
"skipped": "Skipped", "skipped": "Skipped",
"skips": "Skips", "skips": "Skips",
"some_files_failed_to_upload": "Some files failed to upload", "some_files_failed_to_upload": "Some files failed to upload",
@@ -438,7 +428,6 @@
"top_right": "Top Right", "top_right": "Top Right",
"try_again": "Try again", "try_again": "Try again",
"type": "Type", "type": "Type",
"unknown_survey": "Unknown survey",
"unlock_more_workspaces_with_a_higher_plan": "Unlock more workspaces with a higher plan.", "unlock_more_workspaces_with_a_higher_plan": "Unlock more workspaces with a higher plan.",
"update": "Update", "update": "Update",
"updated": "Updated", "updated": "Updated",
@@ -464,7 +453,6 @@
"website_survey": "Website Survey", "website_survey": "Website Survey",
"weeks": "weeks", "weeks": "weeks",
"welcome_card": "Welcome card", "welcome_card": "Welcome card",
"workflows": "Workflows",
"workspace_configuration": "Workspace Configuration", "workspace_configuration": "Workspace Configuration",
"workspace_created_successfully": "Workspace created successfully", "workspace_created_successfully": "Workspace created successfully",
"workspace_creation_description": "Organize surveys in workspaces for better access control.", "workspace_creation_description": "Organize surveys in workspaces for better access control.",
@@ -647,27 +635,27 @@
"attribute_value": "Value", "attribute_value": "Value",
"attribute_value_placeholder": "Attribute Value", "attribute_value_placeholder": "Attribute Value",
"attributes_msg_attribute_limit_exceeded": "Could not create {count} new attribute(s) as it would exceed the maximum limit of {limit} attribute classes. Existing attributes were updated successfully.", "attributes_msg_attribute_limit_exceeded": "Could not create {count} new attribute(s) as it would exceed the maximum limit of {limit} attribute classes. Existing attributes were updated successfully.",
"attributes_msg_attribute_type_validation_error": "{error} (attribute {key} has dataType: {dataType})", "attributes_msg_attribute_type_validation_error": "{error} (attribute '{key}' has dataType: {dataType})",
"attributes_msg_email_already_exists": "The email already exists for this environment and was not updated.", "attributes_msg_email_already_exists": "The email already exists for this environment and was not updated.",
"attributes_msg_email_or_userid_required": "Either email or user ID is required. The existing values were preserved.", "attributes_msg_email_or_userid_required": "Either email or userId is required. The existing values were preserved.",
"attributes_msg_new_attribute_created": "Created new attribute {key} with type {dataType}", "attributes_msg_new_attribute_created": "Created new attribute '{key}' with type '{dataType}'",
"attributes_msg_userid_already_exists": "The user ID already exists for this environment and was not updated.", "attributes_msg_userid_already_exists": "The userId already exists for this environment and was not updated.",
"contact_deleted_successfully": "Contact deleted successfully", "contact_deleted_successfully": "Contact deleted successfully",
"contact_not_found": "No such contact found", "contact_not_found": "No such contact found",
"contacts_table_refresh": "Refresh contacts", "contacts_table_refresh": "Refresh contacts",
"contacts_table_refresh_success": "Contacts refreshed successfully", "contacts_table_refresh_success": "Contacts refreshed successfully",
"create_attribute": "Create attribute", "create_attribute": "Create attribute",
"create_key": "Create Key",
"create_new_attribute": "Create new attribute", "create_new_attribute": "Create new attribute",
"create_new_attribute_description": "Create a new attribute for segmentation purposes.", "create_new_attribute_description": "Create a new attribute for segmentation purposes.",
"custom_attributes": "Custom Attributes", "custom_attributes": "Custom Attributes",
"data_type": "Data Type", "data_type": "Data Type",
"data_type_cannot_be_changed": "Data type cannot be changed after creation", "data_type_cannot_be_changed": "Data type cannot be changed after creation",
"data_type_description": "Choose how this attribute should be stored and filtered", "data_type_description": "Choose how this attribute should be stored and filtered",
"date_value_required": "Date value is required. Use the delete button to remove this attribute if you do not want to set a date.", "date_value_required": "Date value is required. Use the delete button to remove this attribute if you don't want to set a date.",
"delete_attribute_confirmation": "{value, plural, one {This will delete the selected attribute. Any contact data associated with this attribute will be lost.} other {This will delete the selected attributes. Any contact data associated with these attributes will be lost.}}", "delete_attribute_confirmation": "{value, plural, one {This will delete the selected attribute. Any contact data associated with this attribute will be lost.} other {This will delete the selected attributes. Any contact data associated with these attributes will be lost.}}",
"delete_contact_confirmation": "This will delete all survey responses and contact attributes associated with this contact. Any targeting and personalization based on this contacts data will be lost.", "delete_contact_confirmation": "This will delete all survey responses and contact attributes associated with this contact. Any targeting and personalization based on this contacts data will be lost.",
"delete_contact_confirmation_with_quotas": "{value, plural, one {This will delete all survey responses and contact attributes associated with this contact. Any targeting and personalization based on this contacts data will be lost. If this contact has responses that count towards survey quotas, the quota counts will be reduced but the quota limits will remain unchanged.} other {This will delete all survey responses and contact attributes associated with these contacts. Any targeting and personalization based on these contacts data will be lost. If these contacts have responses that count towards survey quotas, the quota counts will be reduced but the quota limits will remain unchanged.}}", "delete_contact_confirmation_with_quotas": "{value, plural, one {This will delete all survey responses and contact attributes associated with this contact. Any targeting and personalization based on this contacts data will be lost. If this contact has responses that count towards survey quotas, the quota counts will be reduced but the quota limits will remain unchanged.} other {This will delete all survey responses and contact attributes associated with these contacts. Any targeting and personalization based on these contacts data will be lost. If these contacts have responses that count towards survey quotas, the quota counts will be reduced but the quota limits will remain unchanged.}}",
"displays": "Displays",
"edit_attribute": "Edit attribute", "edit_attribute": "Edit attribute",
"edit_attribute_description": "Update the label and description for this attribute.", "edit_attribute_description": "Update the label and description for this attribute.",
"edit_attribute_values": "Edit attributes", "edit_attribute_values": "Edit attributes",
@@ -679,7 +667,6 @@
"invalid_csv_column_names": "Invalid CSV column name(s): {columns}. Column names that will become new attributes must only contain lowercase letters, numbers, and underscores, and must start with a letter.", "invalid_csv_column_names": "Invalid CSV column name(s): {columns}. Column names that will become new attributes must only contain lowercase letters, numbers, and underscores, and must start with a letter.",
"invalid_date_format": "Invalid date format. Please use a valid date.", "invalid_date_format": "Invalid date format. Please use a valid date.",
"invalid_number_format": "Invalid number format. Please enter a valid number.", "invalid_number_format": "Invalid number format. Please enter a valid number.",
"no_activity_yet": "No activity yet",
"no_published_link_surveys_available": "No published link surveys available. Please publish a link survey first.", "no_published_link_surveys_available": "No published link surveys available. Please publish a link survey first.",
"no_published_surveys": "No published surveys", "no_published_surveys": "No published surveys",
"no_responses_found": "No responses found", "no_responses_found": "No responses found",
@@ -694,12 +681,10 @@
"select_a_survey": "Select a survey", "select_a_survey": "Select a survey",
"select_attribute": "Select Attribute", "select_attribute": "Select Attribute",
"select_attribute_key": "Select attribute key", "select_attribute_key": "Select attribute key",
"survey_viewed": "Survey viewed",
"survey_viewed_at": "Viewed At",
"system_attributes": "System Attributes", "system_attributes": "System Attributes",
"unlock_contacts_description": "Manage contacts and send out targeted surveys", "unlock_contacts_description": "Manage contacts and send out targeted surveys",
"unlock_contacts_title": "Unlock contacts with a higher plan", "unlock_contacts_title": "Unlock contacts with a higher plan",
"upload_contacts_error_attribute_type_mismatch": "Attribute {key} is typed as {dataType} but CSV contains invalid values: {values}", "upload_contacts_error_attribute_type_mismatch": "Attribute \"{key}\" is typed as \"{dataType}\" but CSV contains invalid values: {values}",
"upload_contacts_error_duplicate_mappings": "Duplicate mappings found for the following attributes: {attributes}", "upload_contacts_error_duplicate_mappings": "Duplicate mappings found for the following attributes: {attributes}",
"upload_contacts_error_file_too_large": "File size exceeds the maximum limit of 800KB", "upload_contacts_error_file_too_large": "File size exceeds the maximum limit of 800KB",
"upload_contacts_error_generic": "An error occurred while uploading the contacts. Please try again later.", "upload_contacts_error_generic": "An error occurred while uploading the contacts. Please try again later.",
@@ -767,12 +752,7 @@
"link_google_sheet": "Link Google Sheet", "link_google_sheet": "Link Google Sheet",
"link_new_sheet": "Link new Sheet", "link_new_sheet": "Link new Sheet",
"no_integrations_yet": "Your google sheet integrations will appear here as soon as you add them. ⏲️", "no_integrations_yet": "Your google sheet integrations will appear here as soon as you add them. ⏲️",
"reconnect_button": "Reconnect", "spreadsheet_url": "Spreadsheet URL"
"reconnect_button_description": "Your Google Sheets connection has expired. Please reconnect to continue syncing responses. Your existing spreadsheet links and data will be preserved.",
"reconnect_button_tooltip": "Reconnect the integration to refresh your access. Your existing spreadsheet links and data will be preserved.",
"spreadsheet_permission_error": "You do not have permission to access this spreadsheet. Please ensure the spreadsheet is shared with your Google account and you have write access to the spreadsheet.",
"spreadsheet_url": "Spreadsheet URL",
"token_expired_error": "Google Sheets refresh token has expired or been revoked. Please reconnect the integration."
}, },
"include_created_at": "Include Created At", "include_created_at": "Include Created At",
"include_hidden_fields": "Include Hidden Fields", "include_hidden_fields": "Include Hidden Fields",
@@ -981,7 +961,7 @@
"current_plan": "Current Plan", "current_plan": "Current Plan",
"current_tier_limit": "Current Tier Limit", "current_tier_limit": "Current Tier Limit",
"custom": "Custom & Scale", "custom": "Custom & Scale",
"custom_contacts_limit": "Custom Contact Limit", "custom_contacts_limit": "Custom Contacts Limit",
"custom_response_limit": "Custom Response Limit", "custom_response_limit": "Custom Response Limit",
"custom_workspace_limit": "Custom Workspace Limit", "custom_workspace_limit": "Custom Workspace Limit",
"email_embedded_surveys": "Email Embedded Surveys", "email_embedded_surveys": "Email Embedded Surveys",
@@ -1059,7 +1039,7 @@
"recheck_license_invalid": "The license key is invalid. Please verify your ENTERPRISE_LICENSE_KEY.", "recheck_license_invalid": "The license key is invalid. Please verify your ENTERPRISE_LICENSE_KEY.",
"recheck_license_success": "License check successful", "recheck_license_success": "License check successful",
"recheck_license_unreachable": "License server is unreachable. Please try again later.", "recheck_license_unreachable": "License server is unreachable. Please try again later.",
"rechecking": "Rechecking", "rechecking": "Rechecking...",
"request_30_day_trial_license": "Request 30-day Trial License", "request_30_day_trial_license": "Request 30-day Trial License",
"saml_sso": "SAML SSO", "saml_sso": "SAML SSO",
"service_level_agreement": "Service Level Agreement", "service_level_agreement": "Service Level Agreement",
@@ -1087,7 +1067,7 @@
"email_customization_preview_email_heading": "Hey {userName}", "email_customization_preview_email_heading": "Hey {userName}",
"email_customization_preview_email_text": "This is an email preview to show you which logo will be rendered in the emails.", "email_customization_preview_email_text": "This is an email preview to show you which logo will be rendered in the emails.",
"error_deleting_organization_please_try_again": "Error deleting organization. Please try again.", "error_deleting_organization_please_try_again": "Error deleting organization. Please try again.",
"from_your_organization": "{memberName} from your organization", "from_your_organization": "from your organization",
"invitation_sent_once_more": "Invitation sent once more.", "invitation_sent_once_more": "Invitation sent once more.",
"invite_deleted_successfully": "Invite deleted successfully", "invite_deleted_successfully": "Invite deleted successfully",
"invite_expires_on": "Invite expires on {date}", "invite_expires_on": "Invite expires on {date}",
@@ -1136,7 +1116,7 @@
"notification_settings_updated": "Notification settings updated", "notification_settings_updated": "Notification settings updated",
"set_up_an_alert_to_get_an_email_on_new_responses": "Set up an alert to get an email on new responses", "set_up_an_alert_to_get_an_email_on_new_responses": "Set up an alert to get an email on new responses",
"use_the_integration": "Use the integration", "use_the_integration": "Use the integration",
"want_to_loop_in_organization_mates": "Want to loop in organization mates?", "want_to_loop_in_organization_mates": "Want to loop in organization mates",
"you_will_not_be_auto_subscribed_to_this_organizations_surveys_anymore": "You will not be auto-subscribed to this organizations surveys anymore!", "you_will_not_be_auto_subscribed_to_this_organizations_surveys_anymore": "You will not be auto-subscribed to this organizations surveys anymore!",
"you_will_not_receive_any_more_emails_for_responses_on_this_survey": "You will not receive any more emails for responses on this survey!" "you_will_not_receive_any_more_emails_for_responses_on_this_survey": "You will not receive any more emails for responses on this survey!"
}, },
@@ -1252,7 +1232,6 @@
"add_fallback_placeholder": "Add a placeholder to show if there is no value to recall.", "add_fallback_placeholder": "Add a placeholder to show if there is no value to recall.",
"add_hidden_field_id": "Add hidden field ID", "add_hidden_field_id": "Add hidden field ID",
"add_highlight_border": "Add highlight border", "add_highlight_border": "Add highlight border",
"add_highlight_border_description": "Only applies to in-product surveys.",
"add_logic": "Add logic", "add_logic": "Add logic",
"add_none_of_the_above": "Add “None of the Above”", "add_none_of_the_above": "Add “None of the Above”",
"add_option": "Add option", "add_option": "Add option",
@@ -1269,14 +1248,12 @@
"adjust_survey_closed_message": "Adjust “Survey Closed” message", "adjust_survey_closed_message": "Adjust “Survey Closed” message",
"adjust_survey_closed_message_description": "Change the message visitors see when the survey is closed.", "adjust_survey_closed_message_description": "Change the message visitors see when the survey is closed.",
"adjust_the_theme_in_the": "Adjust the theme in the", "adjust_the_theme_in_the": "Adjust the theme in the",
"all_are_true": "all are true",
"all_other_answers_will_continue_to": "All other answers will continue to", "all_other_answers_will_continue_to": "All other answers will continue to",
"allow_multi_select": "Allow multi-select", "allow_multi_select": "Allow multi-select",
"allow_multiple_files": "Allow multiple files", "allow_multiple_files": "Allow multiple files",
"allow_users_to_select_more_than_one_image": "Allow users to select more than one image", "allow_users_to_select_more_than_one_image": "Allow users to select more than one image",
"and_launch_surveys_in_your_website_or_app": "and launch surveys in your website or app.", "and_launch_surveys_in_your_website_or_app": "and launch surveys in your website or app.",
"animation": "Animation", "animation": "Animation",
"any_is_true": "any is true",
"app_survey_description": "Embed a survey in your web app or website to collect responses.", "app_survey_description": "Embed a survey in your web app or website to collect responses.",
"assign": "Assign =", "assign": "Assign =",
"audience": "Audience", "audience": "Audience",
@@ -1307,7 +1284,7 @@
"calculate": "Calculate", "calculate": "Calculate",
"capture_a_new_action_to_trigger_a_survey_on": "Capture a new action to trigger a survey on.", "capture_a_new_action_to_trigger_a_survey_on": "Capture a new action to trigger a survey on.",
"capture_ip_address": "Capture IP address", "capture_ip_address": "Capture IP address",
"capture_ip_address_description": "Store the respondents IP address in response metadata for duplicate detection and security purposes", "capture_ip_address_description": "Store the respondent's IP address in response metadata for duplicate detection and security purposes",
"capture_new_action": "Capture new action", "capture_new_action": "Capture new action",
"card_arrangement_for_survey_type_derived": "Card Arrangement for {surveyTypeDerived} Surveys", "card_arrangement_for_survey_type_derived": "Card Arrangement for {surveyTypeDerived} Surveys",
"card_background_color": "Card background color", "card_background_color": "Card background color",
@@ -1318,7 +1295,7 @@
"casual": "Casual", "casual": "Casual",
"caution_edit_duplicate": "Duplicate & edit", "caution_edit_duplicate": "Duplicate & edit",
"caution_edit_published_survey": "Edit a published survey?", "caution_edit_published_survey": "Edit a published survey?",
"caution_explanation_intro": "We understand you might still want to make changes. Here is what happens if you do:", "caution_explanation_intro": "We understand you might still want to make changes. Here is what happens if you do: ",
"caution_explanation_new_responses_separated": "Responses before the change may not or only partially be included in the survey summary.", "caution_explanation_new_responses_separated": "Responses before the change may not or only partially be included in the survey summary.",
"caution_explanation_only_new_responses_in_summary": "All data, including past responses, remain available as download on the survey summary page.", "caution_explanation_only_new_responses_in_summary": "All data, including past responses, remain available as download on the survey summary page.",
"caution_explanation_responses_are_safe": "Older and newer responses get mixed which can lead to misleading data summaries.", "caution_explanation_responses_are_safe": "Older and newer responses get mixed which can lead to misleading data summaries.",
@@ -1453,6 +1430,7 @@
"follow_ups_modal_updated_successfull_toast": "Follow-up updated and will be saved once you save the survey.", "follow_ups_modal_updated_successfull_toast": "Follow-up updated and will be saved once you save the survey.",
"follow_ups_new": "New follow-up", "follow_ups_new": "New follow-up",
"follow_ups_upgrade_button_text": "Upgrade to enable follow-ups", "follow_ups_upgrade_button_text": "Upgrade to enable follow-ups",
"form_styling": "Form styling",
"formbricks_sdk_is_not_connected": "Formbricks SDK is not connected", "formbricks_sdk_is_not_connected": "Formbricks SDK is not connected",
"four_points": "4 points", "four_points": "4 points",
"heading": "Heading", "heading": "Heading",
@@ -1542,7 +1520,7 @@
"option_idx": "Option {choiceIndex}", "option_idx": "Option {choiceIndex}",
"option_used_in_logic_error": "This option is used in logic of question {questionIndex}. Please remove it from logic first.", "option_used_in_logic_error": "This option is used in logic of question {questionIndex}. Please remove it from logic first.",
"optional": "Optional", "optional": "Optional",
"options": "Options*", "options": "Options",
"options_used_in_logic_bulk_error": "The following options are used in logic: {questionIndexes}. Please remove them from logic first.", "options_used_in_logic_bulk_error": "The following options are used in logic: {questionIndexes}. Please remove them from logic first.",
"override_theme_with_individual_styles_for_this_survey": "Override the theme with individual styles for this survey.", "override_theme_with_individual_styles_for_this_survey": "Override the theme with individual styles for this survey.",
"overwrite_global_waiting_time": "Set custom Cooldown Period", "overwrite_global_waiting_time": "Set custom Cooldown Period",
@@ -1567,7 +1545,6 @@
"question_deleted": "Question deleted.", "question_deleted": "Question deleted.",
"question_duplicated": "Question duplicated.", "question_duplicated": "Question duplicated.",
"question_id_updated": "Question ID updated", "question_id_updated": "Question ID updated",
"question_number": "Question {number}",
"question_used_in_logic_warning_text": "Elements from this block are used in a logic rule, are you sure you want to delete it?", "question_used_in_logic_warning_text": "Elements from this block are used in a logic rule, are you sure you want to delete it?",
"question_used_in_logic_warning_title": "Logic Inconsistency", "question_used_in_logic_warning_title": "Logic Inconsistency",
"question_used_in_quota": "This question is being used in “{quotaName}” quota", "question_used_in_quota": "This question is being used in “{quotaName}” quota",
@@ -1626,7 +1603,7 @@
"response_limits_redirections_and_more": "Response limits, redirections and more.", "response_limits_redirections_and_more": "Response limits, redirections and more.",
"response_options": "Response Options", "response_options": "Response Options",
"roundness": "Roundness", "roundness": "Roundness",
"roundness_description": "Controls how rounded corners are.", "roundness_description": "Controls how rounded the card corners are.",
"row_used_in_logic_error": "This row is used in logic of question {questionIndex}. Please remove it from logic first.", "row_used_in_logic_error": "This row is used in logic of question {questionIndex}. Please remove it from logic first.",
"rows": "Rows", "rows": "Rows",
"save_and_close": "Save & Close", "save_and_close": "Save & Close",
@@ -1672,7 +1649,6 @@
"survey_completed_subheading": "This free & open-source survey has been closed", "survey_completed_subheading": "This free & open-source survey has been closed",
"survey_display_settings": "Survey Display Settings", "survey_display_settings": "Survey Display Settings",
"survey_placement": "Survey Placement", "survey_placement": "Survey Placement",
"survey_styling": "Survey styling",
"survey_trigger": "Survey Trigger", "survey_trigger": "Survey Trigger",
"switch_multi_language_on_to_get_started": "Switch multi-language on to get started 👉", "switch_multi_language_on_to_get_started": "Switch multi-language on to get started 👉",
"target_block_not_found": "Target block not found", "target_block_not_found": "Target block not found",
@@ -1735,7 +1711,7 @@
"pattern": "Matches regex pattern", "pattern": "Matches regex pattern",
"phone": "Is valid phone", "phone": "Is valid phone",
"rank_all_options": "Rank all options", "rank_all_options": "Rank all options",
"select_file_extensions": "Select file extensions", "select_file_extensions": "Select file extensions...",
"select_option": "Select option", "select_option": "Select option",
"start_date": "Start date", "start_date": "Start date",
"url": "Is valid URL" "url": "Is valid URL"
@@ -1761,9 +1737,9 @@
"waiting_time_across_surveys": "Cooldown Period (across surveys)", "waiting_time_across_surveys": "Cooldown Period (across surveys)",
"waiting_time_across_surveys_description": "To prevent survey fatigue, choose how this survey interacts with the workspace-wide Cooldown Period.", "waiting_time_across_surveys_description": "To prevent survey fatigue, choose how this survey interacts with the workspace-wide Cooldown Period.",
"welcome_message": "Welcome message", "welcome_message": "Welcome message",
"when": "When",
"without_a_filter_all_of_your_users_can_be_surveyed": "Without a filter, all of your users can be surveyed.", "without_a_filter_all_of_your_users_can_be_surveyed": "Without a filter, all of your users can be surveyed.",
"you_have_not_created_a_segment_yet": "You have not created a segment yet", "you_have_not_created_a_segment_yet": "You have not created a segment yet",
"you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "You need to have two or more languages set up in your workspace to work with translations.",
"your_description_here_recall_information_with": "Your description here. Recall information with @", "your_description_here_recall_information_with": "Your description here. Recall information with @",
"your_question_here_recall_information_with": "Your question here. Recall information with @", "your_question_here_recall_information_with": "Your question here. Recall information with @",
"your_web_app": "Your web app", "your_web_app": "Your web app",
@@ -1833,7 +1809,7 @@
"disable_single_use_link_modal_button": "Disable single-use links", "disable_single_use_link_modal_button": "Disable single-use links",
"disable_single_use_link_modal_description": "If you shared single-use links, participants will not be able to respond to the survey any longer.", "disable_single_use_link_modal_description": "If you shared single-use links, participants will not be able to respond to the survey any longer.",
"generate_and_download_links": "Generate & download links", "generate_and_download_links": "Generate & download links",
"generate_links_error": "Single use links could not get generated. Please work directly with the API.", "generate_links_error": "Single use links could not get generated. Please work directly with the API",
"multi_use_link": "Multi-use link", "multi_use_link": "Multi-use link",
"multi_use_link_description": "Collect multiple responses from anonymous respondents with one link.", "multi_use_link_description": "Collect multiple responses from anonymous respondents with one link.",
"multi_use_powers_other_channels_description": "If you disable it, these other distribution channels will also get disabled.", "multi_use_powers_other_channels_description": "If you disable it, these other distribution channels will also get disabled.",
@@ -1944,7 +1920,7 @@
} }
}, },
"summary": { "summary": {
"added_filter_for_responses_where_answer_to_question": "Added filter for responses where answer to question {questionIdx} is {filterComboBoxValue} - {filterValue}", "added_filter_for_responses_where_answer_to_question": "Added filter for responses where answer to question {questionIdx} is {filterComboBoxValue} - {filterValue} ",
"added_filter_for_responses_where_answer_to_question_is_skipped": "Added filter for responses where answer to question {questionIdx} is skipped", "added_filter_for_responses_where_answer_to_question_is_skipped": "Added filter for responses where answer to question {questionIdx} is skipped",
"aggregated": "Aggregated", "aggregated": "Aggregated",
"all_responses_csv": "All responses (CSV)", "all_responses_csv": "All responses (CSV)",
@@ -1971,7 +1947,6 @@
"filtered_responses_excel": "Filtered responses (Excel)", "filtered_responses_excel": "Filtered responses (Excel)",
"generating_qr_code": "Generating QR code", "generating_qr_code": "Generating QR code",
"impressions": "Impressions", "impressions": "Impressions",
"impressions_identified_only": "Only showing impressions from identified contacts",
"impressions_tooltip": "Number of times the survey has been viewed.", "impressions_tooltip": "Number of times the survey has been viewed.",
"in_app": { "in_app": {
"connection_description": "The survey will be shown to users of your website, that match the criteria listed below", "connection_description": "The survey will be shown to users of your website, that match the criteria listed below",
@@ -2014,7 +1989,6 @@
"last_quarter": "Last quarter", "last_quarter": "Last quarter",
"last_year": "Last year", "last_year": "Last year",
"limit": "Limit", "limit": "Limit",
"no_identified_impressions": "No impressions from identified contacts",
"no_responses_found": "No responses found", "no_responses_found": "No responses found",
"other_values_found": "Other values found", "other_values_found": "Other values found",
"overall": "Overall", "overall": "Overall",
@@ -2037,7 +2011,6 @@
"starts": "Starts", "starts": "Starts",
"starts_tooltip": "Number of times the survey has been started.", "starts_tooltip": "Number of times the survey has been started.",
"survey_reset_successfully": "Survey reset successfully. {responseCount} responses and {displayCount} displays were deleted.", "survey_reset_successfully": "Survey reset successfully. {responseCount} responses and {displayCount} displays were deleted.",
"survey_results": "{surveyName} Results",
"this_month": "This month", "this_month": "This month",
"this_quarter": "This quarter", "this_quarter": "This quarter",
"this_year": "This year", "this_year": "This year",
@@ -2157,7 +2130,7 @@
"add_background_color_description": "Add a background color to the logo container.", "add_background_color_description": "Add a background color to the logo container.",
"advanced_styling_field_border_radius": "Border Radius", "advanced_styling_field_border_radius": "Border Radius",
"advanced_styling_field_button_bg": "Button Background", "advanced_styling_field_button_bg": "Button Background",
"advanced_styling_field_button_bg_description": "Fills the Next / Submit button.", "advanced_styling_field_button_bg_description": "Fills the Next / Submit button.",
"advanced_styling_field_button_border_radius_description": "Rounds the button corners.", "advanced_styling_field_button_border_radius_description": "Rounds the button corners.",
"advanced_styling_field_button_font_size_description": "Scales the button label text.", "advanced_styling_field_button_font_size_description": "Scales the button label text.",
"advanced_styling_field_button_font_weight_description": "Makes button text lighter or bolder.", "advanced_styling_field_button_font_weight_description": "Makes button text lighter or bolder.",
@@ -2185,7 +2158,7 @@
"advanced_styling_field_indicator_bg_description": "Colors the filled portion of the bar.", "advanced_styling_field_indicator_bg_description": "Colors the filled portion of the bar.",
"advanced_styling_field_input_border_radius_description": "Rounds the input corners.", "advanced_styling_field_input_border_radius_description": "Rounds the input corners.",
"advanced_styling_field_input_font_size_description": "Scales the typed text in inputs.", "advanced_styling_field_input_font_size_description": "Scales the typed text in inputs.",
"advanced_styling_field_input_height_description": "Controls the minimum height of the input.", "advanced_styling_field_input_height_description": "Controls the minimum height of the input field.",
"advanced_styling_field_input_padding_x_description": "Adds space on the left and right.", "advanced_styling_field_input_padding_x_description": "Adds space on the left and right.",
"advanced_styling_field_input_padding_y_description": "Adds space on the top and bottom.", "advanced_styling_field_input_padding_y_description": "Adds space on the top and bottom.",
"advanced_styling_field_input_placeholder_opacity_description": "Fades the placeholder hint text.", "advanced_styling_field_input_placeholder_opacity_description": "Fades the placeholder hint text.",
@@ -2194,8 +2167,6 @@
"advanced_styling_field_input_text_description": "Colors the typed text in inputs.", "advanced_styling_field_input_text_description": "Colors the typed text in inputs.",
"advanced_styling_field_option_bg": "Background", "advanced_styling_field_option_bg": "Background",
"advanced_styling_field_option_bg_description": "Fills the option items.", "advanced_styling_field_option_bg_description": "Fills the option items.",
"advanced_styling_field_option_border": "Border Color",
"advanced_styling_field_option_border_description": "Outlines radio and checkbox options.",
"advanced_styling_field_option_border_radius_description": "Rounds the option corners.", "advanced_styling_field_option_border_radius_description": "Rounds the option corners.",
"advanced_styling_field_option_font_size_description": "Scales the option label text.", "advanced_styling_field_option_font_size_description": "Scales the option label text.",
"advanced_styling_field_option_label": "Label Color", "advanced_styling_field_option_label": "Label Color",
@@ -2250,7 +2221,7 @@
"show_powered_by_formbricks": "Show “Powered by Formbricks” Signature", "show_powered_by_formbricks": "Show “Powered by Formbricks” Signature",
"styling_updated_successfully": "Styling updated successfully", "styling_updated_successfully": "Styling updated successfully",
"suggest_colors": "Suggest colors", "suggest_colors": "Suggest colors",
"suggested_colors_applied_please_save": "Suggested colors generated successfully. Press Save to persist the changes.", "suggested_colors_applied_please_save": "Suggested colors generated successfully. Press \"Save\" to persist the changes.",
"theme": "Theme", "theme": "Theme",
"theme_settings_description": "Create a style theme for all surveys. You can enable custom styling for each survey." "theme_settings_description": "Create a style theme for all surveys. You can enable custom styling for each survey."
}, },
@@ -2334,7 +2305,7 @@
"check_inbox_or_spam": "Please also check your spam folder if you do not see the email in your inbox.", "check_inbox_or_spam": "Please also check your spam folder if you do not see the email in your inbox.",
"completed": "This survey is closed.", "completed": "This survey is closed.",
"create_your_own": "Create your own open-source survey", "create_your_own": "Create your own open-source survey",
"enter_pin": "This survey is protected. Enter the PIN below.", "enter_pin": "This survey is protected. Enter the PIN below",
"just_curious": "Just curious?", "just_curious": "Just curious?",
"link_invalid": "This survey can only be taken by invitation.", "link_invalid": "This survey can only be taken by invitation.",
"paused": "This survey is temporarily paused.", "paused": "This survey is temporarily paused.",
@@ -2405,16 +2376,6 @@
"alignment_and_engagement_survey_question_4_headline": "How can the company improve its vision and strategy alignment?", "alignment_and_engagement_survey_question_4_headline": "How can the company improve its vision and strategy alignment?",
"alignment_and_engagement_survey_question_4_placeholder": "Type your answer here…", "alignment_and_engagement_survey_question_4_placeholder": "Type your answer here…",
"back": "Back", "back": "Back",
"block_1": "Block 1",
"block_10": "Block 10",
"block_2": "Block 2",
"block_3": "Block 3",
"block_4": "Block 4",
"block_5": "Block 5",
"block_6": "Block 6",
"block_7": "Block 7",
"block_8": "Block 8",
"block_9": "Block 9",
"book_interview": "Book interview", "book_interview": "Book interview",
"build_product_roadmap_description": "Identify the ONE thing your users want the most and build it.", "build_product_roadmap_description": "Identify the ONE thing your users want the most and build it.",
"build_product_roadmap_name": "Build Product Roadmap", "build_product_roadmap_name": "Build Product Roadmap",
@@ -2570,7 +2531,7 @@
"csat_question_3_choice_8": "Poor quality", "csat_question_3_choice_8": "Poor quality",
"csat_question_3_choice_9": "Unreliable", "csat_question_3_choice_9": "Unreliable",
"csat_question_3_headline": "Which of the following words would you use to describe our $[projectName]?", "csat_question_3_headline": "Which of the following words would you use to describe our $[projectName]?",
"csat_question_3_subheader": "Please select all that apply:", "csat_question_3_subheader": "Select all that apply:",
"csat_question_4_choice_1": "Extremely well", "csat_question_4_choice_1": "Extremely well",
"csat_question_4_choice_2": "Very well", "csat_question_4_choice_2": "Very well",
"csat_question_4_choice_3": "Somewhat well", "csat_question_4_choice_3": "Somewhat well",
@@ -2622,6 +2583,7 @@
"csat_survey_question_3_headline": "Ugh, sorry! Is there anything we can do to improve your experience?", "csat_survey_question_3_headline": "Ugh, sorry! Is there anything we can do to improve your experience?",
"csat_survey_question_3_placeholder": "Type your answer here…", "csat_survey_question_3_placeholder": "Type your answer here…",
"cta_description": "Display information and prompt users to take a specific action", "cta_description": "Display information and prompt users to take a specific action",
"custom_survey_block_1_name": "Block 1",
"custom_survey_description": "Create a survey without template.", "custom_survey_description": "Create a survey without template.",
"custom_survey_name": "Start from scratch", "custom_survey_name": "Start from scratch",
"custom_survey_question_1_headline": "What would you like to know?", "custom_survey_question_1_headline": "What would you like to know?",
@@ -3024,9 +2986,6 @@
"preview_survey_question_2_choice_2_label": "No, thank you!", "preview_survey_question_2_choice_2_label": "No, thank you!",
"preview_survey_question_2_headline": "Want to stay in the loop?", "preview_survey_question_2_headline": "Want to stay in the loop?",
"preview_survey_question_2_subheader": "This is an example description.", "preview_survey_question_2_subheader": "This is an example description.",
"preview_survey_question_open_text_headline": "Anything else you'd like to share?",
"preview_survey_question_open_text_placeholder": "Type your answer here…",
"preview_survey_question_open_text_subheader": "Your feedback helps us improve.",
"preview_survey_welcome_card_headline": "Welcome!", "preview_survey_welcome_card_headline": "Welcome!",
"prioritize_features_description": "Identify features your users need most and least.", "prioritize_features_description": "Identify features your users need most and least.",
"prioritize_features_name": "Prioritize Features", "prioritize_features_name": "Prioritize Features",
@@ -3096,7 +3055,7 @@
"professional_development_survey_question_2_choice_5": "Individual research", "professional_development_survey_question_2_choice_5": "Individual research",
"professional_development_survey_question_2_choice_6": "Other", "professional_development_survey_question_2_choice_6": "Other",
"professional_development_survey_question_2_headline": "What types of professional development activities do you think would be most valuable for your growth?", "professional_development_survey_question_2_headline": "What types of professional development activities do you think would be most valuable for your growth?",
"professional_development_survey_question_2_subheader": "Please select all that apply:", "professional_development_survey_question_2_subheader": "Select all that apply",
"professional_development_survey_question_3_choice_1": "Yes", "professional_development_survey_question_3_choice_1": "Yes",
"professional_development_survey_question_3_choice_2": "No", "professional_development_survey_question_3_choice_2": "No",
"professional_development_survey_question_3_headline": "Have you dedicated time to your professional development in the past?", "professional_development_survey_question_3_headline": "Have you dedicated time to your professional development in the past?",
@@ -3275,18 +3234,5 @@
"usability_question_9_headline": "I felt confident while using the system.", "usability_question_9_headline": "I felt confident while using the system.",
"usability_rating_description": "Measure perceived usability by asking users to rate their experience with your product using a standardized 10-question survey.", "usability_rating_description": "Measure perceived usability by asking users to rate their experience with your product using a standardized 10-question survey.",
"usability_score_name": "System Usability Score (SUS)" "usability_score_name": "System Usability Score (SUS)"
},
"workflows": {
"coming_soon_description": "Thank you for sharing your workflow idea with us! We are currently designing this feature and your feedback will help us build exactly what you need.",
"coming_soon_title": "We are almost there!",
"follow_up_label": "Is there anything else you'd like to add?",
"follow_up_placeholder": "What specific tasks would you like to automate? Any tools or integrations you would want included?",
"generate_button": "Generate workflow",
"heading": "What workflow do you want to create?",
"placeholder": "Describe the workflow you want to generate…",
"subheading": "Generate your workflow in seconds.",
"submit_button": "Add details",
"thank_you_description": "Your input helps us build the Workflows feature you actually need. We will keep you posted on our progress.",
"thank_you_title": "Thank you for your feedback!"
} }
} }
+33 -87
View File
@@ -175,12 +175,9 @@
"copy": "Copiar", "copy": "Copiar",
"copy_code": "Copiar código", "copy_code": "Copiar código",
"copy_link": "Copiar enlace", "copy_link": "Copiar enlace",
"count_attributes": "{count, plural, one {{count} atributo} other {{count} atributos}}", "count_attributes": "{value, plural, one {{value} atributo} other {{value} atributos}}",
"count_contacts": "{count, plural, one {{count} contacto} other {{count} contactos}}", "count_contacts": "{value, plural, one {{value} contacto} other {{value} contactos}}",
"count_members": "{count, plural, one {{count} miembro} other {{count} miembros}}", "count_responses": "{value, plural, one {{value} respuesta} other {{value} respuestas}}",
"count_questions": "{count, plural, one {{count} pregunta} other {{count} preguntas}}",
"count_responses": "{count, plural, one {{count} respuesta} other {{count} respuestas}}",
"count_selections": "{count, plural, one {{count} selección} other {{count} selecciones}}",
"create_new_organization": "Crear organización nueva", "create_new_organization": "Crear organización nueva",
"create_segment": "Crear segmento", "create_segment": "Crear segmento",
"create_survey": "Crear encuesta", "create_survey": "Crear encuesta",
@@ -194,7 +191,6 @@
"days": "días", "days": "días",
"default": "Predeterminado", "default": "Predeterminado",
"delete": "Eliminar", "delete": "Eliminar",
"delete_what": "Eliminar {deleteWhat}",
"description": "Descripción", "description": "Descripción",
"dev_env": "Entorno de desarrollo", "dev_env": "Entorno de desarrollo",
"development": "Desarrollo", "development": "Desarrollo",
@@ -210,8 +206,6 @@
"download": "Descargar", "download": "Descargar",
"draft": "Borrador", "draft": "Borrador",
"duplicate": "Duplicar", "duplicate": "Duplicar",
"duplicate_copy": "(copia)",
"duplicate_copy_number": "(copia {copyNumber})",
"e_commerce": "Comercio electrónico", "e_commerce": "Comercio electrónico",
"edit": "Editar", "edit": "Editar",
"email": "Email", "email": "Email",
@@ -224,16 +218,13 @@
"error": "Error", "error": "Error",
"error_component_description": "Este recurso no existe o no tienes los derechos necesarios para acceder a él.", "error_component_description": "Este recurso no existe o no tienes los derechos necesarios para acceder a él.",
"error_component_title": "Error al cargar recursos", "error_component_title": "Error al cargar recursos",
"error_loading_data": "Error al cargar los datos",
"error_rate_limit_description": "Número máximo de solicitudes alcanzado. Por favor, inténtalo de nuevo más tarde.", "error_rate_limit_description": "Número máximo de solicitudes alcanzado. Por favor, inténtalo de nuevo más tarde.",
"error_rate_limit_title": "Límite de frecuencia excedido", "error_rate_limit_title": "Límite de frecuencia excedido",
"expand_rows": "Expandir filas", "expand_rows": "Expandir filas",
"failed_to_copy_to_clipboard": "Error al copiar al portapapeles", "failed_to_copy_to_clipboard": "Error al copiar al portapapeles",
"failed_to_load_organizations": "Error al cargar organizaciones", "failed_to_load_organizations": "Error al cargar organizaciones",
"failed_to_load_workspaces": "Error al cargar los proyectos", "failed_to_load_workspaces": "Error al cargar los proyectos",
"filter": "Filtro",
"finish": "Finalizar", "finish": "Finalizar",
"first_name": "Nombre",
"follow_these": "Sigue estos", "follow_these": "Sigue estos",
"formbricks_version": "Versión de Formbricks", "formbricks_version": "Versión de Formbricks",
"full_name": "Nombre completo", "full_name": "Nombre completo",
@@ -246,7 +237,6 @@
"hidden_field": "Campo oculto", "hidden_field": "Campo oculto",
"hidden_fields": "Campos ocultos", "hidden_fields": "Campos ocultos",
"hide_column": "Ocultar columna", "hide_column": "Ocultar columna",
"id": "ID",
"image": "Imagen", "image": "Imagen",
"images": "Imágenes", "images": "Imágenes",
"import": "Importar", "import": "Importar",
@@ -264,7 +254,6 @@
"key": "Clave", "key": "Clave",
"label": "Etiqueta", "label": "Etiqueta",
"language": "Idioma", "language": "Idioma",
"last_name": "Apellido",
"learn_more": "Saber más", "learn_more": "Saber más",
"license_expired": "License Expired", "license_expired": "License Expired",
"light_overlay": "Superposición clara", "light_overlay": "Superposición clara",
@@ -279,6 +268,7 @@
"look_and_feel": "Apariencia", "look_and_feel": "Apariencia",
"manage": "Gestionar", "manage": "Gestionar",
"marketing": "Marketing", "marketing": "Marketing",
"member": "Miembro",
"members": "Miembros", "members": "Miembros",
"members_and_teams": "Miembros y equipos", "members_and_teams": "Miembros y equipos",
"membership_not_found": "Membresía no encontrada", "membership_not_found": "Membresía no encontrada",
@@ -290,7 +280,6 @@
"move_down": "Mover hacia abajo", "move_down": "Mover hacia abajo",
"move_up": "Mover hacia arriba", "move_up": "Mover hacia arriba",
"multiple_languages": "Múltiples idiomas", "multiple_languages": "Múltiples idiomas",
"my_product": "mi producto",
"name": "Nombre", "name": "Nombre",
"new": "Nuevo", "new": "Nuevo",
"new_version_available": "Formbricks {version} está aquí. ¡Actualiza ahora!", "new_version_available": "Formbricks {version} está aquí. ¡Actualiza ahora!",
@@ -386,6 +375,8 @@
"select_teams": "Seleccionar equipos", "select_teams": "Seleccionar equipos",
"selected": "Seleccionado", "selected": "Seleccionado",
"selected_questions": "Preguntas seleccionadas", "selected_questions": "Preguntas seleccionadas",
"selection": "Selección",
"selections": "Selecciones",
"send_test_email": "Enviar correo electrónico de prueba", "send_test_email": "Enviar correo electrónico de prueba",
"session_not_found": "Sesión no encontrada", "session_not_found": "Sesión no encontrada",
"settings": "Ajustes", "settings": "Ajustes",
@@ -394,7 +385,6 @@
"show_response_count": "Mostrar recuento de respuestas", "show_response_count": "Mostrar recuento de respuestas",
"shown": "Mostrado", "shown": "Mostrado",
"size": "Tamaño", "size": "Tamaño",
"skip": "Omitir",
"skipped": "Omitido", "skipped": "Omitido",
"skips": "Omisiones", "skips": "Omisiones",
"some_files_failed_to_upload": "Algunos archivos no se han podido subir", "some_files_failed_to_upload": "Algunos archivos no se han podido subir",
@@ -438,7 +428,6 @@
"top_right": "Superior derecha", "top_right": "Superior derecha",
"try_again": "Intentar de nuevo", "try_again": "Intentar de nuevo",
"type": "Tipo", "type": "Tipo",
"unknown_survey": "Encuesta desconocida",
"unlock_more_workspaces_with_a_higher_plan": "Desbloquea más proyectos con un plan superior.", "unlock_more_workspaces_with_a_higher_plan": "Desbloquea más proyectos con un plan superior.",
"update": "Actualizar", "update": "Actualizar",
"updated": "Actualizado", "updated": "Actualizado",
@@ -464,7 +453,6 @@
"website_survey": "Encuesta de sitio web", "website_survey": "Encuesta de sitio web",
"weeks": "semanas", "weeks": "semanas",
"welcome_card": "Tarjeta de bienvenida", "welcome_card": "Tarjeta de bienvenida",
"workflows": "Flujos de trabajo",
"workspace_configuration": "Configuración del proyecto", "workspace_configuration": "Configuración del proyecto",
"workspace_created_successfully": "Proyecto creado correctamente", "workspace_created_successfully": "Proyecto creado correctamente",
"workspace_creation_description": "Organiza las encuestas en proyectos para un mejor control de acceso.", "workspace_creation_description": "Organiza las encuestas en proyectos para un mejor control de acceso.",
@@ -647,27 +635,27 @@
"attribute_value": "Valor", "attribute_value": "Valor",
"attribute_value_placeholder": "Valor del atributo", "attribute_value_placeholder": "Valor del atributo",
"attributes_msg_attribute_limit_exceeded": "No se pudieron crear {count} atributo(s) nuevo(s) ya que se excedería el límite máximo de {limit} clases de atributos. Los atributos existentes se actualizaron correctamente.", "attributes_msg_attribute_limit_exceeded": "No se pudieron crear {count} atributo(s) nuevo(s) ya que se excedería el límite máximo de {limit} clases de atributos. Los atributos existentes se actualizaron correctamente.",
"attributes_msg_attribute_type_validation_error": "{error} (el atributo {key} tiene dataType: {dataType})", "attributes_msg_attribute_type_validation_error": "{error} (el atributo '{key}' tiene dataType: {dataType})",
"attributes_msg_email_already_exists": "El email ya existe para este entorno y no se actualizó.", "attributes_msg_email_already_exists": "El email ya existe para este entorno y no se actualizó.",
"attributes_msg_email_or_userid_required": "Se requiere el correo electrónico o el ID de usuario. Se conservaron los valores existentes.", "attributes_msg_email_or_userid_required": "Se requiere email o userId. Se conservaron los valores existentes.",
"attributes_msg_new_attribute_created": "Se creó el atributo nuevo {key} con el tipo {dataType}", "attributes_msg_new_attribute_created": "Se creó el atributo nuevo '{key}' con tipo '{dataType}'",
"attributes_msg_userid_already_exists": "El ID de usuario ya existe para este entorno y no se actualizó.", "attributes_msg_userid_already_exists": "El userId ya existe para este entorno y no se actualizó.",
"contact_deleted_successfully": "Contacto eliminado correctamente", "contact_deleted_successfully": "Contacto eliminado correctamente",
"contact_not_found": "No se ha encontrado dicho contacto", "contact_not_found": "No se ha encontrado dicho contacto",
"contacts_table_refresh": "Actualizar contactos", "contacts_table_refresh": "Actualizar contactos",
"contacts_table_refresh_success": "Contactos actualizados correctamente", "contacts_table_refresh_success": "Contactos actualizados correctamente",
"create_attribute": "Crear atributo", "create_attribute": "Crear atributo",
"create_key": "Crear clave",
"create_new_attribute": "Crear atributo nuevo", "create_new_attribute": "Crear atributo nuevo",
"create_new_attribute_description": "Crea un atributo nuevo para fines de segmentación.", "create_new_attribute_description": "Crea un atributo nuevo para fines de segmentación.",
"custom_attributes": "Atributos personalizados", "custom_attributes": "Atributos personalizados",
"data_type": "Tipo de dato", "data_type": "Tipo de dato",
"data_type_cannot_be_changed": "El tipo de dato no se puede cambiar después de la creación", "data_type_cannot_be_changed": "El tipo de dato no se puede cambiar después de la creación",
"data_type_description": "Elige cómo debe almacenarse y filtrarse este atributo", "data_type_description": "Elige cómo debe almacenarse y filtrarse este atributo",
"date_value_required": "Se requiere el valor de fecha. Usa el botón de eliminar para quitar este atributo si no deseas establecer una fecha.", "date_value_required": "Se requiere un valor de fecha. Usa el botón de eliminar para quitar este atributo si no quieres establecer una fecha.",
"delete_attribute_confirmation": "{value, plural, one {Esto eliminará el atributo seleccionado. Se perderán todos los datos de contacto asociados con este atributo.} other {Esto eliminará los atributos seleccionados. Se perderán todos los datos de contacto asociados con estos atributos.}}", "delete_attribute_confirmation": "{value, plural, one {Esto eliminará el atributo seleccionado. Se perderán todos los datos de contacto asociados con este atributo.} other {Esto eliminará los atributos seleccionados. Se perderán todos los datos de contacto asociados con estos atributos.}}",
"delete_contact_confirmation": "Esto eliminará todas las respuestas de encuestas y atributos de contacto asociados con este contacto. Cualquier segmentación y personalización basada en los datos de este contacto se perderá.", "delete_contact_confirmation": "Esto eliminará todas las respuestas de encuestas y atributos de contacto asociados con este contacto. Cualquier segmentación y personalización basada en los datos de este contacto se perderá.",
"delete_contact_confirmation_with_quotas": "{value, plural, one {Esto eliminará todas las respuestas de encuestas y atributos de contacto asociados con este contacto. Cualquier segmentación y personalización basada en los datos de este contacto se perderá. Si este contacto tiene respuestas que cuentan para las cuotas de encuesta, los recuentos de cuota se reducirán pero los límites de cuota permanecerán sin cambios.} other {Esto eliminará todas las respuestas de encuestas y atributos de contacto asociados con estos contactos. Cualquier segmentación y personalización basada en los datos de estos contactos se perderá. Si estos contactos tienen respuestas que cuentan para las cuotas de encuesta, los recuentos de cuota se reducirán pero los límites de cuota permanecerán sin cambios.}}", "delete_contact_confirmation_with_quotas": "{value, plural, one {Esto eliminará todas las respuestas de encuestas y atributos de contacto asociados con este contacto. Cualquier segmentación y personalización basada en los datos de este contacto se perderá. Si este contacto tiene respuestas que cuentan para las cuotas de encuesta, los recuentos de cuota se reducirán pero los límites de cuota permanecerán sin cambios.} other {Esto eliminará todas las respuestas de encuestas y atributos de contacto asociados con estos contactos. Cualquier segmentación y personalización basada en los datos de estos contactos se perderá. Si estos contactos tienen respuestas que cuentan para las cuotas de encuesta, los recuentos de cuota se reducirán pero los límites de cuota permanecerán sin cambios.}}",
"displays": "Visualizaciones",
"edit_attribute": "Editar atributo", "edit_attribute": "Editar atributo",
"edit_attribute_description": "Actualiza la etiqueta y la descripción de este atributo.", "edit_attribute_description": "Actualiza la etiqueta y la descripción de este atributo.",
"edit_attribute_values": "Editar atributos", "edit_attribute_values": "Editar atributos",
@@ -679,7 +667,6 @@
"invalid_csv_column_names": "Nombre(s) de columna CSV no válido(s): {columns}. Los nombres de columna que se convertirán en nuevos atributos solo deben contener letras minúsculas, números y guiones bajos, y deben comenzar con una letra.", "invalid_csv_column_names": "Nombre(s) de columna CSV no válido(s): {columns}. Los nombres de columna que se convertirán en nuevos atributos solo deben contener letras minúsculas, números y guiones bajos, y deben comenzar con una letra.",
"invalid_date_format": "Formato de fecha no válido. Por favor, usa una fecha válida.", "invalid_date_format": "Formato de fecha no válido. Por favor, usa una fecha válida.",
"invalid_number_format": "Formato de número no válido. Por favor, introduce un número válido.", "invalid_number_format": "Formato de número no válido. Por favor, introduce un número válido.",
"no_activity_yet": "Aún no hay actividad",
"no_published_link_surveys_available": "No hay encuestas de enlace publicadas disponibles. Por favor, publica primero una encuesta de enlace.", "no_published_link_surveys_available": "No hay encuestas de enlace publicadas disponibles. Por favor, publica primero una encuesta de enlace.",
"no_published_surveys": "No hay encuestas publicadas", "no_published_surveys": "No hay encuestas publicadas",
"no_responses_found": "No se encontraron respuestas", "no_responses_found": "No se encontraron respuestas",
@@ -694,12 +681,10 @@
"select_a_survey": "Selecciona una encuesta", "select_a_survey": "Selecciona una encuesta",
"select_attribute": "Seleccionar atributo", "select_attribute": "Seleccionar atributo",
"select_attribute_key": "Seleccionar clave de atributo", "select_attribute_key": "Seleccionar clave de atributo",
"survey_viewed": "Encuesta vista",
"survey_viewed_at": "Vista el",
"system_attributes": "Atributos del sistema", "system_attributes": "Atributos del sistema",
"unlock_contacts_description": "Gestiona contactos y envía encuestas dirigidas", "unlock_contacts_description": "Gestiona contactos y envía encuestas dirigidas",
"unlock_contacts_title": "Desbloquea contactos con un plan superior", "unlock_contacts_title": "Desbloquea contactos con un plan superior",
"upload_contacts_error_attribute_type_mismatch": "El atributo {key} está tipado como {dataType} pero el CSV contiene valores no válidos: {values}", "upload_contacts_error_attribute_type_mismatch": "El atributo \"{key}\" está tipado como \"{dataType}\" pero el CSV contiene valores no válidos: {values}",
"upload_contacts_error_duplicate_mappings": "Se encontraron mapeos duplicados para los siguientes atributos: {attributes}", "upload_contacts_error_duplicate_mappings": "Se encontraron mapeos duplicados para los siguientes atributos: {attributes}",
"upload_contacts_error_file_too_large": "El tamaño del archivo supera el límite máximo de 800 KB", "upload_contacts_error_file_too_large": "El tamaño del archivo supera el límite máximo de 800 KB",
"upload_contacts_error_generic": "Se produjo un error al cargar los contactos. Por favor, inténtalo de nuevo más tarde.", "upload_contacts_error_generic": "Se produjo un error al cargar los contactos. Por favor, inténtalo de nuevo más tarde.",
@@ -767,12 +752,7 @@
"link_google_sheet": "Vincular Google Sheet", "link_google_sheet": "Vincular Google Sheet",
"link_new_sheet": "Vincular nueva hoja", "link_new_sheet": "Vincular nueva hoja",
"no_integrations_yet": "Tus integraciones de Google Sheet aparecerán aquí tan pronto como las añadas. ⏲️", "no_integrations_yet": "Tus integraciones de Google Sheet aparecerán aquí tan pronto como las añadas. ⏲️",
"reconnect_button": "Reconectar", "spreadsheet_url": "URL de la hoja de cálculo"
"reconnect_button_description": "Tu conexión con Google Sheets ha caducado. Reconecta para continuar sincronizando respuestas. Tus enlaces de hojas de cálculo y datos existentes se conservarán.",
"reconnect_button_tooltip": "Reconecta la integración para actualizar tu acceso. Tus enlaces de hojas de cálculo y datos existentes se conservarán.",
"spreadsheet_permission_error": "No tienes permiso para acceder a esta hoja de cálculo. Por favor, asegúrate de que la hoja de cálculo esté compartida con tu cuenta de Google y de que tengas acceso de escritura a la hoja de cálculo.",
"spreadsheet_url": "URL de la hoja de cálculo",
"token_expired_error": "El token de actualización de Google Sheets ha caducado o ha sido revocado. Reconecta la integración."
}, },
"include_created_at": "Incluir fecha de creación", "include_created_at": "Incluir fecha de creación",
"include_hidden_fields": "Incluir campos ocultos", "include_hidden_fields": "Incluir campos ocultos",
@@ -1059,7 +1039,7 @@
"recheck_license_invalid": "La clave de licencia no es válida. Por favor, verifica tu ENTERPRISE_LICENSE_KEY.", "recheck_license_invalid": "La clave de licencia no es válida. Por favor, verifica tu ENTERPRISE_LICENSE_KEY.",
"recheck_license_success": "Comprobación de licencia correcta", "recheck_license_success": "Comprobación de licencia correcta",
"recheck_license_unreachable": "El servidor de licencias no está disponible. Inténtalo de nuevo más tarde.", "recheck_license_unreachable": "El servidor de licencias no está disponible. Inténtalo de nuevo más tarde.",
"rechecking": "Volviendo a comprobar…", "rechecking": "Comprobando...",
"request_30_day_trial_license": "Solicitar licencia de prueba de 30 días", "request_30_day_trial_license": "Solicitar licencia de prueba de 30 días",
"saml_sso": "SAML SSO", "saml_sso": "SAML SSO",
"service_level_agreement": "Acuerdo de nivel de servicio", "service_level_agreement": "Acuerdo de nivel de servicio",
@@ -1087,7 +1067,7 @@
"email_customization_preview_email_heading": "Hola {userName}", "email_customization_preview_email_heading": "Hola {userName}",
"email_customization_preview_email_text": "Este es un correo electrónico de vista previa para mostrarte qué logotipo se mostrará en los correos electrónicos.", "email_customization_preview_email_text": "Este es un correo electrónico de vista previa para mostrarte qué logotipo se mostrará en los correos electrónicos.",
"error_deleting_organization_please_try_again": "Error al eliminar la organización. Por favor, inténtalo de nuevo.", "error_deleting_organization_please_try_again": "Error al eliminar la organización. Por favor, inténtalo de nuevo.",
"from_your_organization": "{memberName} de tu organización", "from_your_organization": "de tu organización",
"invitation_sent_once_more": "Invitación enviada una vez más.", "invitation_sent_once_more": "Invitación enviada una vez más.",
"invite_deleted_successfully": "Invitación eliminada correctamente", "invite_deleted_successfully": "Invitación eliminada correctamente",
"invite_expires_on": "La invitación expira el {date}", "invite_expires_on": "La invitación expira el {date}",
@@ -1136,7 +1116,7 @@
"notification_settings_updated": "Configuración de notificaciones actualizada", "notification_settings_updated": "Configuración de notificaciones actualizada",
"set_up_an_alert_to_get_an_email_on_new_responses": "Configura una alerta para recibir un correo electrónico sobre nuevas respuestas", "set_up_an_alert_to_get_an_email_on_new_responses": "Configura una alerta para recibir un correo electrónico sobre nuevas respuestas",
"use_the_integration": "Utiliza la integración", "use_the_integration": "Utiliza la integración",
"want_to_loop_in_organization_mates": "¿Quieres incluir a compañeros de la organización?", "want_to_loop_in_organization_mates": "¿Quieres incluir a compañeros de organización?",
"you_will_not_be_auto_subscribed_to_this_organizations_surveys_anymore": "¡Ya no serás suscrito automáticamente a las encuestas de esta organización!", "you_will_not_be_auto_subscribed_to_this_organizations_surveys_anymore": "¡Ya no serás suscrito automáticamente a las encuestas de esta organización!",
"you_will_not_receive_any_more_emails_for_responses_on_this_survey": "¡No recibirás más correos electrónicos por respuestas en esta encuesta!" "you_will_not_receive_any_more_emails_for_responses_on_this_survey": "¡No recibirás más correos electrónicos por respuestas en esta encuesta!"
}, },
@@ -1252,7 +1232,6 @@
"add_fallback_placeholder": "Añadir un marcador de posición para mostrar si no hay valor que recuperar.", "add_fallback_placeholder": "Añadir un marcador de posición para mostrar si no hay valor que recuperar.",
"add_hidden_field_id": "Añadir ID de campo oculto", "add_hidden_field_id": "Añadir ID de campo oculto",
"add_highlight_border": "Añadir borde destacado", "add_highlight_border": "Añadir borde destacado",
"add_highlight_border_description": "Solo se aplica a encuestas dentro del producto.",
"add_logic": "Añadir lógica", "add_logic": "Añadir lógica",
"add_none_of_the_above": "Añadir \"Ninguna de las anteriores\"", "add_none_of_the_above": "Añadir \"Ninguna de las anteriores\"",
"add_option": "Añadir opción", "add_option": "Añadir opción",
@@ -1269,14 +1248,12 @@
"adjust_survey_closed_message": "Ajustar mensaje 'Encuesta cerrada'", "adjust_survey_closed_message": "Ajustar mensaje 'Encuesta cerrada'",
"adjust_survey_closed_message_description": "Cambiar el mensaje que ven los visitantes cuando la encuesta está cerrada.", "adjust_survey_closed_message_description": "Cambiar el mensaje que ven los visitantes cuando la encuesta está cerrada.",
"adjust_the_theme_in_the": "Ajustar el tema en el", "adjust_the_theme_in_the": "Ajustar el tema en el",
"all_are_true": "todas son verdaderas",
"all_other_answers_will_continue_to": "Todas las demás respuestas continuarán", "all_other_answers_will_continue_to": "Todas las demás respuestas continuarán",
"allow_multi_select": "Permitir selección múltiple", "allow_multi_select": "Permitir selección múltiple",
"allow_multiple_files": "Permitir múltiples archivos", "allow_multiple_files": "Permitir múltiples archivos",
"allow_users_to_select_more_than_one_image": "Permitir a los usuarios seleccionar más de una imagen", "allow_users_to_select_more_than_one_image": "Permitir a los usuarios seleccionar más de una imagen",
"and_launch_surveys_in_your_website_or_app": "y lanzar encuestas en tu sitio web o aplicación.", "and_launch_surveys_in_your_website_or_app": "y lanzar encuestas en tu sitio web o aplicación.",
"animation": "Animación", "animation": "Animación",
"any_is_true": "alguna es verdadera",
"app_survey_description": "Integra una encuesta en tu aplicación web o sitio web para recopilar respuestas.", "app_survey_description": "Integra una encuesta en tu aplicación web o sitio web para recopilar respuestas.",
"assign": "Asignar =", "assign": "Asignar =",
"audience": "Audiencia", "audience": "Audiencia",
@@ -1307,7 +1284,7 @@
"calculate": "Calcular", "calculate": "Calcular",
"capture_a_new_action_to_trigger_a_survey_on": "Captura una nueva acción para activar una encuesta.", "capture_a_new_action_to_trigger_a_survey_on": "Captura una nueva acción para activar una encuesta.",
"capture_ip_address": "Capturar dirección IP", "capture_ip_address": "Capturar dirección IP",
"capture_ip_address_description": "Almacena la dirección IP del encuestado en los metadatos de respuesta para la detección de duplicados y fines de seguridad", "capture_ip_address_description": "Almacenar la dirección IP del encuestado en los metadatos de respuesta para la detección de duplicados y fines de seguridad",
"capture_new_action": "Capturar nueva acción", "capture_new_action": "Capturar nueva acción",
"card_arrangement_for_survey_type_derived": "Disposición de tarjetas para encuestas de tipo {surveyTypeDerived}", "card_arrangement_for_survey_type_derived": "Disposición de tarjetas para encuestas de tipo {surveyTypeDerived}",
"card_background_color": "Color de fondo de la tarjeta", "card_background_color": "Color de fondo de la tarjeta",
@@ -1318,7 +1295,7 @@
"casual": "Informal", "casual": "Informal",
"caution_edit_duplicate": "Duplicar y editar", "caution_edit_duplicate": "Duplicar y editar",
"caution_edit_published_survey": "¿Editar una encuesta publicada?", "caution_edit_published_survey": "¿Editar una encuesta publicada?",
"caution_explanation_intro": "Entendemos que es posible que aún quieras hacer cambios. Esto es lo que sucede si lo haces:", "caution_explanation_intro": "Entendemos que quizás aún quieras hacer cambios. Esto es lo que ocurre si lo haces: ",
"caution_explanation_new_responses_separated": "Las respuestas anteriores al cambio pueden no incluirse o incluirse solo parcialmente en el resumen de la encuesta.", "caution_explanation_new_responses_separated": "Las respuestas anteriores al cambio pueden no incluirse o incluirse solo parcialmente en el resumen de la encuesta.",
"caution_explanation_only_new_responses_in_summary": "Todos los datos, incluidas las respuestas anteriores, siguen estando disponibles para descargar en la página de resumen de la encuesta.", "caution_explanation_only_new_responses_in_summary": "Todos los datos, incluidas las respuestas anteriores, siguen estando disponibles para descargar en la página de resumen de la encuesta.",
"caution_explanation_responses_are_safe": "Las respuestas antiguas y nuevas se mezclan, lo que puede llevar a resúmenes de datos engañosos.", "caution_explanation_responses_are_safe": "Las respuestas antiguas y nuevas se mezclan, lo que puede llevar a resúmenes de datos engañosos.",
@@ -1453,6 +1430,7 @@
"follow_ups_modal_updated_successfull_toast": "Seguimiento actualizado y se guardará cuando guardes la encuesta.", "follow_ups_modal_updated_successfull_toast": "Seguimiento actualizado y se guardará cuando guardes la encuesta.",
"follow_ups_new": "Nuevo seguimiento", "follow_ups_new": "Nuevo seguimiento",
"follow_ups_upgrade_button_text": "Actualiza para habilitar seguimientos", "follow_ups_upgrade_button_text": "Actualiza para habilitar seguimientos",
"form_styling": "Estilo del formulario",
"formbricks_sdk_is_not_connected": "El SDK de Formbricks no está conectado", "formbricks_sdk_is_not_connected": "El SDK de Formbricks no está conectado",
"four_points": "4 puntos", "four_points": "4 puntos",
"heading": "Encabezado", "heading": "Encabezado",
@@ -1542,7 +1520,7 @@
"option_idx": "Opción {choiceIndex}", "option_idx": "Opción {choiceIndex}",
"option_used_in_logic_error": "Esta opción se utiliza en la lógica de la pregunta {questionIndex}. Por favor, elimínala de la lógica primero.", "option_used_in_logic_error": "Esta opción se utiliza en la lógica de la pregunta {questionIndex}. Por favor, elimínala de la lógica primero.",
"optional": "Opcional", "optional": "Opcional",
"options": "Opciones*", "options": "Opciones",
"options_used_in_logic_bulk_error": "Las siguientes opciones se utilizan en la lógica: {questionIndexes}. Por favor, elimínalas de la lógica primero.", "options_used_in_logic_bulk_error": "Las siguientes opciones se utilizan en la lógica: {questionIndexes}. Por favor, elimínalas de la lógica primero.",
"override_theme_with_individual_styles_for_this_survey": "Anular el tema con estilos individuales para esta encuesta.", "override_theme_with_individual_styles_for_this_survey": "Anular el tema con estilos individuales para esta encuesta.",
"overwrite_global_waiting_time": "Establecer periodo de espera personalizado", "overwrite_global_waiting_time": "Establecer periodo de espera personalizado",
@@ -1567,7 +1545,6 @@
"question_deleted": "Pregunta eliminada.", "question_deleted": "Pregunta eliminada.",
"question_duplicated": "Pregunta duplicada.", "question_duplicated": "Pregunta duplicada.",
"question_id_updated": "ID de pregunta actualizado", "question_id_updated": "ID de pregunta actualizado",
"question_number": "Pregunta {number}",
"question_used_in_logic_warning_text": "Los elementos de este bloque se usan en una regla de lógica, ¿estás seguro de que quieres eliminarlo?", "question_used_in_logic_warning_text": "Los elementos de este bloque se usan en una regla de lógica, ¿estás seguro de que quieres eliminarlo?",
"question_used_in_logic_warning_title": "Inconsistencia de lógica", "question_used_in_logic_warning_title": "Inconsistencia de lógica",
"question_used_in_quota": "Esta pregunta se está utilizando en la cuota “{quotaName}”", "question_used_in_quota": "Esta pregunta se está utilizando en la cuota “{quotaName}”",
@@ -1626,7 +1603,7 @@
"response_limits_redirections_and_more": "Límites de respuestas, redirecciones y más.", "response_limits_redirections_and_more": "Límites de respuestas, redirecciones y más.",
"response_options": "Opciones de respuesta", "response_options": "Opciones de respuesta",
"roundness": "Redondez", "roundness": "Redondez",
"roundness_description": "Controla qué tan redondeadas están las esquinas.", "roundness_description": "Controla qué tan redondeadas están las esquinas de la tarjeta.",
"row_used_in_logic_error": "Esta fila se utiliza en la lógica de la pregunta {questionIndex}. Por favor, elimínala de la lógica primero.", "row_used_in_logic_error": "Esta fila se utiliza en la lógica de la pregunta {questionIndex}. Por favor, elimínala de la lógica primero.",
"rows": "Filas", "rows": "Filas",
"save_and_close": "Guardar y cerrar", "save_and_close": "Guardar y cerrar",
@@ -1672,7 +1649,6 @@
"survey_completed_subheading": "Esta encuesta gratuita y de código abierto ha sido cerrada", "survey_completed_subheading": "Esta encuesta gratuita y de código abierto ha sido cerrada",
"survey_display_settings": "Ajustes de visualización de la encuesta", "survey_display_settings": "Ajustes de visualización de la encuesta",
"survey_placement": "Ubicación de la encuesta", "survey_placement": "Ubicación de la encuesta",
"survey_styling": "Estilo del formulario",
"survey_trigger": "Activador de la encuesta", "survey_trigger": "Activador de la encuesta",
"switch_multi_language_on_to_get_started": "Activa el modo multiidioma para comenzar 👉", "switch_multi_language_on_to_get_started": "Activa el modo multiidioma para comenzar 👉",
"target_block_not_found": "Bloque objetivo no encontrado", "target_block_not_found": "Bloque objetivo no encontrado",
@@ -1735,7 +1711,7 @@
"pattern": "Coincide con el patrón regex", "pattern": "Coincide con el patrón regex",
"phone": "Es un teléfono válido", "phone": "Es un teléfono válido",
"rank_all_options": "Clasificar todas las opciones", "rank_all_options": "Clasificar todas las opciones",
"select_file_extensions": "Selecciona extensiones de archivo", "select_file_extensions": "Selecciona extensiones de archivo...",
"select_option": "Seleccionar opción", "select_option": "Seleccionar opción",
"start_date": "Fecha de inicio", "start_date": "Fecha de inicio",
"url": "Es una URL válida" "url": "Es una URL válida"
@@ -1761,9 +1737,9 @@
"waiting_time_across_surveys": "Periodo de espera (entre encuestas)", "waiting_time_across_surveys": "Periodo de espera (entre encuestas)",
"waiting_time_across_surveys_description": "Para evitar la fatiga de encuestas, elige cómo interactúa esta encuesta con el periodo de espera general del espacio de trabajo.", "waiting_time_across_surveys_description": "Para evitar la fatiga de encuestas, elige cómo interactúa esta encuesta con el periodo de espera general del espacio de trabajo.",
"welcome_message": "Mensaje de bienvenida", "welcome_message": "Mensaje de bienvenida",
"when": "Cuando",
"without_a_filter_all_of_your_users_can_be_surveyed": "Sin un filtro, todos tus usuarios pueden ser encuestados.", "without_a_filter_all_of_your_users_can_be_surveyed": "Sin un filtro, todos tus usuarios pueden ser encuestados.",
"you_have_not_created_a_segment_yet": "Aún no has creado un segmento", "you_have_not_created_a_segment_yet": "Aún no has creado un segmento",
"you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "Necesitas tener dos o más idiomas configurados en tu proyecto para trabajar con traducciones.",
"your_description_here_recall_information_with": "Tu descripción aquí. Recupera información con @", "your_description_here_recall_information_with": "Tu descripción aquí. Recupera información con @",
"your_question_here_recall_information_with": "Tu pregunta aquí. Recupera información con @", "your_question_here_recall_information_with": "Tu pregunta aquí. Recupera información con @",
"your_web_app": "Tu aplicación web", "your_web_app": "Tu aplicación web",
@@ -1833,7 +1809,7 @@
"disable_single_use_link_modal_button": "Desactivar enlaces de uso único", "disable_single_use_link_modal_button": "Desactivar enlaces de uso único",
"disable_single_use_link_modal_description": "Si has compartido enlaces de uso único, los participantes ya no podrán responder a la encuesta.", "disable_single_use_link_modal_description": "Si has compartido enlaces de uso único, los participantes ya no podrán responder a la encuesta.",
"generate_and_download_links": "Generar y descargar enlaces", "generate_and_download_links": "Generar y descargar enlaces",
"generate_links_error": "No se pudieron generar los enlaces de un solo uso. Por favor, trabaja directamente con la API.", "generate_links_error": "No se pudieron generar enlaces de uso único. Por favor, trabaja directamente con la API",
"multi_use_link": "Enlace de uso múltiple", "multi_use_link": "Enlace de uso múltiple",
"multi_use_link_description": "Recopila múltiples respuestas de encuestados anónimos con un solo enlace.", "multi_use_link_description": "Recopila múltiples respuestas de encuestados anónimos con un solo enlace.",
"multi_use_powers_other_channels_description": "Si lo desactivas, estos otros canales de distribución también se desactivarán.", "multi_use_powers_other_channels_description": "Si lo desactivas, estos otros canales de distribución también se desactivarán.",
@@ -1944,7 +1920,7 @@
} }
}, },
"summary": { "summary": {
"added_filter_for_responses_where_answer_to_question": "Se ha añadido un filtro para las respuestas donde la respuesta a la pregunta {questionIdx} es {filterComboBoxValue} - {filterValue}", "added_filter_for_responses_where_answer_to_question": "Filtro añadido para respuestas donde la respuesta a la pregunta {questionIdx} es {filterComboBoxValue} - {filterValue} ",
"added_filter_for_responses_where_answer_to_question_is_skipped": "Filtro añadido para respuestas donde la respuesta a la pregunta {questionIdx} se ha omitido", "added_filter_for_responses_where_answer_to_question_is_skipped": "Filtro añadido para respuestas donde la respuesta a la pregunta {questionIdx} se ha omitido",
"aggregated": "Agregado", "aggregated": "Agregado",
"all_responses_csv": "Todas las respuestas (CSV)", "all_responses_csv": "Todas las respuestas (CSV)",
@@ -1971,7 +1947,6 @@
"filtered_responses_excel": "Respuestas filtradas (Excel)", "filtered_responses_excel": "Respuestas filtradas (Excel)",
"generating_qr_code": "Generando código QR", "generating_qr_code": "Generando código QR",
"impressions": "Impresiones", "impressions": "Impresiones",
"impressions_identified_only": "Solo se muestran impresiones de contactos identificados",
"impressions_tooltip": "Número de veces que se ha visto la encuesta.", "impressions_tooltip": "Número de veces que se ha visto la encuesta.",
"in_app": { "in_app": {
"connection_description": "La encuesta se mostrará a los usuarios de tu sitio web que cumplan con los criterios enumerados a continuación", "connection_description": "La encuesta se mostrará a los usuarios de tu sitio web que cumplan con los criterios enumerados a continuación",
@@ -2014,7 +1989,6 @@
"last_quarter": "Último trimestre", "last_quarter": "Último trimestre",
"last_year": "Último año", "last_year": "Último año",
"limit": "Límite", "limit": "Límite",
"no_identified_impressions": "No hay impresiones de contactos identificados",
"no_responses_found": "No se han encontrado respuestas", "no_responses_found": "No se han encontrado respuestas",
"other_values_found": "Otros valores encontrados", "other_values_found": "Otros valores encontrados",
"overall": "General", "overall": "General",
@@ -2037,7 +2011,6 @@
"starts": "Inicios", "starts": "Inicios",
"starts_tooltip": "Número de veces que se ha iniciado la encuesta.", "starts_tooltip": "Número de veces que se ha iniciado la encuesta.",
"survey_reset_successfully": "¡Encuesta restablecida correctamente! Se eliminaron {responseCount} respuestas y {displayCount} visualizaciones.", "survey_reset_successfully": "¡Encuesta restablecida correctamente! Se eliminaron {responseCount} respuestas y {displayCount} visualizaciones.",
"survey_results": "Resultados de {surveyName}",
"this_month": "Este mes", "this_month": "Este mes",
"this_quarter": "Este trimestre", "this_quarter": "Este trimestre",
"this_year": "Este año", "this_year": "Este año",
@@ -2157,7 +2130,7 @@
"add_background_color_description": "Añade un color de fondo al contenedor del logotipo.", "add_background_color_description": "Añade un color de fondo al contenedor del logotipo.",
"advanced_styling_field_border_radius": "Radio del borde", "advanced_styling_field_border_radius": "Radio del borde",
"advanced_styling_field_button_bg": "Fondo del botón", "advanced_styling_field_button_bg": "Fondo del botón",
"advanced_styling_field_button_bg_description": "Rellena el botón “Siguiente / “Enviar.", "advanced_styling_field_button_bg_description": "Rellena el botón siguiente / enviar.",
"advanced_styling_field_button_border_radius_description": "Redondea las esquinas del botón.", "advanced_styling_field_button_border_radius_description": "Redondea las esquinas del botón.",
"advanced_styling_field_button_font_size_description": "Escala el texto de la etiqueta del botón.", "advanced_styling_field_button_font_size_description": "Escala el texto de la etiqueta del botón.",
"advanced_styling_field_button_font_weight_description": "Hace el texto del botón más ligero o más grueso.", "advanced_styling_field_button_font_weight_description": "Hace el texto del botón más ligero o más grueso.",
@@ -2171,7 +2144,7 @@
"advanced_styling_field_description_size": "Tamaño de fuente de la descripción", "advanced_styling_field_description_size": "Tamaño de fuente de la descripción",
"advanced_styling_field_description_size_description": "Escala el texto de la descripción.", "advanced_styling_field_description_size_description": "Escala el texto de la descripción.",
"advanced_styling_field_description_weight": "Grosor de fuente de la descripción", "advanced_styling_field_description_weight": "Grosor de fuente de la descripción",
"advanced_styling_field_description_weight_description": "Hace que el texto de la descripción sea más claro o más negrita.", "advanced_styling_field_description_weight_description": "Hace el texto de la descripción más ligero o más grueso.",
"advanced_styling_field_font_size": "Tamaño de fuente", "advanced_styling_field_font_size": "Tamaño de fuente",
"advanced_styling_field_font_weight": "Grosor de fuente", "advanced_styling_field_font_weight": "Grosor de fuente",
"advanced_styling_field_headline_color": "Color del titular", "advanced_styling_field_headline_color": "Color del titular",
@@ -2194,8 +2167,6 @@
"advanced_styling_field_input_text_description": "Colorea el texto escrito en los campos de entrada.", "advanced_styling_field_input_text_description": "Colorea el texto escrito en los campos de entrada.",
"advanced_styling_field_option_bg": "Fondo", "advanced_styling_field_option_bg": "Fondo",
"advanced_styling_field_option_bg_description": "Rellena los elementos de opción.", "advanced_styling_field_option_bg_description": "Rellena los elementos de opción.",
"advanced_styling_field_option_border": "Color del borde",
"advanced_styling_field_option_border_description": "Delimita las opciones de radio y casillas de verificación.",
"advanced_styling_field_option_border_radius_description": "Redondea las esquinas de las opciones.", "advanced_styling_field_option_border_radius_description": "Redondea las esquinas de las opciones.",
"advanced_styling_field_option_font_size_description": "Escala el texto de la etiqueta de opción.", "advanced_styling_field_option_font_size_description": "Escala el texto de la etiqueta de opción.",
"advanced_styling_field_option_label": "Color de la etiqueta", "advanced_styling_field_option_label": "Color de la etiqueta",
@@ -2250,7 +2221,7 @@
"show_powered_by_formbricks": "Mostrar firma 'Powered by Formbricks'", "show_powered_by_formbricks": "Mostrar firma 'Powered by Formbricks'",
"styling_updated_successfully": "Estilo actualizado correctamente", "styling_updated_successfully": "Estilo actualizado correctamente",
"suggest_colors": "Sugerir colores", "suggest_colors": "Sugerir colores",
"suggested_colors_applied_please_save": "Colores sugeridos generados correctamente. Pulsa Guardar para conservar los cambios.", "suggested_colors_applied_please_save": "Colores sugeridos generados correctamente. Pulsa \"Guardar\" para conservar los cambios.",
"theme": "Tema", "theme": "Tema",
"theme_settings_description": "Crea un tema de estilo para todas las encuestas. Puedes activar el estilo personalizado para cada encuesta." "theme_settings_description": "Crea un tema de estilo para todas las encuestas. Puedes activar el estilo personalizado para cada encuesta."
}, },
@@ -2334,7 +2305,7 @@
"check_inbox_or_spam": "Por favor, comprueba también tu carpeta de spam si no ves el correo electrónico en tu bandeja de entrada.", "check_inbox_or_spam": "Por favor, comprueba también tu carpeta de spam si no ves el correo electrónico en tu bandeja de entrada.",
"completed": "Esta encuesta está cerrada.", "completed": "Esta encuesta está cerrada.",
"create_your_own": "Crea tu propia encuesta de código abierto", "create_your_own": "Crea tu propia encuesta de código abierto",
"enter_pin": "Esta encuesta está protegida. Introduce el PIN a continuación.", "enter_pin": "Esta encuesta está protegida. Introduce el PIN a continuación",
"just_curious": "¿Solo tienes curiosidad?", "just_curious": "¿Solo tienes curiosidad?",
"link_invalid": "Esta encuesta solo se puede realizar por invitación.", "link_invalid": "Esta encuesta solo se puede realizar por invitación.",
"paused": "Esta encuesta está temporalmente pausada.", "paused": "Esta encuesta está temporalmente pausada.",
@@ -2405,16 +2376,6 @@
"alignment_and_engagement_survey_question_4_headline": "¿Cómo puede mejorar la empresa su alineación de visión y estrategia?", "alignment_and_engagement_survey_question_4_headline": "¿Cómo puede mejorar la empresa su alineación de visión y estrategia?",
"alignment_and_engagement_survey_question_4_placeholder": "Escribe tu respuesta aquí...", "alignment_and_engagement_survey_question_4_placeholder": "Escribe tu respuesta aquí...",
"back": "Atrás", "back": "Atrás",
"block_1": "Bloque 1",
"block_10": "Bloque 10",
"block_2": "Bloque 2",
"block_3": "Bloque 3",
"block_4": "Bloque 4",
"block_5": "Bloque 5",
"block_6": "Bloque 6",
"block_7": "Bloque 7",
"block_8": "Bloque 8",
"block_9": "Bloque 9",
"book_interview": "Reservar entrevista", "book_interview": "Reservar entrevista",
"build_product_roadmap_description": "Identifica lo ÚNICO que tus usuarios desean más y constrúyelo.", "build_product_roadmap_description": "Identifica lo ÚNICO que tus usuarios desean más y constrúyelo.",
"build_product_roadmap_name": "Crear hoja de ruta del producto", "build_product_roadmap_name": "Crear hoja de ruta del producto",
@@ -2570,7 +2531,7 @@
"csat_question_3_choice_8": "Baja calidad", "csat_question_3_choice_8": "Baja calidad",
"csat_question_3_choice_9": "Poco fiable", "csat_question_3_choice_9": "Poco fiable",
"csat_question_3_headline": "¿Cuáles de las siguientes palabras utilizarías para describir nuestro $[projectName]?", "csat_question_3_headline": "¿Cuáles de las siguientes palabras utilizarías para describir nuestro $[projectName]?",
"csat_question_3_subheader": "Por favor, selecciona todas las opciones que correspondan:", "csat_question_3_subheader": "Selecciona todas las opciones que correspondan:",
"csat_question_4_choice_1": "Extremadamente bien", "csat_question_4_choice_1": "Extremadamente bien",
"csat_question_4_choice_2": "Muy bien", "csat_question_4_choice_2": "Muy bien",
"csat_question_4_choice_3": "Bastante bien", "csat_question_4_choice_3": "Bastante bien",
@@ -2622,6 +2583,7 @@
"csat_survey_question_3_headline": "Vaya, ¡lo sentimos! ¿Hay algo que podamos hacer para mejorar tu experiencia?", "csat_survey_question_3_headline": "Vaya, ¡lo sentimos! ¿Hay algo que podamos hacer para mejorar tu experiencia?",
"csat_survey_question_3_placeholder": "Escribe tu respuesta aquí...", "csat_survey_question_3_placeholder": "Escribe tu respuesta aquí...",
"cta_description": "Muestra información y anima a los usuarios a realizar una acción específica", "cta_description": "Muestra información y anima a los usuarios a realizar una acción específica",
"custom_survey_block_1_name": "Bloque 1",
"custom_survey_description": "Crea una encuesta sin plantilla.", "custom_survey_description": "Crea una encuesta sin plantilla.",
"custom_survey_name": "Empezar desde cero", "custom_survey_name": "Empezar desde cero",
"custom_survey_question_1_headline": "¿Qué te gustaría saber?", "custom_survey_question_1_headline": "¿Qué te gustaría saber?",
@@ -3024,9 +2986,6 @@
"preview_survey_question_2_choice_2_label": "¡No, gracias!", "preview_survey_question_2_choice_2_label": "¡No, gracias!",
"preview_survey_question_2_headline": "¿Quieres estar al tanto?", "preview_survey_question_2_headline": "¿Quieres estar al tanto?",
"preview_survey_question_2_subheader": "Esta es una descripción de ejemplo.", "preview_survey_question_2_subheader": "Esta es una descripción de ejemplo.",
"preview_survey_question_open_text_headline": "¿Hay algo más que te gustaría compartir?",
"preview_survey_question_open_text_placeholder": "Escribe tu respuesta aquí...",
"preview_survey_question_open_text_subheader": "Tus comentarios nos ayudan a mejorar.",
"preview_survey_welcome_card_headline": "¡Bienvenido!", "preview_survey_welcome_card_headline": "¡Bienvenido!",
"prioritize_features_description": "Identifica las funciones que tus usuarios necesitan más y menos.", "prioritize_features_description": "Identifica las funciones que tus usuarios necesitan más y menos.",
"prioritize_features_name": "Priorizar funciones", "prioritize_features_name": "Priorizar funciones",
@@ -3096,7 +3055,7 @@
"professional_development_survey_question_2_choice_5": "Investigación individual", "professional_development_survey_question_2_choice_5": "Investigación individual",
"professional_development_survey_question_2_choice_6": "Otros", "professional_development_survey_question_2_choice_6": "Otros",
"professional_development_survey_question_2_headline": "¿Qué tipos de actividades de desarrollo profesional crees que serían más valiosas para tu crecimiento?", "professional_development_survey_question_2_headline": "¿Qué tipos de actividades de desarrollo profesional crees que serían más valiosas para tu crecimiento?",
"professional_development_survey_question_2_subheader": "Por favor, selecciona todas las opciones que correspondan:", "professional_development_survey_question_2_subheader": "Selecciona todas las que correspondan",
"professional_development_survey_question_3_choice_1": "Sí", "professional_development_survey_question_3_choice_1": "Sí",
"professional_development_survey_question_3_choice_2": "No", "professional_development_survey_question_3_choice_2": "No",
"professional_development_survey_question_3_headline": "¿Has dedicado tiempo a tu desarrollo profesional en el pasado?", "professional_development_survey_question_3_headline": "¿Has dedicado tiempo a tu desarrollo profesional en el pasado?",
@@ -3275,18 +3234,5 @@
"usability_question_9_headline": "Me sentí seguro mientras usaba el sistema.", "usability_question_9_headline": "Me sentí seguro mientras usaba el sistema.",
"usability_rating_description": "Mide la usabilidad percibida pidiendo a los usuarios que valoren su experiencia con tu producto mediante una encuesta estandarizada de 10 preguntas.", "usability_rating_description": "Mide la usabilidad percibida pidiendo a los usuarios que valoren su experiencia con tu producto mediante una encuesta estandarizada de 10 preguntas.",
"usability_score_name": "Puntuación de usabilidad del sistema (SUS)" "usability_score_name": "Puntuación de usabilidad del sistema (SUS)"
},
"workflows": {
"coming_soon_description": "¡Gracias por compartir tu idea de flujo de trabajo con nosotros! Actualmente estamos diseñando esta funcionalidad y tus comentarios nos ayudarán a construir exactamente lo que necesitas.",
"coming_soon_title": "¡Ya casi estamos!",
"follow_up_label": "¿Hay algo más que te gustaría añadir?",
"follow_up_placeholder": "¿Qué tareas específicas te gustaría automatizar? ¿Alguna herramienta o integración que quieras incluir?",
"generate_button": "Generar flujo de trabajo",
"heading": "¿Qué flujo de trabajo quieres crear?",
"placeholder": "Describe el flujo de trabajo que quieres generar…",
"subheading": "Genera tu flujo de trabajo en segundos.",
"submit_button": "Añadir detalles",
"thank_you_description": "Tu aportación nos ayuda a construir la funcionalidad de Flujos de trabajo que realmente necesitas. Te mantendremos informado sobre nuestro progreso.",
"thank_you_title": "¡Gracias por tus comentarios!"
} }
} }
+33 -87
View File
@@ -175,12 +175,9 @@
"copy": "Copier", "copy": "Copier",
"copy_code": "Copier le code", "copy_code": "Copier le code",
"copy_link": "Copier le lien", "copy_link": "Copier le lien",
"count_attributes": "{count, plural, one {{count} attribut} other {{count} attributs}}", "count_attributes": "{value, plural, one {{value} attribut} other {{value} attributs}}",
"count_contacts": "{count, plural, one {{count} contact} other {{count} contacts}}", "count_contacts": "{value, plural, one {# contact} other {# contacts} }",
"count_members": "{count, plural, one {{count} membre} other {{count} membres}}", "count_responses": "{value, plural, other {# réponses}}",
"count_questions": "{count, plural, one {{count} question} other {{count} questions}}",
"count_responses": "{count, plural, one {{count} réponse} other {{count} réponses}}",
"count_selections": "{count, plural, one {{count} sélection} other {{count} sélections}}",
"create_new_organization": "Créer une nouvelle organisation", "create_new_organization": "Créer une nouvelle organisation",
"create_segment": "Créer un segment", "create_segment": "Créer un segment",
"create_survey": "Créer un sondage", "create_survey": "Créer un sondage",
@@ -194,7 +191,6 @@
"days": "jours", "days": "jours",
"default": "Par défaut", "default": "Par défaut",
"delete": "Supprimer", "delete": "Supprimer",
"delete_what": "Supprimer {deleteWhat}",
"description": "Description", "description": "Description",
"dev_env": "Environnement de développement", "dev_env": "Environnement de développement",
"development": "Développement", "development": "Développement",
@@ -210,8 +206,6 @@
"download": "Télécharger", "download": "Télécharger",
"draft": "Brouillon", "draft": "Brouillon",
"duplicate": "Dupliquer", "duplicate": "Dupliquer",
"duplicate_copy": "(copie)",
"duplicate_copy_number": "(copie {copyNumber})",
"e_commerce": "E-commerce", "e_commerce": "E-commerce",
"edit": "Modifier", "edit": "Modifier",
"email": "Email", "email": "Email",
@@ -224,16 +218,13 @@
"error": "Erreur", "error": "Erreur",
"error_component_description": "Cette ressource n'existe pas ou vous n'avez pas les droits nécessaires pour y accéder.", "error_component_description": "Cette ressource n'existe pas ou vous n'avez pas les droits nécessaires pour y accéder.",
"error_component_title": "Erreur de chargement des ressources", "error_component_title": "Erreur de chargement des ressources",
"error_loading_data": "Erreur lors du chargement des données",
"error_rate_limit_description": "Nombre maximal de demandes atteint. Veuillez réessayer plus tard.", "error_rate_limit_description": "Nombre maximal de demandes atteint. Veuillez réessayer plus tard.",
"error_rate_limit_title": "Limite de Taux Dépassée", "error_rate_limit_title": "Limite de Taux Dépassée",
"expand_rows": "Développer les lignes", "expand_rows": "Développer les lignes",
"failed_to_copy_to_clipboard": "Échec de la copie dans le presse-papiers", "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_organizations": "Échec du chargement des organisations",
"failed_to_load_workspaces": "Échec du chargement des projets", "failed_to_load_workspaces": "Échec du chargement des projets",
"filter": "Filtre",
"finish": "Terminer", "finish": "Terminer",
"first_name": "Prénom",
"follow_these": "Suivez ceci", "follow_these": "Suivez ceci",
"formbricks_version": "Version de Formbricks", "formbricks_version": "Version de Formbricks",
"full_name": "Nom complet", "full_name": "Nom complet",
@@ -246,7 +237,6 @@
"hidden_field": "Champ caché", "hidden_field": "Champ caché",
"hidden_fields": "Champs cachés", "hidden_fields": "Champs cachés",
"hide_column": "Cacher la colonne", "hide_column": "Cacher la colonne",
"id": "ID",
"image": "Image", "image": "Image",
"images": "Images", "images": "Images",
"import": "Importer", "import": "Importer",
@@ -264,7 +254,6 @@
"key": "Clé", "key": "Clé",
"label": "Étiquette", "label": "Étiquette",
"language": "Langue", "language": "Langue",
"last_name": "Nom de famille",
"learn_more": "En savoir plus", "learn_more": "En savoir plus",
"license_expired": "License Expired", "license_expired": "License Expired",
"light_overlay": "Claire", "light_overlay": "Claire",
@@ -279,6 +268,7 @@
"look_and_feel": "Apparence", "look_and_feel": "Apparence",
"manage": "Gérer", "manage": "Gérer",
"marketing": "Marketing", "marketing": "Marketing",
"member": "Membre",
"members": "Membres", "members": "Membres",
"members_and_teams": "Membres & Équipes", "members_and_teams": "Membres & Équipes",
"membership_not_found": "Abonnement non trouvé", "membership_not_found": "Abonnement non trouvé",
@@ -290,7 +280,6 @@
"move_down": "Déplacer vers le bas", "move_down": "Déplacer vers le bas",
"move_up": "Déplacer vers le haut", "move_up": "Déplacer vers le haut",
"multiple_languages": "Plusieurs langues", "multiple_languages": "Plusieurs langues",
"my_product": "mon produit",
"name": "Nom", "name": "Nom",
"new": "Nouveau", "new": "Nouveau",
"new_version_available": "Formbricks {version} est là. Mettez à jour maintenant !", "new_version_available": "Formbricks {version} est là. Mettez à jour maintenant !",
@@ -386,6 +375,8 @@
"select_teams": "Sélectionner les équipes", "select_teams": "Sélectionner les équipes",
"selected": "Sélectionné", "selected": "Sélectionné",
"selected_questions": "Questions sélectionnées", "selected_questions": "Questions sélectionnées",
"selection": "Sélection",
"selections": "Sélections",
"send_test_email": "Envoyer un e-mail de test", "send_test_email": "Envoyer un e-mail de test",
"session_not_found": "Session non trouvée", "session_not_found": "Session non trouvée",
"settings": "Paramètres", "settings": "Paramètres",
@@ -394,7 +385,6 @@
"show_response_count": "Afficher le nombre de réponses", "show_response_count": "Afficher le nombre de réponses",
"shown": "Montré", "shown": "Montré",
"size": "Taille", "size": "Taille",
"skip": "Ignorer",
"skipped": "Passé", "skipped": "Passé",
"skips": "Sauter", "skips": "Sauter",
"some_files_failed_to_upload": "Certains fichiers n'ont pas pu être téléchargés", "some_files_failed_to_upload": "Certains fichiers n'ont pas pu être téléchargés",
@@ -438,7 +428,6 @@
"top_right": "En haut à droite", "top_right": "En haut à droite",
"try_again": "Réessayer", "try_again": "Réessayer",
"type": "Type", "type": "Type",
"unknown_survey": "Enquête inconnue",
"unlock_more_workspaces_with_a_higher_plan": "Débloquez plus de projets avec un forfait supérieur.", "unlock_more_workspaces_with_a_higher_plan": "Débloquez plus de projets avec un forfait supérieur.",
"update": "Mise à jour", "update": "Mise à jour",
"updated": "Mise à jour", "updated": "Mise à jour",
@@ -464,7 +453,6 @@
"website_survey": "Sondage de site web", "website_survey": "Sondage de site web",
"weeks": "semaines", "weeks": "semaines",
"welcome_card": "Carte de bienvenue", "welcome_card": "Carte de bienvenue",
"workflows": "Workflows",
"workspace_configuration": "Configuration du projet", "workspace_configuration": "Configuration du projet",
"workspace_created_successfully": "Projet créé avec succès", "workspace_created_successfully": "Projet créé avec succès",
"workspace_creation_description": "Organisez les enquêtes dans des projets pour un meilleur contrôle d'accès.", "workspace_creation_description": "Organisez les enquêtes dans des projets pour un meilleur contrôle d'accès.",
@@ -647,27 +635,27 @@
"attribute_value": "Valeur", "attribute_value": "Valeur",
"attribute_value_placeholder": "Valeur d'attribut", "attribute_value_placeholder": "Valeur d'attribut",
"attributes_msg_attribute_limit_exceeded": "Impossible de créer {count, plural, one {# nouvel attribut} other {# nouveaux attributs}} car cela dépasserait la limite maximale de {limit} classes d'attributs. Les attributs existants ont été mis à jour avec succès.", "attributes_msg_attribute_limit_exceeded": "Impossible de créer {count, plural, one {# nouvel attribut} other {# nouveaux attributs}} car cela dépasserait la limite maximale de {limit} classes d'attributs. Les attributs existants ont été mis à jour avec succès.",
"attributes_msg_attribute_type_validation_error": "{error} (l'attribut {key} a le type de données: {dataType})", "attributes_msg_attribute_type_validation_error": "{error} (l'attribut « {key} » a le type de données: {dataType})",
"attributes_msg_email_already_exists": "L'adresse e-mail existe déjà pour cet environnement et n'a pas été mise à jour.", "attributes_msg_email_already_exists": "L'adresse e-mail existe déjà pour cet environnement et n'a pas été mise à jour.",
"attributes_msg_email_or_userid_required": "L'e-mail ou l'identifiant utilisateur est requis. Les valeurs existantes ont été conservées.", "attributes_msg_email_or_userid_required": "L'adresse e-mail ou l'identifiant utilisateur est requis. Les valeurs existantes ont été conservées.",
"attributes_msg_new_attribute_created": "Nouvel attribut {key} créé avec le type {dataType}", "attributes_msg_new_attribute_created": "Nouvel attribut « {key} » créé avec le type « {dataType} »",
"attributes_msg_userid_already_exists": "L'identifiant utilisateur existe déjà pour cet environnement et n'a pas été mis à jour.", "attributes_msg_userid_already_exists": "L'identifiant utilisateur existe déjà pour cet environnement et n'a pas été mis à jour.",
"contact_deleted_successfully": "Contact supprimé avec succès", "contact_deleted_successfully": "Contact supprimé avec succès",
"contact_not_found": "Aucun contact trouvé", "contact_not_found": "Aucun contact trouvé",
"contacts_table_refresh": "Actualiser les contacts", "contacts_table_refresh": "Actualiser les contacts",
"contacts_table_refresh_success": "Contacts rafraîchis avec succès", "contacts_table_refresh_success": "Contacts rafraîchis avec succès",
"create_attribute": "Créer un attribut", "create_attribute": "Créer un attribut",
"create_key": "Créer une clé",
"create_new_attribute": "Créer un nouvel attribut", "create_new_attribute": "Créer un nouvel attribut",
"create_new_attribute_description": "Créez un nouvel attribut à des fins de segmentation.", "create_new_attribute_description": "Créez un nouvel attribut à des fins de segmentation.",
"custom_attributes": "Attributs personnalisés", "custom_attributes": "Attributs personnalisés",
"data_type": "Type de données", "data_type": "Type de données",
"data_type_cannot_be_changed": "Le type de données ne peut pas être modifié après la création", "data_type_cannot_be_changed": "Le type de données ne peut pas être modifié après la création",
"data_type_description": "Choisis comment cet attribut doit être stocké et filtré", "data_type_description": "Choisis comment cet attribut doit être stocké et filtré",
"date_value_required": "La valeur de date est requise. Utilisez le bouton de suppression pour retirer cet attribut si vous ne souhaitez pas définir de date.", "date_value_required": "Une valeur de date est requise. Utilise le bouton supprimer pour retirer cet attribut si tu ne veux pas définir de date.",
"delete_attribute_confirmation": "{value, plural, one {Cela supprimera l'attribut sélectionné. Toutes les données de contact associées à cet attribut seront perdues.} other {Cela supprimera les attributs sélectionnés. Toutes les données de contact associées à ces attributs seront perdues.}}", "delete_attribute_confirmation": "{value, plural, one {Cela supprimera l'attribut sélectionné. Toutes les données de contact associées à cet attribut seront perdues.} other {Cela supprimera les attributs sélectionnés. Toutes les données de contact associées à ces attributs seront perdues.}}",
"delete_contact_confirmation": "Cela supprimera toutes les réponses aux enquêtes et les attributs de contact associés à ce contact. Toute la personnalisation et le ciblage basés sur les données de ce contact seront perdus.", "delete_contact_confirmation": "Cela supprimera toutes les réponses aux enquêtes et les attributs de contact associés à ce contact. Toute la personnalisation et le ciblage basés sur les données de ce contact seront perdus.",
"delete_contact_confirmation_with_quotas": "{value, plural, other {Cela supprimera toutes les réponses aux enquêtes et les attributs de contact associés à ce contact. Toute la personnalisation et le ciblage basés sur les données de ce contact seront perdus. Si ce contact a des réponses qui comptent dans les quotas de l'enquête, les comptes de quotas seront réduits mais les limites de quota resteront inchangées.}}", "delete_contact_confirmation_with_quotas": "{value, plural, other {Cela supprimera toutes les réponses aux enquêtes et les attributs de contact associés à ce contact. Toute la personnalisation et le ciblage basés sur les données de ce contact seront perdus. Si ce contact a des réponses qui comptent dans les quotas de l'enquête, les comptes de quotas seront réduits mais les limites de quota resteront inchangées.}}",
"displays": "Affichages",
"edit_attribute": "Modifier l'attribut", "edit_attribute": "Modifier l'attribut",
"edit_attribute_description": "Mettez à jour l'étiquette et la description de cet attribut.", "edit_attribute_description": "Mettez à jour l'étiquette et la description de cet attribut.",
"edit_attribute_values": "Modifier les attributs", "edit_attribute_values": "Modifier les attributs",
@@ -679,7 +667,6 @@
"invalid_csv_column_names": "Nom(s) de colonne CSV invalide(s): {columns}. Les noms de colonnes qui deviendront de nouveaux attributs ne doivent contenir que des lettres minuscules, des chiffres et des underscores, et doivent commencer par une lettre.", "invalid_csv_column_names": "Nom(s) de colonne CSV invalide(s): {columns}. Les noms de colonnes qui deviendront de nouveaux attributs ne doivent contenir que des lettres minuscules, des chiffres et des underscores, et doivent commencer par une lettre.",
"invalid_date_format": "Format de date invalide. Merci d'utiliser une date valide.", "invalid_date_format": "Format de date invalide. Merci d'utiliser une date valide.",
"invalid_number_format": "Format de nombre invalide. Veuillez saisir un nombre valide.", "invalid_number_format": "Format de nombre invalide. Veuillez saisir un nombre valide.",
"no_activity_yet": "Aucune activité pour le moment",
"no_published_link_surveys_available": "Aucune enquête par lien publiée n'est disponible. Veuillez d'abord publier une enquête par lien.", "no_published_link_surveys_available": "Aucune enquête par lien publiée n'est disponible. Veuillez d'abord publier une enquête par lien.",
"no_published_surveys": "Aucune enquête publiée", "no_published_surveys": "Aucune enquête publiée",
"no_responses_found": "Aucune réponse trouvée", "no_responses_found": "Aucune réponse trouvée",
@@ -694,12 +681,10 @@
"select_a_survey": "Sélectionner une enquête", "select_a_survey": "Sélectionner une enquête",
"select_attribute": "Sélectionner un attribut", "select_attribute": "Sélectionner un attribut",
"select_attribute_key": "Sélectionner une clé d'attribut", "select_attribute_key": "Sélectionner une clé d'attribut",
"survey_viewed": "Enquête consultée",
"survey_viewed_at": "Consultée le",
"system_attributes": "Attributs système", "system_attributes": "Attributs système",
"unlock_contacts_description": "Gérer les contacts et envoyer des enquêtes ciblées", "unlock_contacts_description": "Gérer les contacts et envoyer des enquêtes ciblées",
"unlock_contacts_title": "Débloquez des contacts avec un plan supérieur.", "unlock_contacts_title": "Débloquez des contacts avec un plan supérieur.",
"upload_contacts_error_attribute_type_mismatch": "L'attribut {key} est de type {dataType} mais le CSV contient des valeurs invalides: {values}", "upload_contacts_error_attribute_type_mismatch": "L'attribut « {key} » est de type « {dataType} » mais le CSV contient des valeurs invalides: {values}",
"upload_contacts_error_duplicate_mappings": "Mappages en double trouvés pour les attributs suivants: {attributes}", "upload_contacts_error_duplicate_mappings": "Mappages en double trouvés pour les attributs suivants: {attributes}",
"upload_contacts_error_file_too_large": "La taille du fichier dépasse la limite maximale de 800 Ko", "upload_contacts_error_file_too_large": "La taille du fichier dépasse la limite maximale de 800 Ko",
"upload_contacts_error_generic": "Une erreur s'est produite lors de l'importation des contacts. Veuillez réessayer plus tard.", "upload_contacts_error_generic": "Une erreur s'est produite lors de l'importation des contacts. Veuillez réessayer plus tard.",
@@ -767,12 +752,7 @@
"link_google_sheet": "Lien Google Sheet", "link_google_sheet": "Lien Google Sheet",
"link_new_sheet": "Lier une nouvelle feuille", "link_new_sheet": "Lier une nouvelle feuille",
"no_integrations_yet": "Vos intégrations Google Sheets apparaîtront ici dès que vous les ajouterez. ⏲️", "no_integrations_yet": "Vos intégrations Google Sheets apparaîtront ici dès que vous les ajouterez. ⏲️",
"reconnect_button": "Reconnecter", "spreadsheet_url": "URL de la feuille de calcul"
"reconnect_button_description": "Votre connexion Google Sheets a expiré. Veuillez vous reconnecter pour continuer à synchroniser les réponses. Vos liens de feuilles de calcul et données existants seront préservés.",
"reconnect_button_tooltip": "Reconnectez l'intégration pour actualiser votre accès. Vos liens de feuilles de calcul et données existants seront préservés.",
"spreadsheet_permission_error": "Vous n'avez pas l'autorisation d'accéder à cette feuille de calcul. Veuillez vous assurer que la feuille de calcul est partagée avec votre compte Google et que vous disposez d'un accès en écriture.",
"spreadsheet_url": "URL de la feuille de calcul",
"token_expired_error": "Le jeton d'actualisation Google Sheets a expiré ou a été révoqué. Veuillez reconnecter l'intégration."
}, },
"include_created_at": "Inclure la date de création", "include_created_at": "Inclure la date de création",
"include_hidden_fields": "Inclure les champs cachés", "include_hidden_fields": "Inclure les champs cachés",
@@ -981,7 +961,7 @@
"current_plan": "Forfait actuel", "current_plan": "Forfait actuel",
"current_tier_limit": "Limite de niveau actuel", "current_tier_limit": "Limite de niveau actuel",
"custom": "Personnalisé et Échelle", "custom": "Personnalisé et Échelle",
"custom_contacts_limit": "Limite de contacts personnalisée", "custom_contacts_limit": "Personnalisation du nombre de contacts",
"custom_response_limit": "Personnalisation des réponses", "custom_response_limit": "Personnalisation des réponses",
"custom_workspace_limit": "Limite de projets personnalisée", "custom_workspace_limit": "Limite de projets personnalisée",
"email_embedded_surveys": "Sondages intégrés à des e-mails", "email_embedded_surveys": "Sondages intégrés à des e-mails",
@@ -1059,7 +1039,7 @@
"recheck_license_invalid": "La clé de licence est invalide. Veuillez vérifier votre ENTERPRISE_LICENSE_KEY.", "recheck_license_invalid": "La clé de licence est invalide. Veuillez vérifier votre ENTERPRISE_LICENSE_KEY.",
"recheck_license_success": "Vérification de la licence réussie", "recheck_license_success": "Vérification de la licence réussie",
"recheck_license_unreachable": "Le serveur de licences est inaccessible. Veuillez réessayer plus tard.", "recheck_license_unreachable": "Le serveur de licences est inaccessible. Veuillez réessayer plus tard.",
"rechecking": "Revérification en cours", "rechecking": "Revérification en cours...",
"request_30_day_trial_license": "Demander une licence d'essai de 30 jours", "request_30_day_trial_license": "Demander une licence d'essai de 30 jours",
"saml_sso": "SAML SSO", "saml_sso": "SAML SSO",
"service_level_agreement": "Accord de niveau de service", "service_level_agreement": "Accord de niveau de service",
@@ -1087,7 +1067,7 @@
"email_customization_preview_email_heading": "Salut {userName}", "email_customization_preview_email_heading": "Salut {userName}",
"email_customization_preview_email_text": "Cette est une prévisualisation d'e-mail pour vous montrer quel logo sera rendu dans les e-mails.", "email_customization_preview_email_text": "Cette est une prévisualisation d'e-mail pour vous montrer quel logo sera rendu dans les e-mails.",
"error_deleting_organization_please_try_again": "Erreur lors de la suppression de l'organisation. Veuillez réessayer.", "error_deleting_organization_please_try_again": "Erreur lors de la suppression de l'organisation. Veuillez réessayer.",
"from_your_organization": "{memberName} de votre organisation", "from_your_organization": "de votre organisation",
"invitation_sent_once_more": "Invitation envoyée une fois de plus.", "invitation_sent_once_more": "Invitation envoyée une fois de plus.",
"invite_deleted_successfully": "Invitation supprimée avec succès", "invite_deleted_successfully": "Invitation supprimée avec succès",
"invite_expires_on": "L'invitation expire le {date}", "invite_expires_on": "L'invitation expire le {date}",
@@ -1136,7 +1116,7 @@
"notification_settings_updated": "Paramètres de notification mis à jour", "notification_settings_updated": "Paramètres de notification mis à jour",
"set_up_an_alert_to_get_an_email_on_new_responses": "Vous pouvez configurer une alerte afin d'être informé par e-mail en cas de nouvelles réponses.", "set_up_an_alert_to_get_an_email_on_new_responses": "Vous pouvez configurer une alerte afin d'être informé par e-mail en cas de nouvelles réponses.",
"use_the_integration": "Utilisez l'intégration", "use_the_integration": "Utilisez l'intégration",
"want_to_loop_in_organization_mates": "Souhaitez-vous inclure des membres de l'organisation?", "want_to_loop_in_organization_mates": "Voulez-vous inclure des collègues de l'organisation ?",
"you_will_not_be_auto_subscribed_to_this_organizations_surveys_anymore": "Vous ne serez plus automatiquement abonné aux enquêtes de cette organisation !", "you_will_not_be_auto_subscribed_to_this_organizations_surveys_anymore": "Vous ne serez plus automatiquement abonné aux enquêtes de cette organisation !",
"you_will_not_receive_any_more_emails_for_responses_on_this_survey": "Vous ne recevrez plus d'e-mails concernant les réponses à cette enquête !" "you_will_not_receive_any_more_emails_for_responses_on_this_survey": "Vous ne recevrez plus d'e-mails concernant les réponses à cette enquête !"
}, },
@@ -1252,7 +1232,6 @@
"add_fallback_placeholder": "Ajouter un espace réservé à afficher s'il n'y a pas de valeur à rappeler.", "add_fallback_placeholder": "Ajouter un espace réservé à afficher s'il n'y a pas de valeur à rappeler.",
"add_hidden_field_id": "Ajouter un champ caché ID", "add_hidden_field_id": "Ajouter un champ caché ID",
"add_highlight_border": "Ajouter une bordure de surlignage", "add_highlight_border": "Ajouter une bordure de surlignage",
"add_highlight_border_description": "S'applique uniquement aux sondages intégrés au produit.",
"add_logic": "Ajouter de la logique", "add_logic": "Ajouter de la logique",
"add_none_of_the_above": "Ajouter \"Aucun des éléments ci-dessus\"", "add_none_of_the_above": "Ajouter \"Aucun des éléments ci-dessus\"",
"add_option": "Ajouter une option", "add_option": "Ajouter une option",
@@ -1269,14 +1248,12 @@
"adjust_survey_closed_message": "Ajuster le message \"Sondage fermé\"", "adjust_survey_closed_message": "Ajuster le message \"Sondage fermé\"",
"adjust_survey_closed_message_description": "Modifiez le message que les visiteurs voient lorsque l'enquête est fermée.", "adjust_survey_closed_message_description": "Modifiez le message que les visiteurs voient lorsque l'enquête est fermée.",
"adjust_the_theme_in_the": "Ajustez le thème dans le", "adjust_the_theme_in_the": "Ajustez le thème dans le",
"all_are_true": "toutes sont vraies",
"all_other_answers_will_continue_to": "Toutes les autres réponses continueront à", "all_other_answers_will_continue_to": "Toutes les autres réponses continueront à",
"allow_multi_select": "Autoriser la sélection multiple", "allow_multi_select": "Autoriser la sélection multiple",
"allow_multiple_files": "Autoriser plusieurs fichiers", "allow_multiple_files": "Autoriser plusieurs fichiers",
"allow_users_to_select_more_than_one_image": "Permettre aux utilisateurs de sélectionner plusieurs images", "allow_users_to_select_more_than_one_image": "Permettre aux utilisateurs de sélectionner plusieurs images",
"and_launch_surveys_in_your_website_or_app": "et lancez des enquêtes sur votre site web ou votre application.", "and_launch_surveys_in_your_website_or_app": "et lancez des enquêtes sur votre site web ou votre application.",
"animation": "Animation", "animation": "Animation",
"any_is_true": "au moins une est vraie",
"app_survey_description": "Intégrez une enquête dans votre application web ou votre site web pour collecter des réponses.", "app_survey_description": "Intégrez une enquête dans votre application web ou votre site web pour collecter des réponses.",
"assign": "Attribuer =", "assign": "Attribuer =",
"audience": "Public", "audience": "Public",
@@ -1307,7 +1284,7 @@
"calculate": "Calculer", "calculate": "Calculer",
"capture_a_new_action_to_trigger_a_survey_on": "Capturez une nouvelle action pour déclencher une enquête.", "capture_a_new_action_to_trigger_a_survey_on": "Capturez une nouvelle action pour déclencher une enquête.",
"capture_ip_address": "Capturer l'adresse IP", "capture_ip_address": "Capturer l'adresse IP",
"capture_ip_address_description": "Enregistrer l'adresse IP du répondant dans les métadonnées de réponse à des fins de détection des doublons et de sécurité", "capture_ip_address_description": "Stocker l'adresse IP du répondant dans les métadonnées de réponse à des fins de détection des doublons et de sécurité",
"capture_new_action": "Capturer une nouvelle action", "capture_new_action": "Capturer une nouvelle action",
"card_arrangement_for_survey_type_derived": "Disposition des cartes pour les enquêtes {surveyTypeDerived}", "card_arrangement_for_survey_type_derived": "Disposition des cartes pour les enquêtes {surveyTypeDerived}",
"card_background_color": "Couleur de fond de la carte", "card_background_color": "Couleur de fond de la carte",
@@ -1318,7 +1295,7 @@
"casual": "Décontracté", "casual": "Décontracté",
"caution_edit_duplicate": "Dupliquer et modifier", "caution_edit_duplicate": "Dupliquer et modifier",
"caution_edit_published_survey": "Modifier un sondage publié ?", "caution_edit_published_survey": "Modifier un sondage publié ?",
"caution_explanation_intro": "Nous comprenons que vous souhaitiez peut-être encore apporter des modifications. Voici ce qui se passe si vous le faites:", "caution_explanation_intro": "Nous comprenons que vous souhaitiez encore apporter des modifications. Voici ce qui se passe si vous le faites : ",
"caution_explanation_new_responses_separated": "Les réponses avant le changement peuvent ne pas être ou ne faire partie que partiellement du résumé de l'enquête.", "caution_explanation_new_responses_separated": "Les réponses avant le changement peuvent ne pas être ou ne faire partie que partiellement du résumé de l'enquête.",
"caution_explanation_only_new_responses_in_summary": "Toutes les données, y compris les réponses passées, restent disponibles en téléchargement sur la page de résumé de l'enquête.", "caution_explanation_only_new_responses_in_summary": "Toutes les données, y compris les réponses passées, restent disponibles en téléchargement sur la page de résumé de l'enquête.",
"caution_explanation_responses_are_safe": "Les réponses anciennes et nouvelles se mélangent, ce qui peut entraîner des résumés de données trompeurs.", "caution_explanation_responses_are_safe": "Les réponses anciennes et nouvelles se mélangent, ce qui peut entraîner des résumés de données trompeurs.",
@@ -1453,6 +1430,7 @@
"follow_ups_modal_updated_successfull_toast": "\"Suivi mis à jour et sera enregistré une fois que vous sauvegarderez le sondage.\"", "follow_ups_modal_updated_successfull_toast": "\"Suivi mis à jour et sera enregistré une fois que vous sauvegarderez le sondage.\"",
"follow_ups_new": "Nouveau suivi", "follow_ups_new": "Nouveau suivi",
"follow_ups_upgrade_button_text": "Passez à la version supérieure pour activer les relances", "follow_ups_upgrade_button_text": "Passez à la version supérieure pour activer les relances",
"form_styling": "Style de formulaire",
"formbricks_sdk_is_not_connected": "Le SDK Formbricks n'est pas connecté", "formbricks_sdk_is_not_connected": "Le SDK Formbricks n'est pas connecté",
"four_points": "4 points", "four_points": "4 points",
"heading": "En-tête", "heading": "En-tête",
@@ -1542,7 +1520,7 @@
"option_idx": "Option {choiceIndex}", "option_idx": "Option {choiceIndex}",
"option_used_in_logic_error": "Cette option est utilisée dans la logique de la question {questionIndex}. Veuillez d'abord la supprimer de la logique.", "option_used_in_logic_error": "Cette option est utilisée dans la logique de la question {questionIndex}. Veuillez d'abord la supprimer de la logique.",
"optional": "Optionnel", "optional": "Optionnel",
"options": "Options*", "options": "Options",
"options_used_in_logic_bulk_error": "Les options suivantes sont utilisées dans la logique: {questionIndexes}. Veuillez d'abord les supprimer de la logique.", "options_used_in_logic_bulk_error": "Les options suivantes sont utilisées dans la logique: {questionIndexes}. Veuillez d'abord les supprimer de la logique.",
"override_theme_with_individual_styles_for_this_survey": "Override the theme with individual styles for this survey.", "override_theme_with_individual_styles_for_this_survey": "Override the theme with individual styles for this survey.",
"overwrite_global_waiting_time": "Définir une période de refroidissement personnalisée", "overwrite_global_waiting_time": "Définir une période de refroidissement personnalisée",
@@ -1567,7 +1545,6 @@
"question_deleted": "Question supprimée.", "question_deleted": "Question supprimée.",
"question_duplicated": "Question dupliquée.", "question_duplicated": "Question dupliquée.",
"question_id_updated": "ID de la question mis à jour", "question_id_updated": "ID de la question mis à jour",
"question_number": "Question {number}",
"question_used_in_logic_warning_text": "Des éléments de ce bloc sont utilisés dans une règle logique, êtes-vous sûr de vouloir le supprimer?", "question_used_in_logic_warning_text": "Des éléments de ce bloc sont utilisés dans une règle logique, êtes-vous sûr de vouloir le supprimer?",
"question_used_in_logic_warning_title": "Incohérence de logique", "question_used_in_logic_warning_title": "Incohérence de logique",
"question_used_in_quota": "Cette question est utilisée dans le quota “{quotaName}”", "question_used_in_quota": "Cette question est utilisée dans le quota “{quotaName}”",
@@ -1626,7 +1603,7 @@
"response_limits_redirections_and_more": "Limites de réponse, redirections et plus.", "response_limits_redirections_and_more": "Limites de réponse, redirections et plus.",
"response_options": "Options de réponse", "response_options": "Options de réponse",
"roundness": "Rondeur", "roundness": "Rondeur",
"roundness_description": "Contrôle l'arrondi des coins.", "roundness_description": "Contrôle l'arrondi des coins de la carte.",
"row_used_in_logic_error": "Cette ligne est utilisée dans la logique de la question {questionIndex}. Veuillez d'abord la supprimer de la logique.", "row_used_in_logic_error": "Cette ligne est utilisée dans la logique de la question {questionIndex}. Veuillez d'abord la supprimer de la logique.",
"rows": "Lignes", "rows": "Lignes",
"save_and_close": "Enregistrer et fermer", "save_and_close": "Enregistrer et fermer",
@@ -1672,7 +1649,6 @@
"survey_completed_subheading": "Cette enquête gratuite et open-source a été fermée", "survey_completed_subheading": "Cette enquête gratuite et open-source a été fermée",
"survey_display_settings": "Paramètres d'affichage de l'enquête", "survey_display_settings": "Paramètres d'affichage de l'enquête",
"survey_placement": "Placement de l'enquête", "survey_placement": "Placement de l'enquête",
"survey_styling": "Style de formulaire",
"survey_trigger": "Déclencheur d'enquête", "survey_trigger": "Déclencheur d'enquête",
"switch_multi_language_on_to_get_started": "Activez le mode multilingue pour commencer 👉", "switch_multi_language_on_to_get_started": "Activez le mode multilingue pour commencer 👉",
"target_block_not_found": "Bloc cible non trouvé", "target_block_not_found": "Bloc cible non trouvé",
@@ -1735,7 +1711,7 @@
"pattern": "Correspond au modèle d'expression régulière", "pattern": "Correspond au modèle d'expression régulière",
"phone": "Est un numéro de téléphone valide", "phone": "Est un numéro de téléphone valide",
"rank_all_options": "Classer toutes les options", "rank_all_options": "Classer toutes les options",
"select_file_extensions": "Sélectionner les extensions de fichier", "select_file_extensions": "Sélectionner les extensions de fichier...",
"select_option": "Sélectionner une option", "select_option": "Sélectionner une option",
"start_date": "Date de début", "start_date": "Date de début",
"url": "Est une URL valide" "url": "Est une URL valide"
@@ -1761,9 +1737,9 @@
"waiting_time_across_surveys": "Période de refroidissement (entre les sondages)", "waiting_time_across_surveys": "Période de refroidissement (entre les sondages)",
"waiting_time_across_surveys_description": "Pour éviter la fatigue liée aux sondages, choisissez comment ce sondage interagit avec la période de refroidissement globale de l'espace de travail.", "waiting_time_across_surveys_description": "Pour éviter la fatigue liée aux sondages, choisissez comment ce sondage interagit avec la période de refroidissement globale de l'espace de travail.",
"welcome_message": "Message de bienvenue", "welcome_message": "Message de bienvenue",
"when": "Quand",
"without_a_filter_all_of_your_users_can_be_surveyed": "Sans filtre, tous vos utilisateurs peuvent être sondés.", "without_a_filter_all_of_your_users_can_be_surveyed": "Sans filtre, tous vos utilisateurs peuvent être sondés.",
"you_have_not_created_a_segment_yet": "Tu n'as pas encore créé de segment.", "you_have_not_created_a_segment_yet": "Tu n'as pas encore créé de segment.",
"you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "Vous devez avoir deux langues ou plus configurées dans votre espace de travail pour travailler avec les traductions.",
"your_description_here_recall_information_with": "Votre description ici. Rappelez-vous des informations avec @", "your_description_here_recall_information_with": "Votre description ici. Rappelez-vous des informations avec @",
"your_question_here_recall_information_with": "Votre question ici. Rappelez-vous des informations avec @", "your_question_here_recall_information_with": "Votre question ici. Rappelez-vous des informations avec @",
"your_web_app": "Votre application web", "your_web_app": "Votre application web",
@@ -1833,7 +1809,7 @@
"disable_single_use_link_modal_button": "Désactiver les liens à usage unique", "disable_single_use_link_modal_button": "Désactiver les liens à usage unique",
"disable_single_use_link_modal_description": "Si vous avez partagé des liens à usage unique, les participants ne pourront plus répondre au sondage.", "disable_single_use_link_modal_description": "Si vous avez partagé des liens à usage unique, les participants ne pourront plus répondre au sondage.",
"generate_and_download_links": "Générer et télécharger les liens", "generate_and_download_links": "Générer et télécharger les liens",
"generate_links_error": "Impossible de générer les liens à usage unique. Veuillez travailler directement avec lAPI.", "generate_links_error": "Les liens à usage unique n'ont pas pu être générés. Veuillez travailler directement avec l'API",
"multi_use_link": "Lien multi-usage", "multi_use_link": "Lien multi-usage",
"multi_use_link_description": "Recueillir plusieurs réponses de répondants anonymes avec un seul lien.", "multi_use_link_description": "Recueillir plusieurs réponses de répondants anonymes avec un seul lien.",
"multi_use_powers_other_channels_description": "Si vous le désactivez, ces autres canaux de distribution seront également désactivés.", "multi_use_powers_other_channels_description": "Si vous le désactivez, ces autres canaux de distribution seront également désactivés.",
@@ -1944,7 +1920,7 @@
} }
}, },
"summary": { "summary": {
"added_filter_for_responses_where_answer_to_question": "Filtre ajouté pour les réponses où la réponse à la question {questionIdx} est {filterComboBoxValue} - {filterValue}", "added_filter_for_responses_where_answer_to_question": "Filtre ajouté pour les réponses où la réponse à la question '{'questionIdx'}' est '{'filterComboBoxValue'}' - '{'filterValue'}' ",
"added_filter_for_responses_where_answer_to_question_is_skipped": "Filtre ajouté pour les réponses où la réponse à la question '{'questionIdx'}' est ignorée", "added_filter_for_responses_where_answer_to_question_is_skipped": "Filtre ajouté pour les réponses où la réponse à la question '{'questionIdx'}' est ignorée",
"aggregated": "Agrégé", "aggregated": "Agrégé",
"all_responses_csv": "Tous les réponses (CSV)", "all_responses_csv": "Tous les réponses (CSV)",
@@ -1971,7 +1947,6 @@
"filtered_responses_excel": "Réponses filtrées (Excel)", "filtered_responses_excel": "Réponses filtrées (Excel)",
"generating_qr_code": "Génération du code QR", "generating_qr_code": "Génération du code QR",
"impressions": "Impressions", "impressions": "Impressions",
"impressions_identified_only": "Affichage uniquement des impressions des contacts identifiés",
"impressions_tooltip": "Nombre de fois que l'enquête a été consultée.", "impressions_tooltip": "Nombre de fois que l'enquête a été consultée.",
"in_app": { "in_app": {
"connection_description": "Le sondage sera affiché aux utilisateurs de votre site web, qui correspondent aux critères listés ci-dessous", "connection_description": "Le sondage sera affiché aux utilisateurs de votre site web, qui correspondent aux critères listés ci-dessous",
@@ -2014,7 +1989,6 @@
"last_quarter": "dernier trimestre", "last_quarter": "dernier trimestre",
"last_year": "l'année dernière", "last_year": "l'année dernière",
"limit": "Limite", "limit": "Limite",
"no_identified_impressions": "Aucune impression des contacts identifiés",
"no_responses_found": "Aucune réponse trouvée", "no_responses_found": "Aucune réponse trouvée",
"other_values_found": "D'autres valeurs trouvées", "other_values_found": "D'autres valeurs trouvées",
"overall": "Globalement", "overall": "Globalement",
@@ -2037,7 +2011,6 @@
"starts": "Commence", "starts": "Commence",
"starts_tooltip": "Nombre de fois que l'enquête a été commencée.", "starts_tooltip": "Nombre de fois que l'enquête a été commencée.",
"survey_reset_successfully": "Réinitialisation du sondage réussie ! {responseCount} réponses et {displayCount} affichages ont été supprimés.", "survey_reset_successfully": "Réinitialisation du sondage réussie ! {responseCount} réponses et {displayCount} affichages ont été supprimés.",
"survey_results": "Résultats de {surveyName}",
"this_month": "Ce mois-ci", "this_month": "Ce mois-ci",
"this_quarter": "Ce trimestre", "this_quarter": "Ce trimestre",
"this_year": "Cette année", "this_year": "Cette année",
@@ -2157,7 +2130,7 @@
"add_background_color_description": "Ajoutez une couleur d'arrière-plan au conteneur du logo.", "add_background_color_description": "Ajoutez une couleur d'arrière-plan au conteneur du logo.",
"advanced_styling_field_border_radius": "Rayon de bordure", "advanced_styling_field_border_radius": "Rayon de bordure",
"advanced_styling_field_button_bg": "Arrière-plan du bouton", "advanced_styling_field_button_bg": "Arrière-plan du bouton",
"advanced_styling_field_button_bg_description": "Remplit le bouton Suivant / Envoyer.", "advanced_styling_field_button_bg_description": "Remplit le bouton Suivant / Envoyer.",
"advanced_styling_field_button_border_radius_description": "Arrondit les coins du bouton.", "advanced_styling_field_button_border_radius_description": "Arrondit les coins du bouton.",
"advanced_styling_field_button_font_size_description": "Ajuste la taille du texte du libellé du bouton.", "advanced_styling_field_button_font_size_description": "Ajuste la taille du texte du libellé du bouton.",
"advanced_styling_field_button_font_weight_description": "Rend le texte du bouton plus léger ou plus gras.", "advanced_styling_field_button_font_weight_description": "Rend le texte du bouton plus léger ou plus gras.",
@@ -2171,7 +2144,7 @@
"advanced_styling_field_description_size": "Taille de police de la description", "advanced_styling_field_description_size": "Taille de police de la description",
"advanced_styling_field_description_size_description": "Ajuste la taille du texte de description.", "advanced_styling_field_description_size_description": "Ajuste la taille du texte de description.",
"advanced_styling_field_description_weight": "Graisse de police de la description", "advanced_styling_field_description_weight": "Graisse de police de la description",
"advanced_styling_field_description_weight_description": "Rend le texte de la description plus léger ou plus gras.", "advanced_styling_field_description_weight_description": "Rend le texte de description plus léger ou plus gras.",
"advanced_styling_field_font_size": "Taille de police", "advanced_styling_field_font_size": "Taille de police",
"advanced_styling_field_font_weight": "Graisse de police", "advanced_styling_field_font_weight": "Graisse de police",
"advanced_styling_field_headline_color": "Couleur du titre", "advanced_styling_field_headline_color": "Couleur du titre",
@@ -2194,8 +2167,6 @@
"advanced_styling_field_input_text_description": "Colore le texte saisi dans les champs.", "advanced_styling_field_input_text_description": "Colore le texte saisi dans les champs.",
"advanced_styling_field_option_bg": "Arrière-plan", "advanced_styling_field_option_bg": "Arrière-plan",
"advanced_styling_field_option_bg_description": "Remplit les éléments d'option.", "advanced_styling_field_option_bg_description": "Remplit les éléments d'option.",
"advanced_styling_field_option_border": "Couleur de bordure",
"advanced_styling_field_option_border_description": "Contours des options de boutons radio et de cases à cocher.",
"advanced_styling_field_option_border_radius_description": "Arrondit les coins des options.", "advanced_styling_field_option_border_radius_description": "Arrondit les coins des options.",
"advanced_styling_field_option_font_size_description": "Ajuste la taille du texte des libellés d'option.", "advanced_styling_field_option_font_size_description": "Ajuste la taille du texte des libellés d'option.",
"advanced_styling_field_option_label": "Couleur de l'étiquette", "advanced_styling_field_option_label": "Couleur de l'étiquette",
@@ -2250,7 +2221,7 @@
"show_powered_by_formbricks": "Afficher la signature « Propulsé par Formbricks »", "show_powered_by_formbricks": "Afficher la signature « Propulsé par Formbricks »",
"styling_updated_successfully": "Style mis à jour avec succès", "styling_updated_successfully": "Style mis à jour avec succès",
"suggest_colors": "Suggérer des couleurs", "suggest_colors": "Suggérer des couleurs",
"suggested_colors_applied_please_save": "Couleurs suggérées générées avec succès. Appuyez sur Enregistrer pour conserver les modifications.", "suggested_colors_applied_please_save": "Couleurs suggérées générées avec succès. Appuyez sur « Enregistrer » pour conserver les modifications.",
"theme": "Thème", "theme": "Thème",
"theme_settings_description": "Créez un thème de style pour toutes les enquêtes. Vous pouvez activer un style personnalisé pour chaque enquête." "theme_settings_description": "Créez un thème de style pour toutes les enquêtes. Vous pouvez activer un style personnalisé pour chaque enquête."
}, },
@@ -2334,7 +2305,7 @@
"check_inbox_or_spam": "Veuillez également vérifier votre dossier de spam si vous ne voyez pas l'e-mail dans votre boîte de réception.", "check_inbox_or_spam": "Veuillez également vérifier votre dossier de spam si vous ne voyez pas l'e-mail dans votre boîte de réception.",
"completed": "Cette enquête gratuite et open-source a été fermée.", "completed": "Cette enquête gratuite et open-source a été fermée.",
"create_your_own": "Créez le vôtre", "create_your_own": "Créez le vôtre",
"enter_pin": "Ce sondage est protégé. Entrez le code PIN ci-dessous.", "enter_pin": "Ce sondage est protégé. Entrez le code PIN ci-dessous",
"just_curious": "Juste curieux ?", "just_curious": "Juste curieux ?",
"link_invalid": "Cette enquête ne peut être réalisée que sur invitation.", "link_invalid": "Cette enquête ne peut être réalisée que sur invitation.",
"paused": "Cette enquête gratuite et open-source est temporairement suspendue.", "paused": "Cette enquête gratuite et open-source est temporairement suspendue.",
@@ -2405,16 +2376,6 @@
"alignment_and_engagement_survey_question_4_headline": "Comment l'entreprise peut-elle améliorer l'alignement de sa vision et de sa stratégie ?", "alignment_and_engagement_survey_question_4_headline": "Comment l'entreprise peut-elle améliorer l'alignement de sa vision et de sa stratégie ?",
"alignment_and_engagement_survey_question_4_placeholder": "Entrez votre réponse ici...", "alignment_and_engagement_survey_question_4_placeholder": "Entrez votre réponse ici...",
"back": "Retour", "back": "Retour",
"block_1": "Bloc 1",
"block_10": "Bloc 10",
"block_2": "Bloc 2",
"block_3": "Bloc 3",
"block_4": "Bloc 4",
"block_5": "Bloc 5",
"block_6": "Bloc 6",
"block_7": "Bloc 7",
"block_8": "Bloc 8",
"block_9": "Bloc 9",
"book_interview": "Réserver un entretien", "book_interview": "Réserver un entretien",
"build_product_roadmap_description": "Identifiez la chose UNIQUE que vos utilisateurs désirent le plus et construisez-la.", "build_product_roadmap_description": "Identifiez la chose UNIQUE que vos utilisateurs désirent le plus et construisez-la.",
"build_product_roadmap_name": "Élaborer la feuille de route du produit", "build_product_roadmap_name": "Élaborer la feuille de route du produit",
@@ -2570,7 +2531,7 @@
"csat_question_3_choice_8": "Qualité médiocre", "csat_question_3_choice_8": "Qualité médiocre",
"csat_question_3_choice_9": "Peu fiable", "csat_question_3_choice_9": "Peu fiable",
"csat_question_3_headline": "Lequel des mots suivants utiliseriez-vous pour décrire notre $[projectName] ?", "csat_question_3_headline": "Lequel des mots suivants utiliseriez-vous pour décrire notre $[projectName] ?",
"csat_question_3_subheader": "Veuillez sélectionner tout ce qui s'applique:", "csat_question_3_subheader": "Sélectionnez tout ce qui s'applique :",
"csat_question_4_choice_1": "Extrêmement bien", "csat_question_4_choice_1": "Extrêmement bien",
"csat_question_4_choice_2": "Très bien", "csat_question_4_choice_2": "Très bien",
"csat_question_4_choice_3": "Plutôt bien", "csat_question_4_choice_3": "Plutôt bien",
@@ -2622,6 +2583,7 @@
"csat_survey_question_3_headline": "Ah, désolé ! Y a-t-il quelque chose que nous puissions faire pour améliorer votre expérience ?", "csat_survey_question_3_headline": "Ah, désolé ! Y a-t-il quelque chose que nous puissions faire pour améliorer votre expérience ?",
"csat_survey_question_3_placeholder": "Entrez votre réponse ici...", "csat_survey_question_3_placeholder": "Entrez votre réponse ici...",
"cta_description": "Afficher des informations et inciter les utilisateurs à effectuer une action spécifique", "cta_description": "Afficher des informations et inciter les utilisateurs à effectuer une action spécifique",
"custom_survey_block_1_name": "Bloc 1",
"custom_survey_description": "Créez une enquête sans utiliser de modèle.", "custom_survey_description": "Créez une enquête sans utiliser de modèle.",
"custom_survey_name": "Tout créer moi-même", "custom_survey_name": "Tout créer moi-même",
"custom_survey_question_1_headline": "Que voudriez-vous savoir ?", "custom_survey_question_1_headline": "Que voudriez-vous savoir ?",
@@ -3024,9 +2986,6 @@
"preview_survey_question_2_choice_2_label": "Non, merci !", "preview_survey_question_2_choice_2_label": "Non, merci !",
"preview_survey_question_2_headline": "Souhaitez-vous être informé ?", "preview_survey_question_2_headline": "Souhaitez-vous être informé ?",
"preview_survey_question_2_subheader": "Ceci est un exemple de description.", "preview_survey_question_2_subheader": "Ceci est un exemple de description.",
"preview_survey_question_open_text_headline": "Autre chose que vous aimeriez partager?",
"preview_survey_question_open_text_placeholder": "Entrez votre réponse ici...",
"preview_survey_question_open_text_subheader": "Vos commentaires nous aident à nous améliorer.",
"preview_survey_welcome_card_headline": "Bienvenue !", "preview_survey_welcome_card_headline": "Bienvenue !",
"prioritize_features_description": "Identifiez les fonctionnalités dont vos utilisateurs ont le plus et le moins besoin.", "prioritize_features_description": "Identifiez les fonctionnalités dont vos utilisateurs ont le plus et le moins besoin.",
"prioritize_features_name": "Prioriser les fonctionnalités", "prioritize_features_name": "Prioriser les fonctionnalités",
@@ -3096,7 +3055,7 @@
"professional_development_survey_question_2_choice_5": "Recherche individuelle", "professional_development_survey_question_2_choice_5": "Recherche individuelle",
"professional_development_survey_question_2_choice_6": "Autre", "professional_development_survey_question_2_choice_6": "Autre",
"professional_development_survey_question_2_headline": "Quels types d'activités de développement professionnel pensez-vous être les plus précieuses pour votre croissance ?", "professional_development_survey_question_2_headline": "Quels types d'activités de développement professionnel pensez-vous être les plus précieuses pour votre croissance ?",
"professional_development_survey_question_2_subheader": "Veuillez sélectionner tout ce qui s'applique:", "professional_development_survey_question_2_subheader": "Sélectionnez tout ce qui s'applique",
"professional_development_survey_question_3_choice_1": "Oui", "professional_development_survey_question_3_choice_1": "Oui",
"professional_development_survey_question_3_choice_2": "Non", "professional_development_survey_question_3_choice_2": "Non",
"professional_development_survey_question_3_headline": "Avez-vous consacré du temps à votre développement professionnel dans le passé ?", "professional_development_survey_question_3_headline": "Avez-vous consacré du temps à votre développement professionnel dans le passé ?",
@@ -3275,18 +3234,5 @@
"usability_question_9_headline": "Je me suis senti confiant en utilisant le système.", "usability_question_9_headline": "Je me suis senti confiant en utilisant le système.",
"usability_rating_description": "Mesurez la convivialité perçue en demandant aux utilisateurs d'évaluer leur expérience avec votre produit via un sondage standardisé de 10 questions.", "usability_rating_description": "Mesurez la convivialité perçue en demandant aux utilisateurs d'évaluer leur expérience avec votre produit via un sondage standardisé de 10 questions.",
"usability_score_name": "Score d'Utilisabilité du Système (SUS)" "usability_score_name": "Score d'Utilisabilité du Système (SUS)"
},
"workflows": {
"coming_soon_description": "Merci d'avoir partagé votre idée de workflow avec nous! Nous concevons actuellement cette fonctionnalité et vos retours nous aideront à créer exactement ce dont vous avez besoin.",
"coming_soon_title": "Nous y sommes presque!",
"follow_up_label": "Y a-t-il autre chose que vous aimeriez ajouter?",
"follow_up_placeholder": "Quelles tâches spécifiques souhaitez-vous automatiser ? Y a-t-il des outils ou intégrations que vous aimeriez inclure ?",
"generate_button": "Générer le workflow",
"heading": "Quel workflow souhaitez-vous créer?",
"placeholder": "Décrivez le processus que vous souhaitez générer…",
"subheading": "Générez votre workflow en quelques secondes.",
"submit_button": "Ajouter des détails",
"thank_you_description": "Vos retours nous aident à construire la fonctionnalité Workflows dont vous avez réellement besoin. Nous vous tiendrons informé(e) de notre avancement.",
"thank_you_title": "Merci pour vos retours!"
} }
} }
+72 -126
View File
@@ -175,12 +175,9 @@
"copy": "Másolás", "copy": "Másolás",
"copy_code": "Kód másolása", "copy_code": "Kód másolása",
"copy_link": "Hivatkozás másolása", "copy_link": "Hivatkozás másolása",
"count_attributes": "{count, plural, one {{count} attribútum} other {{count} attribútum}}", "count_attributes": "{value, plural, one {{value} attribútum} other {{value} attribútum}}",
"count_contacts": "{count, plural, one {{count} partner} other {{count} partner}}", "count_contacts": "{value, plural, one {{value} partner} other {{value} partner}}",
"count_members": "{count, plural, one {{count} tag} other {{count} tag}}", "count_responses": "{value, plural, one {{value} válasz} other {{value} válasz}}",
"count_questions": "{count, plural, one {{count} kérdés} other {{count} kérdés}}",
"count_responses": "{count, plural, one {{count} válasz} other {{count} válasz}}",
"count_selections": "{count, plural, one {{count} kiválasztás} other {{count} kiválasztás}}",
"create_new_organization": "Új szervezet létrehozása", "create_new_organization": "Új szervezet létrehozása",
"create_segment": "Szakasz létrehozása", "create_segment": "Szakasz létrehozása",
"create_survey": "Kérdőív létrehozása", "create_survey": "Kérdőív létrehozása",
@@ -191,10 +188,9 @@
"customer_success": "Ügyfélsiker", "customer_success": "Ügyfélsiker",
"dark_overlay": "Sötét rávetítés", "dark_overlay": "Sötét rávetítés",
"date": "Dátum", "date": "Dátum",
"days": "nap", "days": "napok",
"default": "Alapértelmezett", "default": "Alapértelmezett",
"delete": "Törlés", "delete": "Törlés",
"delete_what": "{deleteWhat} törlése",
"description": "Leírás", "description": "Leírás",
"dev_env": "Fejlesztői környezet", "dev_env": "Fejlesztői környezet",
"development": "Fejlesztés", "development": "Fejlesztés",
@@ -210,8 +206,6 @@
"download": "Letöltés", "download": "Letöltés",
"draft": "Piszkozat", "draft": "Piszkozat",
"duplicate": "Kettőzés", "duplicate": "Kettőzés",
"duplicate_copy": "(másolat)",
"duplicate_copy_number": "({copyNumber}. másolat)",
"e_commerce": "E-kereskedelem", "e_commerce": "E-kereskedelem",
"edit": "Szerkesztés", "edit": "Szerkesztés",
"email": "E-mail", "email": "E-mail",
@@ -224,16 +218,13 @@
"error": "Hiba", "error": "Hiba",
"error_component_description": "Ez az erőforrás nem létezik, vagy nem rendelkezik a hozzáféréshez szükséges jogosultságokkal.", "error_component_description": "Ez az erőforrás nem létezik, vagy nem rendelkezik a hozzáféréshez szükséges jogosultságokkal.",
"error_component_title": "Hiba az erőforrások betöltésekor", "error_component_title": "Hiba az erőforrások betöltésekor",
"error_loading_data": "Hiba az adatok betöltésekor",
"error_rate_limit_description": "A kérések legnagyobb száma elérve. Próbálja meg később újra.", "error_rate_limit_description": "A kérések legnagyobb száma elérve. Próbálja meg később újra.",
"error_rate_limit_title": "A sebességkorlát elérve", "error_rate_limit_title": "A sebességkorlát elérve",
"expand_rows": "Sorok kinyitása", "expand_rows": "Sorok kinyitása",
"failed_to_copy_to_clipboard": "Nem sikerült másolni a vágólapra", "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_organizations": "Nem sikerült betölteni a szervezeteket",
"failed_to_load_workspaces": "Nem sikerült a munkaterületek betöltése", "failed_to_load_workspaces": "Nem sikerült a munkaterületek betöltése",
"filter": "Szűrő",
"finish": "Befejezés", "finish": "Befejezés",
"first_name": "Keresztnév",
"follow_these": "Ezek követése", "follow_these": "Ezek követése",
"formbricks_version": "Formbricks verziója", "formbricks_version": "Formbricks verziója",
"full_name": "Teljes név", "full_name": "Teljes név",
@@ -246,11 +237,10 @@
"hidden_field": "Rejtett mező", "hidden_field": "Rejtett mező",
"hidden_fields": "Rejtett mezők", "hidden_fields": "Rejtett mezők",
"hide_column": "Oszlop elrejtése", "hide_column": "Oszlop elrejtése",
"id": "Azonosító",
"image": "Kép", "image": "Kép",
"images": "Képek", "images": "Képek",
"import": "Importálás", "import": "Importálás",
"impressions": "Megtekintések", "impressions": "Benyomások",
"imprint": "Impresszum", "imprint": "Impresszum",
"in_progress": "Folyamatban", "in_progress": "Folyamatban",
"inactive_surveys": "Inaktív kérdőívek", "inactive_surveys": "Inaktív kérdőívek",
@@ -264,14 +254,13 @@
"key": "Kulcs", "key": "Kulcs",
"label": "Címke", "label": "Címke",
"language": "Nyelv", "language": "Nyelv",
"last_name": "Vezetéknév",
"learn_more": "Tudjon meg többet", "learn_more": "Tudjon meg többet",
"license_expired": "A licenc lejárt", "license_expired": "A licenc lejárt",
"light_overlay": "Világos rávetítés", "light_overlay": "Világos rávetítés",
"limits_reached": "Korlátok elérve", "limits_reached": "Korlátok elérve",
"link": "Hivatkozás", "link": "Összekapcsolás",
"link_survey": "Hivatkozás-kérdőív", "link_survey": "Kérdőív összekapcsolása",
"link_surveys": "Hivatkozás-kérdőívek", "link_surveys": "Kérdőívek összekapcsolása",
"load_more": "Továbbiak betöltése", "load_more": "Továbbiak betöltése",
"loading": "Betöltés", "loading": "Betöltés",
"logo": "Logó", "logo": "Logó",
@@ -279,6 +268,7 @@
"look_and_feel": "Megjelenés", "look_and_feel": "Megjelenés",
"manage": "Kezelés", "manage": "Kezelés",
"marketing": "Marketing", "marketing": "Marketing",
"member": "Tag",
"members": "Tagok", "members": "Tagok",
"members_and_teams": "Tagok és csapatok", "members_and_teams": "Tagok és csapatok",
"membership_not_found": "A tagság nem található", "membership_not_found": "A tagság nem található",
@@ -286,11 +276,10 @@
"mobile_overlay_app_works_best_on_desktop": "A Formbricks nagyobb képernyőn működik a legjobban. A kérdőívek kezeléséhez vagy összeállításához váltson másik eszközre.", "mobile_overlay_app_works_best_on_desktop": "A Formbricks nagyobb képernyőn működik a legjobban. A kérdőívek kezeléséhez vagy összeállításához váltson másik eszközre.",
"mobile_overlay_surveys_look_good": "Ne aggódjon a kérdőívei minden eszközön és képernyőméretnél remekül néznek ki!", "mobile_overlay_surveys_look_good": "Ne aggódjon a kérdőívei minden eszközön és képernyőméretnél remekül néznek ki!",
"mobile_overlay_title": "Hoppá, apró képernyő észlelve!", "mobile_overlay_title": "Hoppá, apró képernyő észlelve!",
"months": "hónap", "months": "hónapok",
"move_down": "Mozgatás le", "move_down": "Mozgatás le",
"move_up": "Mozgatás fel", "move_up": "Mozgatás fel",
"multiple_languages": "Több nyelv", "multiple_languages": "Több nyelv",
"my_product": "saját termék",
"name": "Név", "name": "Név",
"new": "Új", "new": "Új",
"new_version_available": "A Formbricks {version} megérkezett. Frissítsen most!", "new_version_available": "A Formbricks {version} megérkezett. Frissítsen most!",
@@ -324,7 +313,7 @@
"organization_settings": "Szervezet beállításai", "organization_settings": "Szervezet beállításai",
"organization_teams_not_found": "A szervezeti csapatok nem találhatók", "organization_teams_not_found": "A szervezeti csapatok nem találhatók",
"other": "Egyéb", "other": "Egyéb",
"others": "Mások", "others": "Egyebek",
"overlay_color": "Rávetítés színe", "overlay_color": "Rávetítés színe",
"overview": "Áttekintés", "overview": "Áttekintés",
"password": "Jelszó", "password": "Jelszó",
@@ -386,6 +375,8 @@
"select_teams": "Csapatok kiválasztása", "select_teams": "Csapatok kiválasztása",
"selected": "Kiválasztva", "selected": "Kiválasztva",
"selected_questions": "Kiválasztott kérdések", "selected_questions": "Kiválasztott kérdések",
"selection": "Kiválasztás",
"selections": "Kiválasztások",
"send_test_email": "Teszt e-mail küldése", "send_test_email": "Teszt e-mail küldése",
"session_not_found": "A munkamenet nem található", "session_not_found": "A munkamenet nem található",
"settings": "Beállítások", "settings": "Beállítások",
@@ -394,7 +385,6 @@
"show_response_count": "Válaszok számának megjelenítése", "show_response_count": "Válaszok számának megjelenítése",
"shown": "Megjelenítve", "shown": "Megjelenítve",
"size": "Méret", "size": "Méret",
"skip": "Kihagyás",
"skipped": "Kihagyva", "skipped": "Kihagyva",
"skips": "Kihagyja", "skips": "Kihagyja",
"some_files_failed_to_upload": "Néhány fájlt nem sikerült feltölteni", "some_files_failed_to_upload": "Néhány fájlt nem sikerült feltölteni",
@@ -438,7 +428,6 @@
"top_right": "Jobbra fent", "top_right": "Jobbra fent",
"try_again": "Próbálja újra", "try_again": "Próbálja újra",
"type": "Típus", "type": "Típus",
"unknown_survey": "Ismeretlen kérdőív",
"unlock_more_workspaces_with_a_higher_plan": "Több munkaterület feloldása egy magasabb csomaggal.", "unlock_more_workspaces_with_a_higher_plan": "Több munkaterület feloldása egy magasabb csomaggal.",
"update": "Frissítés", "update": "Frissítés",
"updated": "Frissítve", "updated": "Frissítve",
@@ -462,9 +451,8 @@
"website_and_app_connection": "Webhely és alkalmazáskapcsolódás", "website_and_app_connection": "Webhely és alkalmazáskapcsolódás",
"website_app_survey": "Webhely és alkalmazás-kérdőív", "website_app_survey": "Webhely és alkalmazás-kérdőív",
"website_survey": "Webhely kérdőív", "website_survey": "Webhely kérdőív",
"weeks": "hét", "weeks": "hetek",
"welcome_card": "Üdvözlő kártya", "welcome_card": "Üdvözlő kártya",
"workflows": "Munkafolyamatok",
"workspace_configuration": "Munkaterület beállítása", "workspace_configuration": "Munkaterület beállítása",
"workspace_created_successfully": "A munkaterület sikeresen létrehozva", "workspace_created_successfully": "A munkaterület sikeresen létrehozva",
"workspace_creation_description": "Kérdőívek munkaterületekre szervezése a jobb hozzáférés-vezérlés érdekében.", "workspace_creation_description": "Kérdőívek munkaterületekre szervezése a jobb hozzáférés-vezérlés érdekében.",
@@ -474,7 +462,7 @@
"workspace_not_found": "A munkaterület nem található", "workspace_not_found": "A munkaterület nem található",
"workspace_permission_not_found": "A munkaterület-jogosultság nem található", "workspace_permission_not_found": "A munkaterület-jogosultság nem található",
"workspaces": "Munkaterületek", "workspaces": "Munkaterületek",
"years": "év", "years": "évek",
"you": "Ön", "you": "Ön",
"you_are_downgraded_to_the_community_edition": "Visszaváltott a közösségi kiadásra.", "you_are_downgraded_to_the_community_edition": "Visszaváltott a közösségi kiadásra.",
"you_are_not_authorized_to_perform_this_action": "Nincs felhatalmazva ennek a műveletnek a végrehajtásához.", "you_are_not_authorized_to_perform_this_action": "Nincs felhatalmazva ennek a műveletnek a végrehajtásához.",
@@ -646,28 +634,28 @@
"attribute_updated_successfully": "Az attribútum sikeresen frissítve", "attribute_updated_successfully": "Az attribútum sikeresen frissítve",
"attribute_value": "Érték", "attribute_value": "Érték",
"attribute_value_placeholder": "Attribútum értéke", "attribute_value_placeholder": "Attribútum értéke",
"attributes_msg_attribute_limit_exceeded": "Nem sikerült létrehozni {count} új attribútumot, mivel túllépte volna a(z) {limit} attribútumosztályból álló legnagyobb korlátot. A meglévő attribútumok sikeresen frissítve lettek.", "attributes_msg_attribute_limit_exceeded": "Nem sikerült létrehozni {count} új attribútumot, mivel az meghaladná a maximális {limit} attribútumosztály-korlátot. A meglévő attribútumok sikeresen frissítve lettek.",
"attributes_msg_attribute_type_validation_error": "{error} (a(z) {key} attribútum a következő adattípussal rendelkezik: {dataType})", "attributes_msg_attribute_type_validation_error": "{error} (a(z) '{key}' attribútum adattípusa: {dataType})",
"attributes_msg_email_already_exists": "Az e-mail-cím már létezik ennél a környezetnél, és nem lett frissítve.", "attributes_msg_email_already_exists": "Az e-mail cím már létezik ebben a környezetben, és nem lett frissítve.",
"attributes_msg_email_or_userid_required": "Vagy e-mail-cím, vagy felhasználó-azonosító szükséges. A meglévő értékek megmaradtak.", "attributes_msg_email_or_userid_required": "E-mail cím vagy felhasználói azonosító megadása kötelező. A meglévő értékek megmaradtak.",
"attributes_msg_new_attribute_created": "Az új „{dataType}” típusú „{key} attribútum létrehozva", "attributes_msg_new_attribute_created": "Új '{key}' attribútum létrehozva '{dataType}' típussal",
"attributes_msg_userid_already_exists": "A felhasználó-azonosító már létezik ennél a környezetnél, és nem lett frissítve.", "attributes_msg_userid_already_exists": "A felhasználói azonosító már létezik ebben a környezetben, és nem lett frissítve.",
"contact_deleted_successfully": "A partner sikeresen törölve", "contact_deleted_successfully": "A partner sikeresen törölve",
"contact_not_found": "Nem található ilyen partner", "contact_not_found": "Nem található ilyen partner",
"contacts_table_refresh": "Partnerek frissítése", "contacts_table_refresh": "Partnerek frissítése",
"contacts_table_refresh_success": "A partnerek sikeresen frissítve", "contacts_table_refresh_success": "A partnerek sikeresen frissítve",
"create_attribute": "Attribútum létrehozása", "create_attribute": "Attribútum létrehozása",
"create_key": "Kulcs létrehozása",
"create_new_attribute": "Új attribútum létrehozása", "create_new_attribute": "Új attribútum létrehozása",
"create_new_attribute_description": "Új attribútum létrehozása szakaszolási célokhoz.", "create_new_attribute_description": "Új attribútum létrehozása szakaszolási célokhoz.",
"custom_attributes": "Egyéni attribútumok", "custom_attributes": "Egyéni attribútumok",
"data_type": "Adattípus", "data_type": "Adattípus",
"data_type_cannot_be_changed": "Az adattípust nem lehet megváltoztatni a létrehozás után", "data_type_cannot_be_changed": "Az adattípus létrehozás után nem módosítható",
"data_type_description": "Annak kiválasztása, hogy ezt az attribútumot hogyan kell tárolni és szűrni", "data_type_description": "Válaszd ki, hogyan legyen tárolva és szűrve ez az attribútum",
"date_value_required": "Dátumérték szükséges. Használja a törlés gombot az attribútum eltávolításához, ha nem szeretne dátumot beállítani.", "date_value_required": "Dátum érték megadása kötelező. Használd a törlés gombot az attribútum eltávolításához, ha nem szeretnél dátumot megadni.",
"delete_attribute_confirmation": "{value, plural, one {Ez törölni fogja a kiválasztott attribútumot. Az ehhez az attribútumhoz hozzárendelt összes partneradat el fog veszni.} other {Ez törölni fogja a kiválasztott attribútumokat. Az ezekhez az attribútumokhoz hozzárendelt összes partneradat el fog veszni.}}", "delete_attribute_confirmation": "{value, plural, one {Ez törölni fogja a kiválasztott attribútumot. Az ehhez az attribútumhoz hozzárendelt összes partneradat el fog veszni.} other {Ez törölni fogja a kiválasztott attribútumokat. Az ezekhez az attribútumokhoz hozzárendelt összes partneradat el fog veszni.}}",
"delete_contact_confirmation": "Ez törölni fogja az ehhez a partnerhez tartozó összes kérdőívválaszt és partnerattribútumot. A partner adatain alapuló bármilyen célzás és személyre szabás el fog veszni.", "delete_contact_confirmation": "Ez törölni fogja az ehhez a partnerhez tartozó összes kérdőívválaszt és partnerattribútumot. A partner adatain alapuló bármilyen célzás és személyre szabás el fog veszni.",
"delete_contact_confirmation_with_quotas": "{value, plural, one {Ez törölni fogja az ehhez a partnerhez tartozó összes kérdőívválaszt és partnerattribútumot. A partner adatain alapuló bármilyen célzás és személyre szabás el fog veszni. Ha ez a partner olyan válaszokkal rendelkezik, amelyek a kérdőívkvótákba beletartoznak, akkor a kvóta számlálója csökkentve lesz, de a kvóta korlátai változatlanok maradnak.} other {Ez törölni fogja az ezekhez a partnerekhez tartozó összes kérdőívválaszt és partnerattribútumot. A partnerek adatain alapuló bármilyen célzás és személyre szabás el fog veszni. Ha ezek a partnerek olyan válaszokkal rendelkeznek, amelyek a kérdőívkvótákba beletartoznak, akkor a kvóta számlálója csökkentve lesz, de a kvóta korlátai változatlanok maradnak.}}", "delete_contact_confirmation_with_quotas": "{value, plural, one {Ez törölni fogja az ehhez a partnerhez tartozó összes kérdőívválaszt és partnerattribútumot. A partner adatain alapuló bármilyen célzás és személyre szabás el fog veszni. Ha ez a partner olyan válaszokkal rendelkezik, amelyek a kérdőívkvótákba beletartoznak, akkor a kvóta számlálója csökkentve lesz, de a kvóta korlátai változatlanok maradnak.} other {Ez törölni fogja az ezekhez a partnerekhez tartozó összes kérdőívválaszt és partnerattribútumot. A partnerek adatain alapuló bármilyen célzás és személyre szabás el fog veszni. Ha ezek a partnerek olyan válaszokkal rendelkeznek, amelyek a kérdőívkvótákba beletartoznak, akkor a kvóta számlálója csökkentve lesz, de a kvóta korlátai változatlanok maradnak.}}",
"displays": "Megjelenítések",
"edit_attribute": "Attribútum szerkesztése", "edit_attribute": "Attribútum szerkesztése",
"edit_attribute_description": "Az attribútum címkéjének és leírásának frissítése.", "edit_attribute_description": "Az attribútum címkéjének és leírásának frissítése.",
"edit_attribute_values": "Attribútumok szerkesztése", "edit_attribute_values": "Attribútumok szerkesztése",
@@ -676,15 +664,14 @@
"edit_attributes_success": "A partner attribútumai sikeresen frissítve", "edit_attributes_success": "A partner attribútumai sikeresen frissítve",
"generate_personal_link": "Személyes hivatkozás előállítása", "generate_personal_link": "Személyes hivatkozás előállítása",
"generate_personal_link_description": "Válasszon egy közzétett kérdőívet, hogy személyre szabott hivatkozást állítson elő ehhez a partnerhez.", "generate_personal_link_description": "Válasszon egy közzétett kérdőívet, hogy személyre szabott hivatkozást állítson elő ehhez a partnerhez.",
"invalid_csv_column_names": "Érvénytelen CSV-oszlopnevek: {columns}. Az új attribútumokká váló oszlopnevek csak ékezet nélküli kisbetűket, számokat és aláhúzásjeleket tartalmazhatnak, valamint betűvel kell kezdődniük.", "invalid_csv_column_names": "Érvénytelen CSV oszlopnév(nevek): {columns}. Az új attribútumokká váló oszlopnevek csak kisbetűket, számokat és aláhúzásjeleket tartalmazhatnak, és betűvel kell kezdődniük.",
"invalid_date_format": "Érvénytelen dátumformátum. Használjon érvényes dátumot.", "invalid_date_format": "Érvénytelen dátumformátum. Kérlek, adj meg egy érvényes dátumot.",
"invalid_number_format": "Érvénytelen számformátum. Adjon meg érvényes számot.", "invalid_number_format": "Érvénytelen számformátum. Kérlek, adj meg egy érvényes számot.",
"no_activity_yet": "Még nincs tevékenység",
"no_published_link_surveys_available": "Nem érhetők el közzétett hivatkozás-kérdőívek. Először tegyen közzé egy hivatkozás-kérdőívet.", "no_published_link_surveys_available": "Nem érhetők el közzétett hivatkozás-kérdőívek. Először tegyen közzé egy hivatkozás-kérdőívet.",
"no_published_surveys": "Nincsenek közzétett kérdőívek", "no_published_surveys": "Nincsenek közzétett kérdőívek",
"no_responses_found": "Nem találhatók válaszok", "no_responses_found": "Nem találhatók válaszok",
"not_provided": "Nincs megadva", "not_provided": "Nincs megadva",
"number_value_required": "Számérték szükséges. Használja a törlés gombot az attribútum eltávolításához.", "number_value_required": "Szám érték megadása kötelező. Használd a törlés gombot az attribútum eltávolításához.",
"personal_link_generated": "A személyes hivatkozás sikeresen előállítva", "personal_link_generated": "A személyes hivatkozás sikeresen előállítva",
"personal_link_generated_but_clipboard_failed": "A személyes hivatkozás előállítva, de nem sikerült a vágólapra másolni: {url}", "personal_link_generated_but_clipboard_failed": "A személyes hivatkozás előállítva, de nem sikerült a vágólapra másolni: {url}",
"personal_survey_link": "Személyes kérdőív-hivatkozás", "personal_survey_link": "Személyes kérdőív-hivatkozás",
@@ -693,24 +680,22 @@
"search_contact": "Partner keresése", "search_contact": "Partner keresése",
"select_a_survey": "Kérdőív kiválasztása", "select_a_survey": "Kérdőív kiválasztása",
"select_attribute": "Attribútum kiválasztása", "select_attribute": "Attribútum kiválasztása",
"select_attribute_key": "Attribútum kulcsának kiválasztása", "select_attribute_key": "Attribútum kulcs kiválasztása",
"survey_viewed": "Kérdőív megtekintve", "system_attributes": "Rendszer attribútumok",
"survey_viewed_at": "Megtekintve ekkor:",
"system_attributes": "Rendszerattribútumok",
"unlock_contacts_description": "Partnerek kezelése és célzott kérdőívek kiküldése", "unlock_contacts_description": "Partnerek kezelése és célzott kérdőívek kiküldése",
"unlock_contacts_title": "Partnerek feloldása egy magasabb csomaggal", "unlock_contacts_title": "Partnerek feloldása egy magasabb csomaggal",
"upload_contacts_error_attribute_type_mismatch": "A(z) {key} attribútum {dataType}” típusként van megadva, de a CSV érvénytelen értékeket tartalmaz: {values}", "upload_contacts_error_attribute_type_mismatch": "A(z) \"{key}\" attribútum típusa \"{dataType}\", de a CSV érvénytelen értékeket tartalmaz: {values}",
"upload_contacts_error_duplicate_mappings": "Kettőzött leképezések találhatók a következő attribútumoknál: {attributes}", "upload_contacts_error_duplicate_mappings": "Duplikált leképezések találhatók a következő attribútumokhoz: {attributes}",
"upload_contacts_error_file_too_large": "A fájlméret túllépi a 800 KB-os legnagyobb méretet", "upload_contacts_error_file_too_large": "A fájl mérete meghaladja a maximális 800KB-os limitet",
"upload_contacts_error_generic": "Hiba történt a partnerek feltöltése során. Próbálja meg később újra.", "upload_contacts_error_generic": "Hiba történt a kapcsolatok feltöltése során. Kérjük, próbáld újra később.",
"upload_contacts_error_invalid_file_type": "Töltsön fel egy CSV-fájlt", "upload_contacts_error_invalid_file_type": "Kérjük, tölts fel egy CSV fájlt",
"upload_contacts_error_no_valid_contacts": "A feltöltött CSV-fájl nem tartalmaz egyetlen érvényes partnert sem. Nézze meg a példa CSV-fájlt a helyes formátumért.", "upload_contacts_error_no_valid_contacts": "A feltöltött CSV fájl nem tartalmaz érvényes kapcsolatokat, kérjük, nézd meg a minta CSV fájlt a helyes formátumhoz.",
"upload_contacts_modal_attribute_header": "Formbricks-attribútum", "upload_contacts_modal_attribute_header": "Formbricks attribútum",
"upload_contacts_modal_attributes_description": "A CSV-ben lévő oszlopok leképezése a Formbricksben lévő attribútumokra.", "upload_contacts_modal_attributes_description": "A CSV-ben lévő oszlopok leképezése a Formbricksben lévő attribútumokra.",
"upload_contacts_modal_attributes_new": "Új attribútum", "upload_contacts_modal_attributes_new": "Új attribútum",
"upload_contacts_modal_attributes_search_or_add": "Attribútum keresése vagy hozzáadása", "upload_contacts_modal_attributes_search_or_add": "Attribútum keresése vagy hozzáadása",
"upload_contacts_modal_attributes_title": "Attribútumok", "upload_contacts_modal_attributes_title": "Attribútumok",
"upload_contacts_modal_csv_column_header": "CSV-oszlop", "upload_contacts_modal_csv_column_header": "CSV oszlop",
"upload_contacts_modal_description": "CSV feltöltése a partnerek attribútumokkal együtt történő gyors importálásához", "upload_contacts_modal_description": "CSV feltöltése a partnerek attribútumokkal együtt történő gyors importálásához",
"upload_contacts_modal_download_example_csv": "Példa CSV letöltése", "upload_contacts_modal_download_example_csv": "Példa CSV letöltése",
"upload_contacts_modal_duplicates_description": "Hogyan kell kezelnünk, ha egy partner már szerepel a partnerek között?", "upload_contacts_modal_duplicates_description": "Hogyan kell kezelnünk, ha egy partner már szerepel a partnerek között?",
@@ -767,12 +752,7 @@
"link_google_sheet": "Google Táblázatok összekapcsolása", "link_google_sheet": "Google Táblázatok összekapcsolása",
"link_new_sheet": "Új táblázat összekapcsolása", "link_new_sheet": "Új táblázat összekapcsolása",
"no_integrations_yet": "A Google Táblázatok integrációi itt fognak megjelenni, amint hozzáadja azokat. ⏲️", "no_integrations_yet": "A Google Táblázatok integrációi itt fognak megjelenni, amint hozzáadja azokat. ⏲️",
"reconnect_button": "Újrakapcsolódás", "spreadsheet_url": "Táblázat URL-e"
"reconnect_button_description": "A Google Táblázatok kapcsolata lejárt. Kapcsolódjon újra a válaszok szinkronizálásának folytatásához. A meglévő táblázatok hivatkozásai és adatai megmaradnak.",
"reconnect_button_tooltip": "Csatlakoztassa újra az integrációt a hozzáférés frissítéséhez. A meglévő táblázatok hivatkozásai és adatai megmaradnak.",
"spreadsheet_permission_error": "Nincs jogosultsága hozzáférni ehhez a táblázathoz. Győződjön meg arról, hogy a táblázat meg van-e osztva a Google-fiókjával, és rendelkezik-e írási hozzáféréssel a táblázathoz.",
"spreadsheet_url": "Táblázat URL-e",
"token_expired_error": "A Google Táblázatok frissítési tokenje lejárt vagy visszavonásra került. Csatlakoztassa újra az integrációt."
}, },
"include_created_at": "Létrehozva felvétele", "include_created_at": "Létrehozva felvétele",
"include_hidden_fields": "Rejtett mezők felvétele", "include_hidden_fields": "Rejtett mezők felvétele",
@@ -901,35 +881,35 @@
"operator_ends_with": "ezzel végződik", "operator_ends_with": "ezzel végződik",
"operator_is_after": "ez után", "operator_is_after": "ez után",
"operator_is_before": "ez előtt", "operator_is_before": "ez előtt",
"operator_is_between": "ezek között", "operator_is_between": "között",
"operator_is_newer_than": "újabb mint", "operator_is_newer_than": "újabb mint",
"operator_is_not_set": "nincs beállítva", "operator_is_not_set": "nincs beállítva",
"operator_is_older_than": "régebbi mint", "operator_is_older_than": "régebbi mint",
"operator_is_same_day": "ugyanaz a nap", "operator_is_same_day": "ugyanazon a napon",
"operator_is_set": "be van állítva", "operator_is_set": "beállítva",
"operator_starts_with": "ezzel kezdődik", "operator_starts_with": "ezzel kezdődik",
"operator_title_contains": "Tartalmazza", "operator_title_contains": "Tartalmazza",
"operator_title_does_not_contain": "Nem tartalmazza", "operator_title_does_not_contain": "Nem tartalmazza",
"operator_title_ends_with": "Ezzel végződik", "operator_title_ends_with": "Ezzel végződik",
"operator_title_equals": "Egyenlő", "operator_title_equals": "Egyenlő",
"operator_title_greater_equal": "Nagyobb mint vagy egyenlő", "operator_title_greater_equal": "Nagyobb vagy egyenlő",
"operator_title_greater_than": "Nagyobb mint", "operator_title_greater_than": "Nagyobb mint",
"operator_title_is_after": "Ez után", "operator_title_is_after": "Ez után",
"operator_title_is_before": "Ez előtt", "operator_title_is_before": "Ez előtt",
"operator_title_is_between": "Ezek között", "operator_title_is_between": "Között",
"operator_title_is_newer_than": "Újabb mint", "operator_title_is_newer_than": "Újabb mint",
"operator_title_is_not_set": "Nincs beállítva", "operator_title_is_not_set": "Nincs beállítva",
"operator_title_is_older_than": "Régebbi mint", "operator_title_is_older_than": "Régebbi mint",
"operator_title_is_same_day": "Ugyanaz a nap", "operator_title_is_same_day": "Ugyanazon a napon",
"operator_title_is_set": "Beállítva", "operator_title_is_set": "Beállítva",
"operator_title_less_equal": "Kisebb mint vagy egyenlő", "operator_title_less_equal": "Kisebb vagy egyenlő",
"operator_title_less_than": "Kisebb mint", "operator_title_less_than": "Kisebb mint",
"operator_title_not_equals": "Nem egyenlő ezzel", "operator_title_not_equals": "Nem egyenlő",
"operator_title_starts_with": "Ezzel kezdődik", "operator_title_starts_with": "Ezzel kezdődik",
"operator_title_user_is_in": "Felhasználó ebben", "operator_title_user_is_in": "A felhasználó benne van",
"operator_title_user_is_not_in": "Felhasználó nem ebben", "operator_title_user_is_not_in": "A felhasználó nincs benne",
"operator_user_is_in": "Felhasználó ebben", "operator_user_is_in": "A felhasználó benne van",
"operator_user_is_not_in": "Felhasználó nem ebben", "operator_user_is_not_in": "A felhasználó nincs benne",
"person_and_attributes": "Személy és attribútumok", "person_and_attributes": "Személy és attribútumok",
"phone": "Telefon", "phone": "Telefon",
"please_remove_the_segment_from_these_surveys_in_order_to_delete_it": "Távolítsa el a szakaszt ezekből a kérdőívekből, hogy törölhesse azt.", "please_remove_the_segment_from_these_surveys_in_order_to_delete_it": "Távolítsa el a szakaszt ezekből a kérdőívekből, hogy törölhesse azt.",
@@ -953,7 +933,7 @@
"unlock_segments_title": "Szakaszok feloldása egy magasabb csomaggal", "unlock_segments_title": "Szakaszok feloldása egy magasabb csomaggal",
"user_targeting_is_currently_only_available_when": "A felhasználók megcélzása jelenleg csak akkor érhető el, ha", "user_targeting_is_currently_only_available_when": "A felhasználók megcélzása jelenleg csak akkor érhető el, ha",
"value_cannot_be_empty": "Az érték nem lehet üres.", "value_cannot_be_empty": "Az érték nem lehet üres.",
"value_must_be_a_number": "Az értéknek számnak kell lennie.", "value_must_be_a_number": "Az értékének számnak kell lennie.",
"value_must_be_positive": "Az értéknek pozitív számnak kell lennie.", "value_must_be_positive": "Az értéknek pozitív számnak kell lennie.",
"view_filters": "Szűrők megtekintése", "view_filters": "Szűrők megtekintése",
"where": "Ahol", "where": "Ahol",
@@ -1087,7 +1067,7 @@
"email_customization_preview_email_heading": "Helló {userName}", "email_customization_preview_email_heading": "Helló {userName}",
"email_customization_preview_email_text": "Ez egy e-mail előnézet, amely azt mutatja meg, hogy melyik logó fog megjelenni az e-mailekben.", "email_customization_preview_email_text": "Ez egy e-mail előnézet, amely azt mutatja meg, hogy melyik logó fog megjelenni az e-mailekben.",
"error_deleting_organization_please_try_again": "Hiba a szervezet törlésekor. Próbálja meg újra.", "error_deleting_organization_please_try_again": "Hiba a szervezet törlésekor. Próbálja meg újra.",
"from_your_organization": "{memberName} a szervezetéből", "from_your_organization": "a szervezetétől",
"invitation_sent_once_more": "A meghívó még egyszer elküldve.", "invitation_sent_once_more": "A meghívó még egyszer elküldve.",
"invite_deleted_successfully": "A meghívó sikeresen törölve", "invite_deleted_successfully": "A meghívó sikeresen törölve",
"invite_expires_on": "A meghívó lejár ekkor: {date}", "invite_expires_on": "A meghívó lejár ekkor: {date}",
@@ -1252,7 +1232,6 @@
"add_fallback_placeholder": "Helykitöltő hozzáadása annak megjelenítéshez, hogy nincs visszahívandó érték.", "add_fallback_placeholder": "Helykitöltő hozzáadása annak megjelenítéshez, hogy nincs visszahívandó érték.",
"add_hidden_field_id": "Rejtett mezőazonosító hozzáadása", "add_hidden_field_id": "Rejtett mezőazonosító hozzáadása",
"add_highlight_border": "Kiemelési szegély hozzáadása", "add_highlight_border": "Kiemelési szegély hozzáadása",
"add_highlight_border_description": "Csak terméken belüli kérdőívekre vonatkozik.",
"add_logic": "Logika hozzáadása", "add_logic": "Logika hozzáadása",
"add_none_of_the_above": "„A fentiek közül egyik sem” hozzáadása", "add_none_of_the_above": "„A fentiek közül egyik sem” hozzáadása",
"add_option": "Lehetőség hozzáadása", "add_option": "Lehetőség hozzáadása",
@@ -1269,14 +1248,12 @@
"adjust_survey_closed_message": "A „Kérdőív lezárva” üzenet módosítása", "adjust_survey_closed_message": "A „Kérdőív lezárva” üzenet módosítása",
"adjust_survey_closed_message_description": "Annak az üzenetnek a megváltoztatása, amelyet a látogatók akkor látnak, amikor a kérdőív lezárul.", "adjust_survey_closed_message_description": "Annak az üzenetnek a megváltoztatása, amelyet a látogatók akkor látnak, amikor a kérdőív lezárul.",
"adjust_the_theme_in_the": "A téma beállítása ebben:", "adjust_the_theme_in_the": "A téma beállítása ebben:",
"all_are_true": "az összes igaz",
"all_other_answers_will_continue_to": "Az összes többi válasz továbbra is", "all_other_answers_will_continue_to": "Az összes többi válasz továbbra is",
"allow_multi_select": "Több választás engedélyezése", "allow_multi_select": "Több választás engedélyezése",
"allow_multiple_files": "Több fájl engedélyezése", "allow_multiple_files": "Több fájl engedélyezése",
"allow_users_to_select_more_than_one_image": "Lehetővé tétel a felhasználóknak, hogy egynél több képet válasszanak ki", "allow_users_to_select_more_than_one_image": "Lehetővé tétel a felhasználóknak, hogy egynél több képet válasszanak ki",
"and_launch_surveys_in_your_website_or_app": "és kérdőívek indítása a webhelyén vagy az alkalmazásában.", "and_launch_surveys_in_your_website_or_app": "és kérdőívek indítása a webhelyén vagy az alkalmazásában.",
"animation": "Animáció", "animation": "Animáció",
"any_is_true": "bármelyik igaz",
"app_survey_description": "Egy kérdőív beágyazása a webalkalmazásába vagy webhelyére a válaszok gyűjtéséhez.", "app_survey_description": "Egy kérdőív beágyazása a webalkalmazásába vagy webhelyére a válaszok gyűjtéséhez.",
"assign": "= hozzárendelése", "assign": "= hozzárendelése",
"audience": "Közönség", "audience": "Közönség",
@@ -1318,7 +1295,7 @@
"casual": "Alkalmi", "casual": "Alkalmi",
"caution_edit_duplicate": "Kettőzés és szerkesztés", "caution_edit_duplicate": "Kettőzés és szerkesztés",
"caution_edit_published_survey": "Szerkeszt egy közzétett kérdőívet?", "caution_edit_published_survey": "Szerkeszt egy közzétett kérdőívet?",
"caution_explanation_intro": "Megértjük, hogy esetleg még változtatásokat szeretne végrehajtani. Ez történik, ha megteszi:", "caution_explanation_intro": "Megértjük, hogy esetleg még változtatásokat szeretne végrehajtani. Ez történik, ha megteszi: ",
"caution_explanation_new_responses_separated": "A változtatás előtti válaszok nem vagy csak részben kerülhetnek be a kérdőív összegzésébe.", "caution_explanation_new_responses_separated": "A változtatás előtti válaszok nem vagy csak részben kerülhetnek be a kérdőív összegzésébe.",
"caution_explanation_only_new_responses_in_summary": "Az összes adat, beleértve a korábbi válaszokat is, továbbra is elérhetők maradnak letölthető formában a kérdőív összegző oldalán.", "caution_explanation_only_new_responses_in_summary": "Az összes adat, beleértve a korábbi válaszokat is, továbbra is elérhetők maradnak letölthető formában a kérdőív összegző oldalán.",
"caution_explanation_responses_are_safe": "A régebbi és az újabb válaszok összekeverednek, ami félrevezető adatösszegzésekhez vezethet.", "caution_explanation_responses_are_safe": "A régebbi és az újabb válaszok összekeverednek, ami félrevezető adatösszegzésekhez vezethet.",
@@ -1453,6 +1430,7 @@
"follow_ups_modal_updated_successfull_toast": "A követés frissítve, és akkor lesz elmentve, ha elmenti a kérdőívet.", "follow_ups_modal_updated_successfull_toast": "A követés frissítve, és akkor lesz elmentve, ha elmenti a kérdőívet.",
"follow_ups_new": "Új követés", "follow_ups_new": "Új követés",
"follow_ups_upgrade_button_text": "Magasabb csomagra váltás a követések engedélyezéséhez", "follow_ups_upgrade_button_text": "Magasabb csomagra váltás a követések engedélyezéséhez",
"form_styling": "Űrlap stílusának beállítása",
"formbricks_sdk_is_not_connected": "A Formbricks SDK nincs csatlakoztatva", "formbricks_sdk_is_not_connected": "A Formbricks SDK nincs csatlakoztatva",
"four_points": "4 pont", "four_points": "4 pont",
"heading": "Címsor", "heading": "Címsor",
@@ -1542,7 +1520,7 @@
"option_idx": "{choiceIndex}. lehetőség", "option_idx": "{choiceIndex}. lehetőség",
"option_used_in_logic_error": "Ez a lehetőség használatban van a(z) {questionIndex}. kérdés logikájában. Először távolítsa el a logikából.", "option_used_in_logic_error": "Ez a lehetőség használatban van a(z) {questionIndex}. kérdés logikájában. Először távolítsa el a logikából.",
"optional": "Választható", "optional": "Választható",
"options": "Beállítások*", "options": "Beállítások",
"options_used_in_logic_bulk_error": "A következő lehetőségek használatban vannak a logikában: {questionIndexes}. Először távolítsa el azokat a logikából.", "options_used_in_logic_bulk_error": "A következő lehetőségek használatban vannak a logikában: {questionIndexes}. Először távolítsa el azokat a logikából.",
"override_theme_with_individual_styles_for_this_survey": "A téma felülírása egyéni stílusokkal ennél a kérdőívnél.", "override_theme_with_individual_styles_for_this_survey": "A téma felülírása egyéni stílusokkal ennél a kérdőívnél.",
"overwrite_global_waiting_time": "Egyéni várakozási időszak beállítása", "overwrite_global_waiting_time": "Egyéni várakozási időszak beállítása",
@@ -1567,7 +1545,6 @@
"question_deleted": "Kérdés törölve.", "question_deleted": "Kérdés törölve.",
"question_duplicated": "Kérdés megkettőzve.", "question_duplicated": "Kérdés megkettőzve.",
"question_id_updated": "Kérdésazonosító frissítve", "question_id_updated": "Kérdésazonosító frissítve",
"question_number": "{number}. kérdés",
"question_used_in_logic_warning_text": "Ezen blokkból származó elemek egy logikai szabályban vannak használva, biztosan törölni szeretné?", "question_used_in_logic_warning_text": "Ezen blokkból származó elemek egy logikai szabályban vannak használva, biztosan törölni szeretné?",
"question_used_in_logic_warning_title": "Logikai következetlenség", "question_used_in_logic_warning_title": "Logikai következetlenség",
"question_used_in_quota": "Ez a kérdés használatban van a(z) „{quotaName}” kvótában", "question_used_in_quota": "Ez a kérdés használatban van a(z) „{quotaName}” kvótában",
@@ -1626,7 +1603,7 @@
"response_limits_redirections_and_more": "Válaszkorlátok, átirányítások és egyebek.", "response_limits_redirections_and_more": "Válaszkorlátok, átirányítások és egyebek.",
"response_options": "Válasz beállításai", "response_options": "Válasz beállításai",
"roundness": "Kerekesség", "roundness": "Kerekesség",
"roundness_description": "Annak vezérlése, hogy a sarkok mennyire legyenek lekerekítve.", "roundness_description": "Annak vezérlése, hogy a kártya sarkai mennyire legyenek lekerekítve.",
"row_used_in_logic_error": "Ez a sor használatban van a(z) {questionIndex}. kérdés logikájában. Először távolítsa el a logikából.", "row_used_in_logic_error": "Ez a sor használatban van a(z) {questionIndex}. kérdés logikájában. Először távolítsa el a logikából.",
"rows": "Sorok", "rows": "Sorok",
"save_and_close": "Mentés és bezárás", "save_and_close": "Mentés és bezárás",
@@ -1672,7 +1649,6 @@
"survey_completed_subheading": "Ez a szabad és nyílt forráskódú kérdőív le lett zárva", "survey_completed_subheading": "Ez a szabad és nyílt forráskódú kérdőív le lett zárva",
"survey_display_settings": "Kérdőív megjelenítésének beállításai", "survey_display_settings": "Kérdőív megjelenítésének beállításai",
"survey_placement": "Kérdőív elhelyezése", "survey_placement": "Kérdőív elhelyezése",
"survey_styling": "Kérdőív stílusának beállítása",
"survey_trigger": "Kérdőív aktiválója", "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 👉", "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ó", "target_block_not_found": "A célblokk nem található",
@@ -1761,9 +1737,9 @@
"waiting_time_across_surveys": "Várakozási időszak (kérdőívek között)", "waiting_time_across_surveys": "Várakozási időszak (kérdőívek között)",
"waiting_time_across_surveys_description": "A kérdőívekbe való belefáradás megakadályozásához válassza ki, hogy ez a kérdőív hogyan lép kölcsönhatásba a munkaterület-szintű várakozási időszakkal.", "waiting_time_across_surveys_description": "A kérdőívekbe való belefáradás megakadályozásához válassza ki, hogy ez a kérdőív hogyan lép kölcsönhatásba a munkaterület-szintű várakozási időszakkal.",
"welcome_message": "Üdvözlő üzenet", "welcome_message": "Üdvözlő üzenet",
"when": "Amikor",
"without_a_filter_all_of_your_users_can_be_surveyed": "Szűrő nélkül az összes felhasználója megkérdezhető.", "without_a_filter_all_of_your_users_can_be_surveyed": "Szűrő nélkül az összes felhasználója megkérdezhető.",
"you_have_not_created_a_segment_yet": "Még nem hozott létre szakaszt", "you_have_not_created_a_segment_yet": "Még nem hozott létre szakaszt",
"you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "Be kell állítania kettő vagy több nyelvet a munkaterületen a fordításokkal való munkához.",
"your_description_here_recall_information_with": "Ide jön a leírás. Információk visszahívása a @ karakterrel.", "your_description_here_recall_information_with": "Ide jön a leírás. Információk visszahívása a @ karakterrel.",
"your_question_here_recall_information_with": "Ide jön a kérdés. Információk visszahívása a @ karakterrel.", "your_question_here_recall_information_with": "Ide jön a kérdés. Információk visszahívása a @ karakterrel.",
"your_web_app": "Saját webalkalmazás", "your_web_app": "Saját webalkalmazás",
@@ -1818,7 +1794,7 @@
"this_response_is_in_progress": "Ez a válasz folyamatban van.", "this_response_is_in_progress": "Ez a válasz folyamatban van.",
"zip_post_code": "Irányítószám" "zip_post_code": "Irányítószám"
}, },
"search_by_survey_name": "Keresés kérdőívnév alapján", "search_by_survey_name": "Keresés kérőívnév alapján",
"share": { "share": {
"anonymous_links": { "anonymous_links": {
"custom_single_use_id_description": "Ha nem titkosítja az egyszer használatos azonosítókat, akkor a „suid=…” bármilyen értéke működik egy válasznál.", "custom_single_use_id_description": "Ha nem titkosítja az egyszer használatos azonosítókat, akkor a „suid=…” bármilyen értéke működik egy válasznál.",
@@ -1944,7 +1920,7 @@
} }
}, },
"summary": { "summary": {
"added_filter_for_responses_where_answer_to_question": "Szűrő hozzáadva azokhoz a válaszokhoz, ahol a(z) {questionIdx}. kérdésre adott válasz {filterComboBoxValue} {filterValue}", "added_filter_for_responses_where_answer_to_question": "Szűrő hozzáadva azokhoz a válaszokhoz, ahol a(z) {questionIdx}. kérdésre adott válasz {filterComboBoxValue} - {filterValue} ",
"added_filter_for_responses_where_answer_to_question_is_skipped": "Szűrő hozzáadva azokhoz a válaszokhoz, ahol a(z) {questionIdx}. kérdésre adott válasz kihagyva", "added_filter_for_responses_where_answer_to_question_is_skipped": "Szűrő hozzáadva azokhoz a válaszokhoz, ahol a(z) {questionIdx}. kérdésre adott válasz kihagyva",
"aggregated": "Összesített", "aggregated": "Összesített",
"all_responses_csv": "Összes válasz (CSV)", "all_responses_csv": "Összes válasz (CSV)",
@@ -1970,8 +1946,7 @@
"filtered_responses_csv": "Szűrt válaszok (CSV)", "filtered_responses_csv": "Szűrt válaszok (CSV)",
"filtered_responses_excel": "Szűrt válaszok (Excel)", "filtered_responses_excel": "Szűrt válaszok (Excel)",
"generating_qr_code": "QR-kód előállítása", "generating_qr_code": "QR-kód előállítása",
"impressions": "Megtekintések", "impressions": "Benyomások",
"impressions_identified_only": "Csak azonosított partnerektől származó megtekintések megjelenítése",
"impressions_tooltip": "A kérdőív megtekintési alkalmainak száma.", "impressions_tooltip": "A kérdőív megtekintési alkalmainak száma.",
"in_app": { "in_app": {
"connection_description": "A kérdőív a webhelye azon felhasználóinak lesz megjelenítve, akik megfelelnek az alább felsorolt feltételeknek", "connection_description": "A kérdőív a webhelye azon felhasználóinak lesz megjelenítve, akik megfelelnek az alább felsorolt feltételeknek",
@@ -2014,7 +1989,6 @@
"last_quarter": "Elmúlt negyedév", "last_quarter": "Elmúlt negyedév",
"last_year": "Elmúlt év", "last_year": "Elmúlt év",
"limit": "Korlát", "limit": "Korlát",
"no_identified_impressions": "Nincsenek azonosított partnerektől származó megtekintések",
"no_responses_found": "Nem találhatók válaszok", "no_responses_found": "Nem találhatók válaszok",
"other_values_found": "Más értékek találhatók", "other_values_found": "Más értékek találhatók",
"overall": "Összesen", "overall": "Összesen",
@@ -2037,7 +2011,6 @@
"starts": "Elkezdések", "starts": "Elkezdések",
"starts_tooltip": "A kérdőív elkezdési alkalmainak száma.", "starts_tooltip": "A kérdőív elkezdési alkalmainak száma.",
"survey_reset_successfully": "A kérdőív sikeresen visszaállítva. {responseCount} válasz és {displayCount} megjelenítés lett törölve.", "survey_reset_successfully": "A kérdőív sikeresen visszaállítva. {responseCount} válasz és {displayCount} megjelenítés lett törölve.",
"survey_results": "{surveyName} eredményei",
"this_month": "Ez a hónap", "this_month": "Ez a hónap",
"this_quarter": "Ez a negyedév", "this_quarter": "Ez a negyedév",
"this_year": "Ez az év", "this_year": "Ez az év",
@@ -2180,12 +2153,12 @@
"advanced_styling_field_headline_size_description": "Átméretezi a címsor szövegét.", "advanced_styling_field_headline_size_description": "Átméretezi a címsor szövegét.",
"advanced_styling_field_headline_weight": "Címsor betűvastagsága", "advanced_styling_field_headline_weight": "Címsor betűvastagsága",
"advanced_styling_field_headline_weight_description": "Vékonyabbá vagy vastagabbá teszi a címsor szövegét.", "advanced_styling_field_headline_weight_description": "Vékonyabbá vagy vastagabbá teszi a címsor szövegét.",
"advanced_styling_field_height": "Legkisebb magasság", "advanced_styling_field_height": "Minimális magasság",
"advanced_styling_field_indicator_bg": "Jelző háttere", "advanced_styling_field_indicator_bg": "Jelző háttere",
"advanced_styling_field_indicator_bg_description": "Kiszínezi a sáv kitöltött részét.", "advanced_styling_field_indicator_bg_description": "Kiszínezi a sáv kitöltött részét.",
"advanced_styling_field_input_border_radius_description": "Lekerekíti a beviteli mező sarkait.", "advanced_styling_field_input_border_radius_description": "Lekerekíti a beviteli mező sarkait.",
"advanced_styling_field_input_font_size_description": "Átméretezi a beviteli mezőkbe beírt szöveget.", "advanced_styling_field_input_font_size_description": "Átméretezi a beviteli mezőkbe beírt szöveget.",
"advanced_styling_field_input_height_description": "A beviteli mező legkisebb magasságát vezérli.", "advanced_styling_field_input_height_description": "A beviteli mező minimális magasságát szabályozza.",
"advanced_styling_field_input_padding_x_description": "Térközt ad hozzá balra és jobbra.", "advanced_styling_field_input_padding_x_description": "Térközt ad hozzá balra és jobbra.",
"advanced_styling_field_input_padding_y_description": "Térközt ad hozzá fent és lent.", "advanced_styling_field_input_padding_y_description": "Térközt ad hozzá fent és lent.",
"advanced_styling_field_input_placeholder_opacity_description": "Elhalványítja a helykitöltő súgószöveget.", "advanced_styling_field_input_placeholder_opacity_description": "Elhalványítja a helykitöltő súgószöveget.",
@@ -2194,8 +2167,6 @@
"advanced_styling_field_input_text_description": "Kiszínezi a beviteli mezőkbe beírt szöveget.", "advanced_styling_field_input_text_description": "Kiszínezi a beviteli mezőkbe beírt szöveget.",
"advanced_styling_field_option_bg": "Háttér", "advanced_styling_field_option_bg": "Háttér",
"advanced_styling_field_option_bg_description": "Kitölti a választási lehetőség elemeit.", "advanced_styling_field_option_bg_description": "Kitölti a választási lehetőség elemeit.",
"advanced_styling_field_option_border": "Szegély színe",
"advanced_styling_field_option_border_description": "Körberajzolja a rádiógomb és a jelölőnégyzet lehetőségeit.",
"advanced_styling_field_option_border_radius_description": "Lekerekíti a választási lehetőség sarkait.", "advanced_styling_field_option_border_radius_description": "Lekerekíti a választási lehetőség sarkait.",
"advanced_styling_field_option_font_size_description": "Átméretezi a választási lehetőség címkéjének szövegét.", "advanced_styling_field_option_font_size_description": "Átméretezi a választási lehetőség címkéjének szövegét.",
"advanced_styling_field_option_label": "Címke színe", "advanced_styling_field_option_label": "Címke színe",
@@ -2233,7 +2204,7 @@
"formbricks_branding_settings_description": "Nagyra értékeljük a támogatását, de megértjük, ha kikapcsolja.", "formbricks_branding_settings_description": "Nagyra értékeljük a támogatását, de megértjük, ha kikapcsolja.",
"formbricks_branding_shown": "A Formbricks márkajel megjelenik.", "formbricks_branding_shown": "A Formbricks márkajel megjelenik.",
"generate_theme_btn": "Előállítás", "generate_theme_btn": "Előállítás",
"generate_theme_confirmation": "Szeretne hozzáillő színtémát előállítani a márkajel színei alapján? Ez felülírja a jelenlegi színbeállításokat.", "generate_theme_confirmation": "Szeretne hozzáillő színtémát létrehozni a márkajel színei alapján? Ez felülírja a jelenlegi színbeállításokat.",
"generate_theme_header": "Előállítja a színtémát?", "generate_theme_header": "Előállítja a színtémát?",
"logo_removed_successfully": "A logó sikeresen eltávolítva", "logo_removed_successfully": "A logó sikeresen eltávolítva",
"logo_settings_description": "Vállalati logo feltöltése a kérdőívek és hivatkozások előnézeteinek márkaépítéséhez.", "logo_settings_description": "Vállalati logo feltöltése a kérdőívek és hivatkozások előnézeteinek márkaépítéséhez.",
@@ -2250,7 +2221,7 @@
"show_powered_by_formbricks": "Az „A gépházban: Formbricks” aláírás megjelenítése", "show_powered_by_formbricks": "Az „A gépházban: Formbricks” aláírás megjelenítése",
"styling_updated_successfully": "A stílus sikeresen frissítve", "styling_updated_successfully": "A stílus sikeresen frissítve",
"suggest_colors": "Színek ajánlása", "suggest_colors": "Színek ajánlása",
"suggested_colors_applied_please_save": "Az ajánlott színek sikeresen előállítva. Nyomja meg a Mentés gombot a változtatások mentéséhez.", "suggested_colors_applied_please_save": "A javasolt színek sikeresen generálva. Nyomd meg a \"Mentés\" gombot a változtatások véglegesítéséhez.",
"theme": "Téma", "theme": "Téma",
"theme_settings_description": "Stílustéma létrehozása az összes kérdőívhez. Egyéni stílust engedélyezhet minden egyes kérdőívhez." "theme_settings_description": "Stílustéma létrehozása az összes kérdőívhez. Egyéni stílust engedélyezhet minden egyes kérdőívhez."
}, },
@@ -2312,7 +2283,7 @@
"mode": { "mode": {
"formbricks_cx": "Formbricks CX", "formbricks_cx": "Formbricks CX",
"formbricks_cx_description": "Kérdőívek és jelentések annak megértéséhez, hogy mire van szükségük az ügyfeleknek.", "formbricks_cx_description": "Kérdőívek és jelentések annak megértéséhez, hogy mire van szükségük az ügyfeleknek.",
"formbricks_surveys": "Formbricks kérdőívek", "formbricks_surveys": "Formbricks kérőívek",
"formbricks_surveys_description": "Többcélú kérdőíves platform web-, alkalmazás- és e-mail-kérdőívekhez.", "formbricks_surveys_description": "Többcélú kérdőíves platform web-, alkalmazás- és e-mail-kérdőívekhez.",
"what_are_you_here_for": "Miért van itt?" "what_are_you_here_for": "Miért van itt?"
}, },
@@ -2405,16 +2376,6 @@
"alignment_and_engagement_survey_question_4_headline": "Hogyan tudná javítani a vállalat a jövőképe és stratégiája összehangolását?", "alignment_and_engagement_survey_question_4_headline": "Hogyan tudná javítani a vállalat a jövőképe és stratégiája összehangolását?",
"alignment_and_engagement_survey_question_4_placeholder": "Írja be ide a válaszát…", "alignment_and_engagement_survey_question_4_placeholder": "Írja be ide a válaszát…",
"back": "Vissza", "back": "Vissza",
"block_1": "1. blokk",
"block_10": "10. blokk",
"block_2": "2. blokk",
"block_3": "3. blokk",
"block_4": "4. blokk",
"block_5": "5. blokk",
"block_6": "6. blokk",
"block_7": "7. blokk",
"block_8": "8. blokk",
"block_9": "9. blokk",
"book_interview": "Interjú foglalása", "book_interview": "Interjú foglalása",
"build_product_roadmap_description": "A felhasználók által leginkább igényelt EGY dolog azonosítása és összeállítása.", "build_product_roadmap_description": "A felhasználók által leginkább igényelt EGY dolog azonosítása és összeállítása.",
"build_product_roadmap_name": "Termékútiterv összeállítása", "build_product_roadmap_name": "Termékútiterv összeállítása",
@@ -2478,7 +2439,7 @@
"career_development_survey_question_6_choice_2": "Igazgató", "career_development_survey_question_6_choice_2": "Igazgató",
"career_development_survey_question_6_choice_3": "Vezető igazgató", "career_development_survey_question_6_choice_3": "Vezető igazgató",
"career_development_survey_question_6_choice_4": "Alelnök", "career_development_survey_question_6_choice_4": "Alelnök",
"career_development_survey_question_6_choice_5": "Ügyvezető", "career_development_survey_question_6_choice_5": "Igazgató",
"career_development_survey_question_6_choice_6": "Egyéb", "career_development_survey_question_6_choice_6": "Egyéb",
"career_development_survey_question_6_headline": "Az alábbiak közül melyik írja le legjobban a jelenlegi munkája szintjét?", "career_development_survey_question_6_headline": "Az alábbiak közül melyik írja le legjobban a jelenlegi munkája szintjét?",
"career_development_survey_question_6_subheader": "Válassza ki a következő lehetőségek egyikét:", "career_development_survey_question_6_subheader": "Válassza ki a következő lehetőségek egyikét:",
@@ -2622,6 +2583,7 @@
"csat_survey_question_3_headline": "Jaj, bocsánat! Tehetünk valamit, amivel javíthatnánk az élményén?", "csat_survey_question_3_headline": "Jaj, bocsánat! Tehetünk valamit, amivel javíthatnánk az élményén?",
"csat_survey_question_3_placeholder": "Írja be ide a válaszát…", "csat_survey_question_3_placeholder": "Írja be ide a válaszát…",
"cta_description": "Információk megjelenítése és a felhasználók felkérése egy bizonyos művelet elvégzésére", "cta_description": "Információk megjelenítése és a felhasználók felkérése egy bizonyos művelet elvégzésére",
"custom_survey_block_1_name": "1. blokk",
"custom_survey_description": "Kérdőív létrehozása sablon nélkül.", "custom_survey_description": "Kérdőív létrehozása sablon nélkül.",
"custom_survey_name": "Kezdés a semmiből", "custom_survey_name": "Kezdés a semmiből",
"custom_survey_question_1_headline": "Mit szeretne tudni?", "custom_survey_question_1_headline": "Mit szeretne tudni?",
@@ -2990,7 +2952,7 @@
"onboarding_segmentation": "Beléptetés szakaszolása", "onboarding_segmentation": "Beléptetés szakaszolása",
"onboarding_segmentation_description": "További információk azzal kapcsolatban, hogy kik regisztráltak a termékére és miért.", "onboarding_segmentation_description": "További információk azzal kapcsolatban, hogy kik regisztráltak a termékére és miért.",
"onboarding_segmentation_question_1_choice_1": "Alapító", "onboarding_segmentation_question_1_choice_1": "Alapító",
"onboarding_segmentation_question_1_choice_2": "Ügyvezető", "onboarding_segmentation_question_1_choice_2": "Igazgató",
"onboarding_segmentation_question_1_choice_3": "Termékmenedzser", "onboarding_segmentation_question_1_choice_3": "Termékmenedzser",
"onboarding_segmentation_question_1_choice_4": "Terméktulajdonos", "onboarding_segmentation_question_1_choice_4": "Terméktulajdonos",
"onboarding_segmentation_question_1_choice_5": "Szoftvermérnök", "onboarding_segmentation_question_1_choice_5": "Szoftvermérnök",
@@ -3024,9 +2986,6 @@
"preview_survey_question_2_choice_2_label": "Nem, köszönöm!", "preview_survey_question_2_choice_2_label": "Nem, köszönöm!",
"preview_survey_question_2_headline": "Szeretne naprakész maradni?", "preview_survey_question_2_headline": "Szeretne naprakész maradni?",
"preview_survey_question_2_subheader": "Ez egy példa a leírásra.", "preview_survey_question_2_subheader": "Ez egy példa a leírásra.",
"preview_survey_question_open_text_headline": "Bármi egyéb, amit meg szeretne osztani?",
"preview_survey_question_open_text_placeholder": "Írja be ide a válaszát…",
"preview_survey_question_open_text_subheader": "A visszajelzése segít nekünk fejlődni.",
"preview_survey_welcome_card_headline": "Üdvözöljük!", "preview_survey_welcome_card_headline": "Üdvözöljük!",
"prioritize_features_description": "A felhasználóknak leginkább és legkevésbé szükséges funkciók azonosítása.", "prioritize_features_description": "A felhasználóknak leginkább és legkevésbé szükséges funkciók azonosítása.",
"prioritize_features_name": "Funkciók rangsorolása", "prioritize_features_name": "Funkciók rangsorolása",
@@ -3061,7 +3020,7 @@
"product_market_fit_superhuman_question_2_headline": "Mennyire lenne csalódott, ha többé nem használhatná a(z) $[projectName] projektet?", "product_market_fit_superhuman_question_2_headline": "Mennyire lenne csalódott, ha többé nem használhatná a(z) $[projectName] projektet?",
"product_market_fit_superhuman_question_2_subheader": "Válassza ki a következő lehetőségek egyikét:", "product_market_fit_superhuman_question_2_subheader": "Válassza ki a következő lehetőségek egyikét:",
"product_market_fit_superhuman_question_3_choice_1": "Alapító", "product_market_fit_superhuman_question_3_choice_1": "Alapító",
"product_market_fit_superhuman_question_3_choice_2": "Ügyvezető", "product_market_fit_superhuman_question_3_choice_2": "Igazgató",
"product_market_fit_superhuman_question_3_choice_3": "Termékmenedzser", "product_market_fit_superhuman_question_3_choice_3": "Termékmenedzser",
"product_market_fit_superhuman_question_3_choice_4": "Terméktulajdonos", "product_market_fit_superhuman_question_3_choice_4": "Terméktulajdonos",
"product_market_fit_superhuman_question_3_choice_5": "Szoftvermérnök", "product_market_fit_superhuman_question_3_choice_5": "Szoftvermérnök",
@@ -3096,7 +3055,7 @@
"professional_development_survey_question_2_choice_5": "Egyéni kutatás", "professional_development_survey_question_2_choice_5": "Egyéni kutatás",
"professional_development_survey_question_2_choice_6": "Egyéb", "professional_development_survey_question_2_choice_6": "Egyéb",
"professional_development_survey_question_2_headline": "Mit gondol, milyen típusú szakmai fejlődési tevékenységek lennének a legértékesebbek a fejlődéséhez?", "professional_development_survey_question_2_headline": "Mit gondol, milyen típusú szakmai fejlődési tevékenységek lennének a legértékesebbek a fejlődéséhez?",
"professional_development_survey_question_2_subheader": "Válassza ki az összes megfelelőt:", "professional_development_survey_question_2_subheader": "Válassza ki az összes megfelelőt",
"professional_development_survey_question_3_choice_1": "Igen", "professional_development_survey_question_3_choice_1": "Igen",
"professional_development_survey_question_3_choice_2": "Nem", "professional_development_survey_question_3_choice_2": "Nem",
"professional_development_survey_question_3_headline": "Fordított időt a szakmai fejlődésére a múltban?", "professional_development_survey_question_3_headline": "Fordított időt a szakmai fejlődésére a múltban?",
@@ -3275,18 +3234,5 @@
"usability_question_9_headline": "Magabiztosnak éreztem magam a rendszer használata során.", "usability_question_9_headline": "Magabiztosnak éreztem magam a rendszer használata során.",
"usability_rating_description": "Az érzékelt használhatóság mérése arra kérve a felhasználókat, hogy értékeljék a termékkel kapcsolatos tapasztalataikat egy szabványosított, 10 kérdésből álló kérdőív használatával.", "usability_rating_description": "Az érzékelt használhatóság mérése arra kérve a felhasználókat, hogy értékeljék a termékkel kapcsolatos tapasztalataikat egy szabványosított, 10 kérdésből álló kérdőív használatával.",
"usability_score_name": "Rendszer-használhatósági pontszám (SUS)" "usability_score_name": "Rendszer-használhatósági pontszám (SUS)"
},
"workflows": {
"coming_soon_description": "Köszönjük, hogy megosztotta velünk a munkafolyamatra vonatkozó ötletét! Jelenleg a funkció kialakításán dolgozunk, és a visszajelzése segít nekünk abban, hogy pontosan azt alkossuk meg, amire szüksége van.",
"coming_soon_title": "Már majdnem kész vagyunk!",
"follow_up_label": "Van még bármi egyéb, amit hozzá szeretne fűzni?",
"follow_up_placeholder": "Milyen konkrét feladatokat szeretne automatizálni? Vannak olyan eszközök vagy integrációk, amelyeket szívesen látna a rendszerben?",
"generate_button": "Munkafolyamat előállítása",
"heading": "Milyen munkafolyamatot szeretne létrehozni?",
"placeholder": "Mutassa be az előállítani kívánt munkafolyamatot…",
"subheading": "Munkafolyamat előállítása másodpercek alatt.",
"submit_button": "Részletek hozzáadása",
"thank_you_description": "A visszajelzése segít nekünk abban, hogy olyan Munkafolyamatok funkciót alakítsunk ki, amelyre valóban szüksége van. Folyamatosan tájékoztatni fogjuk Önt a fejlesztés előrehaladásáról.",
"thank_you_title": "Köszönjük a visszajelzését!"
} }
} }
+31 -85
View File
@@ -175,12 +175,9 @@
"copy": "コピー", "copy": "コピー",
"copy_code": "コードをコピー", "copy_code": "コードをコピー",
"copy_link": "リンクをコピー", "copy_link": "リンクをコピー",
"count_attributes": "{count, plural, other {{count} 件の属性}}", "count_attributes": "{value, plural, other {{value}個の属性}}",
"count_contacts": "{count, plural, other {{count} 件の連絡先}}", "count_contacts": "{count, plural, other {# 件の連絡先}}",
"count_members": "{count, plural, other {{count} 名のメンバー}}", "count_responses": "{count, plural, other {# 件の回答}}",
"count_questions": "{count, plural, other {# 件の質問}}",
"count_responses": "{count, plural, other {{count} 件の回答}}",
"count_selections": "{count, plural, other {{count} 件の選択}}",
"create_new_organization": "新しい組織を作成", "create_new_organization": "新しい組織を作成",
"create_segment": "セグメントを作成", "create_segment": "セグメントを作成",
"create_survey": "フォームを作成", "create_survey": "フォームを作成",
@@ -194,7 +191,6 @@
"days": "日", "days": "日",
"default": "デフォルト", "default": "デフォルト",
"delete": "削除", "delete": "削除",
"delete_what": "{deleteWhat}を削除",
"description": "説明", "description": "説明",
"dev_env": "開発環境", "dev_env": "開発環境",
"development": "開発", "development": "開発",
@@ -210,8 +206,6 @@
"download": "ダウンロード", "download": "ダウンロード",
"draft": "下書き", "draft": "下書き",
"duplicate": "複製", "duplicate": "複製",
"duplicate_copy": "(コピー)",
"duplicate_copy_number": "(コピー {copyNumber})",
"e_commerce": "Eコマース", "e_commerce": "Eコマース",
"edit": "編集", "edit": "編集",
"email": "メールアドレス", "email": "メールアドレス",
@@ -224,16 +218,13 @@
"error": "エラー", "error": "エラー",
"error_component_description": "この リソース は 存在 しない か、アクセス する ための 必要な 権限 が ありません。", "error_component_description": "この リソース は 存在 しない か、アクセス する ための 必要な 権限 が ありません。",
"error_component_title": "リソース の 読み込み エラー", "error_component_title": "リソース の 読み込み エラー",
"error_loading_data": "データの読み込みエラー",
"error_rate_limit_description": "リクエストの最大数に達しました。後でもう一度試してください。", "error_rate_limit_description": "リクエストの最大数に達しました。後でもう一度試してください。",
"error_rate_limit_title": "レート制限を超えました", "error_rate_limit_title": "レート制限を超えました",
"expand_rows": "行を展開", "expand_rows": "行を展開",
"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": "ワークスペースの読み込みに失敗しました",
"filter": "フィルター",
"finish": "完了", "finish": "完了",
"first_name": "名",
"follow_these": "こちらの手順に従って", "follow_these": "こちらの手順に従って",
"formbricks_version": "Formbricksバージョン", "formbricks_version": "Formbricksバージョン",
"full_name": "氏名", "full_name": "氏名",
@@ -246,7 +237,6 @@
"hidden_field": "非表示フィールド", "hidden_field": "非表示フィールド",
"hidden_fields": "非表示フィールド", "hidden_fields": "非表示フィールド",
"hide_column": "列を非表示", "hide_column": "列を非表示",
"id": "ID",
"image": "画像", "image": "画像",
"images": "画像", "images": "画像",
"import": "インポート", "import": "インポート",
@@ -264,7 +254,6 @@
"key": "キー", "key": "キー",
"label": "ラベル", "label": "ラベル",
"language": "言語", "language": "言語",
"last_name": "姓",
"learn_more": "詳細を見る", "learn_more": "詳細を見る",
"license_expired": "License Expired", "license_expired": "License Expired",
"light_overlay": "明るいオーバーレイ", "light_overlay": "明るいオーバーレイ",
@@ -279,6 +268,7 @@
"look_and_feel": "デザイン", "look_and_feel": "デザイン",
"manage": "管理", "manage": "管理",
"marketing": "マーケティング", "marketing": "マーケティング",
"member": "メンバー",
"members": "メンバー", "members": "メンバー",
"members_and_teams": "メンバー&チーム", "members_and_teams": "メンバー&チーム",
"membership_not_found": "メンバーシップが見つかりません", "membership_not_found": "メンバーシップが見つかりません",
@@ -290,7 +280,6 @@
"move_down": "下に移動", "move_down": "下に移動",
"move_up": "上に移動", "move_up": "上に移動",
"multiple_languages": "多言語", "multiple_languages": "多言語",
"my_product": "マイプロダクト",
"name": "名前", "name": "名前",
"new": "新規", "new": "新規",
"new_version_available": "Formbricks {version} が利用可能です。今すぐアップグレード!", "new_version_available": "Formbricks {version} が利用可能です。今すぐアップグレード!",
@@ -386,6 +375,8 @@
"select_teams": "チームを選択", "select_teams": "チームを選択",
"selected": "選択済み", "selected": "選択済み",
"selected_questions": "選択した質問", "selected_questions": "選択した質問",
"selection": "選択",
"selections": "選択",
"send_test_email": "テストメールを送信", "send_test_email": "テストメールを送信",
"session_not_found": "セッションが見つかりません", "session_not_found": "セッションが見つかりません",
"settings": "設定", "settings": "設定",
@@ -394,7 +385,6 @@
"show_response_count": "回答数を表示", "show_response_count": "回答数を表示",
"shown": "表示済み", "shown": "表示済み",
"size": "サイズ", "size": "サイズ",
"skip": "スキップ",
"skipped": "スキップ済み", "skipped": "スキップ済み",
"skips": "スキップ数", "skips": "スキップ数",
"some_files_failed_to_upload": "一部のファイルのアップロードに失敗しました", "some_files_failed_to_upload": "一部のファイルのアップロードに失敗しました",
@@ -438,7 +428,6 @@
"top_right": "右上", "top_right": "右上",
"try_again": "もう一度お試しください", "try_again": "もう一度お試しください",
"type": "種類", "type": "種類",
"unknown_survey": "不明なフォーム",
"unlock_more_workspaces_with_a_higher_plan": "上位プランでより多くのワークスペースを利用できます。", "unlock_more_workspaces_with_a_higher_plan": "上位プランでより多くのワークスペースを利用できます。",
"update": "更新", "update": "更新",
"updated": "更新済み", "updated": "更新済み",
@@ -464,7 +453,6 @@
"website_survey": "ウェブサイトフォーム", "website_survey": "ウェブサイトフォーム",
"weeks": "週間", "weeks": "週間",
"welcome_card": "ウェルカムカード", "welcome_card": "ウェルカムカード",
"workflows": "ワークフロー",
"workspace_configuration": "ワークスペース設定", "workspace_configuration": "ワークスペース設定",
"workspace_created_successfully": "ワークスペースが正常に作成されました", "workspace_created_successfully": "ワークスペースが正常に作成されました",
"workspace_creation_description": "アクセス制御を改善するために、フォームをワークスペースで整理します。", "workspace_creation_description": "アクセス制御を改善するために、フォームをワークスペースで整理します。",
@@ -647,27 +635,27 @@
"attribute_value": "値", "attribute_value": "値",
"attribute_value_placeholder": "属性値", "attribute_value_placeholder": "属性値",
"attributes_msg_attribute_limit_exceeded": "最大制限の{limit}個の属性クラスを超えるため、{count}個の新しい属性を作成できませんでした。既存の属性は正常に更新されました。", "attributes_msg_attribute_limit_exceeded": "最大制限の{limit}個の属性クラスを超えるため、{count}個の新しい属性を作成できませんでした。既存の属性は正常に更新されました。",
"attributes_msg_attribute_type_validation_error": "{error}(属性{key}のデータ型{dataType}", "attributes_msg_attribute_type_validation_error": "{error}(属性'{key}'のデータ型: {dataType}",
"attributes_msg_email_already_exists": "このメールアドレスはこの環境に既に存在するため、更新されませんでした。", "attributes_msg_email_already_exists": "このメールアドレスはこの環境に既に存在するため、更新されませんでした。",
"attributes_msg_email_or_userid_required": "メールアドレスまたはユーザーIDのいずれかが必要です。既存の値は保持されました。", "attributes_msg_email_or_userid_required": "メールアドレスまたはユーザーIDのいずれかが必要です。既存の値は保持されました。",
"attributes_msg_new_attribute_created": "新しい属性{key}”を型“{dataType}で作成しました", "attributes_msg_new_attribute_created": "新しい属性'{key}'をタイプ'{dataType}'で作成しました",
"attributes_msg_userid_already_exists": "この環境にはすでにユーザーIDが存在するため、更新されませんでした。", "attributes_msg_userid_already_exists": "このユーザーIDはこの環境に既に存在するため、更新されませんでした。",
"contact_deleted_successfully": "連絡先を正常に削除しました", "contact_deleted_successfully": "連絡先を正常に削除しました",
"contact_not_found": "そのような連絡先は見つかりません", "contact_not_found": "そのような連絡先は見つかりません",
"contacts_table_refresh": "連絡先を更新", "contacts_table_refresh": "連絡先を更新",
"contacts_table_refresh_success": "連絡先を正常に更新しました", "contacts_table_refresh_success": "連絡先を正常に更新しました",
"create_attribute": "属性を作成", "create_attribute": "属性を作成",
"create_key": "キーを作成",
"create_new_attribute": "新しい属性を作成", "create_new_attribute": "新しい属性を作成",
"create_new_attribute_description": "セグメンテーション用の新しい属性を作成します。", "create_new_attribute_description": "セグメンテーション用の新しい属性を作成します。",
"custom_attributes": "カスタム属性", "custom_attributes": "カスタム属性",
"data_type": "データ型", "data_type": "データ型",
"data_type_cannot_be_changed": "データ型は作成後に変更できません", "data_type_cannot_be_changed": "データ型は作成後に変更できません",
"data_type_description": "この属性の保存方法とフィルタリング方法を選択してください", "data_type_description": "この属性の保存方法とフィルタリング方法を選択してください",
"date_value_required": "日付の値が必要です。日付を設定しない場合は、削除ボタンを使用してこの属性を削除してください。", "date_value_required": "日付の値が必要です。日付を設定したくない場合は、削除ボタンを使用してこの属性を削除してください。",
"delete_attribute_confirmation": "{value, plural, one {選択した属性を削除します。この属性に関連付けられたすべてのコンタクトデータは失われます。} other {選択した属性を削除します。これらの属性に関連付けられたすべてのコンタクトデータは失われます。}}", "delete_attribute_confirmation": "{value, plural, one {選択した属性を削除します。この属性に関連付けられたすべてのコンタクトデータは失われます。} other {選択した属性を削除します。これらの属性に関連付けられたすべてのコンタクトデータは失われます。}}",
"delete_contact_confirmation": "これにより、この連絡先に関連付けられているすべてのフォーム回答と連絡先属性が削除されます。この連絡先のデータに基づいたターゲティングとパーソナライゼーションはすべて失われます。", "delete_contact_confirmation": "これにより、この連絡先に関連付けられているすべてのフォーム回答と連絡先属性が削除されます。この連絡先のデータに基づいたターゲティングとパーソナライゼーションはすべて失われます。",
"delete_contact_confirmation_with_quotas": "{value, plural, one {これにより この連絡先に関連するすべてのアンケート応答と連絡先属性が削除されます。この連絡先のデータに基づくターゲティングとパーソナライゼーションが失われます。この連絡先がアンケートの割当量を考慮した回答を持っている場合、割当量カウントは減少しますが、割当量の制限は変更されません。} other {これにより これらの連絡先に関連するすべてのアンケート応答と連絡先属性が削除されます。これらの連絡先のデータに基づくターゲティングとパーソナライゼーションが失われます。これらの連絡先がアンケートの割当量を考慮した回答を持っている場合、割当量カウントは減少しますが、割当量の制限は変更されません。}}", "delete_contact_confirmation_with_quotas": "{value, plural, one {これにより この連絡先に関連するすべてのアンケート応答と連絡先属性が削除されます。この連絡先のデータに基づくターゲティングとパーソナライゼーションが失われます。この連絡先がアンケートの割当量を考慮した回答を持っている場合、割当量カウントは減少しますが、割当量の制限は変更されません。} other {これにより これらの連絡先に関連するすべてのアンケート応答と連絡先属性が削除されます。これらの連絡先のデータに基づくターゲティングとパーソナライゼーションが失われます。これらの連絡先がアンケートの割当量を考慮した回答を持っている場合、割当量カウントは減少しますが、割当量の制限は変更されません。}}",
"displays": "表示回数",
"edit_attribute": "属性を編集", "edit_attribute": "属性を編集",
"edit_attribute_description": "この属性のラベルと説明を更新します。", "edit_attribute_description": "この属性のラベルと説明を更新します。",
"edit_attribute_values": "属性を編集", "edit_attribute_values": "属性を編集",
@@ -679,7 +667,6 @@
"invalid_csv_column_names": "無効なCSV列名: {columns}。新しい属性となる列名は、小文字、数字、アンダースコアのみを含み、文字で始まる必要があります。", "invalid_csv_column_names": "無効なCSV列名: {columns}。新しい属性となる列名は、小文字、数字、アンダースコアのみを含み、文字で始まる必要があります。",
"invalid_date_format": "無効な日付形式です。有効な日付を使用してください。", "invalid_date_format": "無効な日付形式です。有効な日付を使用してください。",
"invalid_number_format": "無効な数値形式です。有効な数値を入力してください。", "invalid_number_format": "無効な数値形式です。有効な数値を入力してください。",
"no_activity_yet": "まだアクティビティがありません",
"no_published_link_surveys_available": "公開されたリンクフォームはありません。まずリンクフォームを公開してください。", "no_published_link_surveys_available": "公開されたリンクフォームはありません。まずリンクフォームを公開してください。",
"no_published_surveys": "公開されたフォームはありません", "no_published_surveys": "公開されたフォームはありません",
"no_responses_found": "回答が見つかりません", "no_responses_found": "回答が見つかりません",
@@ -694,12 +681,10 @@
"select_a_survey": "フォームを選択", "select_a_survey": "フォームを選択",
"select_attribute": "属性を選択", "select_attribute": "属性を選択",
"select_attribute_key": "属性キーを選択", "select_attribute_key": "属性キーを選択",
"survey_viewed": "フォームを閲覧",
"survey_viewed_at": "閲覧日時",
"system_attributes": "システム属性", "system_attributes": "システム属性",
"unlock_contacts_description": "連絡先を管理し、特定のフォームを送信します", "unlock_contacts_description": "連絡先を管理し、特定のフォームを送信します",
"unlock_contacts_title": "上位プランで連絡先をアンロック", "unlock_contacts_title": "上位プランで連絡先をアンロック",
"upload_contacts_error_attribute_type_mismatch": "属性{key}”は“{dataType}”型ですが、CSVに無効な値が含まれています:{values}", "upload_contacts_error_attribute_type_mismatch": "属性{key}」は「{dataType}」として型付けされていますが、CSVに無効な値が含まれています:{values}",
"upload_contacts_error_duplicate_mappings": "次の属性に重複したマッピングが見つかりました:{attributes}", "upload_contacts_error_duplicate_mappings": "次の属性に重複したマッピングが見つかりました:{attributes}",
"upload_contacts_error_file_too_large": "ファイルサイズが最大制限の800KBを超えています", "upload_contacts_error_file_too_large": "ファイルサイズが最大制限の800KBを超えています",
"upload_contacts_error_generic": "連絡先のアップロード中にエラーが発生しました。後でもう一度お試しください。", "upload_contacts_error_generic": "連絡先のアップロード中にエラーが発生しました。後でもう一度お試しください。",
@@ -767,12 +752,7 @@
"link_google_sheet": "スプレッドシートをリンク", "link_google_sheet": "スプレッドシートをリンク",
"link_new_sheet": "新しいシートをリンク", "link_new_sheet": "新しいシートをリンク",
"no_integrations_yet": "Google スプレッドシート連携は、追加するとここに表示されます。⏲️", "no_integrations_yet": "Google スプレッドシート連携は、追加するとここに表示されます。⏲️",
"reconnect_button": "再接続", "spreadsheet_url": "スプレッドシートURL"
"reconnect_button_description": "Google Sheetsの接続が期限切れになりました。回答の同期を続けるには再接続してください。既存のスプレッドシートリンクとデータは保持されます。",
"reconnect_button_tooltip": "統合を再接続してアクセスを更新します。既存のスプレッドシートリンクとデータは保持されます。",
"spreadsheet_permission_error": "このスプレッドシートにアクセスする権限がありません。スプレッドシートがGoogleアカウントと共有されており、書き込みアクセス権があることを確認してください。",
"spreadsheet_url": "スプレッドシートURL",
"token_expired_error": "Google Sheetsのリフレッシュトークンが期限切れになったか、取り消されました。統合を再接続してください。"
}, },
"include_created_at": "作成日時を含める", "include_created_at": "作成日時を含める",
"include_hidden_fields": "非表示フィールドを含める", "include_hidden_fields": "非表示フィールドを含める",
@@ -1059,7 +1039,7 @@
"recheck_license_invalid": "ライセンスキーが無効です。ENTERPRISE_LICENSE_KEYを確認してください。", "recheck_license_invalid": "ライセンスキーが無効です。ENTERPRISE_LICENSE_KEYを確認してください。",
"recheck_license_success": "ライセンスの確認に成功しました", "recheck_license_success": "ライセンスの確認に成功しました",
"recheck_license_unreachable": "ライセンスサーバーに接続できません。後ほど再度お試しください。", "recheck_license_unreachable": "ライセンスサーバーに接続できません。後ほど再度お試しください。",
"rechecking": "再確認中", "rechecking": "再確認中...",
"request_30_day_trial_license": "30日間トライアルライセンスをリクエスト", "request_30_day_trial_license": "30日間トライアルライセンスをリクエスト",
"saml_sso": "SAML SSO", "saml_sso": "SAML SSO",
"service_level_agreement": "サービスレベル契約", "service_level_agreement": "サービスレベル契約",
@@ -1087,7 +1067,7 @@
"email_customization_preview_email_heading": "こんにちは、{userName}さん", "email_customization_preview_email_heading": "こんにちは、{userName}さん",
"email_customization_preview_email_text": "これは、メールに表示されるロゴを確認するためのプレビューメールです。", "email_customization_preview_email_text": "これは、メールに表示されるロゴを確認するためのプレビューメールです。",
"error_deleting_organization_please_try_again": "組織の削除中にエラーが発生しました。もう一度お試しください。", "error_deleting_organization_please_try_again": "組織の削除中にエラーが発生しました。もう一度お試しください。",
"from_your_organization": "組織から{memberName}を削除", "from_your_organization": "あなたの組織から",
"invitation_sent_once_more": "招待状を再度送信しました。", "invitation_sent_once_more": "招待状を再度送信しました。",
"invite_deleted_successfully": "招待を正常に削除しました", "invite_deleted_successfully": "招待を正常に削除しました",
"invite_expires_on": "招待は{date}に期限切れ", "invite_expires_on": "招待は{date}に期限切れ",
@@ -1136,7 +1116,7 @@
"notification_settings_updated": "通知設定を更新しました", "notification_settings_updated": "通知設定を更新しました",
"set_up_an_alert_to_get_an_email_on_new_responses": "新しい回答が届いたときにメールを受け取るようにアラートを設定します", "set_up_an_alert_to_get_an_email_on_new_responses": "新しい回答が届いたときにメールを受け取るようにアラートを設定します",
"use_the_integration": "連携を使用する", "use_the_integration": "連携を使用する",
"want_to_loop_in_organization_mates": "組織のメンバーを含めますか", "want_to_loop_in_organization_mates": "組織の仲間も巻き込みたいですか",
"you_will_not_be_auto_subscribed_to_this_organizations_surveys_anymore": "この組織のフォームには、自動で購読されなくなります!", "you_will_not_be_auto_subscribed_to_this_organizations_surveys_anymore": "この組織のフォームには、自動で購読されなくなります!",
"you_will_not_receive_any_more_emails_for_responses_on_this_survey": "このフォームの回答に関するメールは、今後一切受け取れなくなります!" "you_will_not_receive_any_more_emails_for_responses_on_this_survey": "このフォームの回答に関するメールは、今後一切受け取れなくなります!"
}, },
@@ -1252,7 +1232,6 @@
"add_fallback_placeholder": "質問がスキップされた場合に表示するプレースホルダーを追加:", "add_fallback_placeholder": "質問がスキップされた場合に表示するプレースホルダーを追加:",
"add_hidden_field_id": "非表示フィールドIDを追加", "add_hidden_field_id": "非表示フィールドIDを追加",
"add_highlight_border": "ハイライトボーダーを追加", "add_highlight_border": "ハイライトボーダーを追加",
"add_highlight_border_description": "プロダクト内サーベイにのみ適用されます。",
"add_logic": "ロジックを追加", "add_logic": "ロジックを追加",
"add_none_of_the_above": "\"いずれも該当しません\" を追加", "add_none_of_the_above": "\"いずれも該当しません\" を追加",
"add_option": "オプションを追加", "add_option": "オプションを追加",
@@ -1269,14 +1248,12 @@
"adjust_survey_closed_message": "「フォームはクローズしました」メッセージを調整", "adjust_survey_closed_message": "「フォームはクローズしました」メッセージを調整",
"adjust_survey_closed_message_description": "フォームがクローズしたときに訪問者が見るメッセージを変更します。", "adjust_survey_closed_message_description": "フォームがクローズしたときに訪問者が見るメッセージを変更します。",
"adjust_the_theme_in_the": "テーマを", "adjust_the_theme_in_the": "テーマを",
"all_are_true": "すべてが真である",
"all_other_answers_will_continue_to": "他のすべての回答は引き続き", "all_other_answers_will_continue_to": "他のすべての回答は引き続き",
"allow_multi_select": "複数選択を許可", "allow_multi_select": "複数選択を許可",
"allow_multiple_files": "複数のファイルを許可", "allow_multiple_files": "複数のファイルを許可",
"allow_users_to_select_more_than_one_image": "ユーザーが複数の画像を選択できるようにする", "allow_users_to_select_more_than_one_image": "ユーザーが複数の画像を選択できるようにする",
"and_launch_surveys_in_your_website_or_app": "ウェブサイトやアプリでフォームを公開できます。", "and_launch_surveys_in_your_website_or_app": "ウェブサイトやアプリでフォームを公開できます。",
"animation": "アニメーション", "animation": "アニメーション",
"any_is_true": "いずれかが真",
"app_survey_description": "回答を収集するために、ウェブアプリまたはウェブサイトにフォームを埋め込みます。", "app_survey_description": "回答を収集するために、ウェブアプリまたはウェブサイトにフォームを埋め込みます。",
"assign": "割り当て =", "assign": "割り当て =",
"audience": "オーディエンス", "audience": "オーディエンス",
@@ -1318,7 +1295,7 @@
"casual": "カジュアル", "casual": "カジュアル",
"caution_edit_duplicate": "複製して編集", "caution_edit_duplicate": "複製して編集",
"caution_edit_published_survey": "公開済みのフォームを編集しますか?", "caution_edit_published_survey": "公開済みのフォームを編集しますか?",
"caution_explanation_intro": "変更を加えたい場合あることは理解しています。変更を行うと次のようになります:", "caution_explanation_intro": "変更を加えたい場合あることは理解しています。以下に変更した場合に起こることです: ",
"caution_explanation_new_responses_separated": "変更前の回答は、フォームの概要に含まれないか、部分的にしか含まれない場合があります。", "caution_explanation_new_responses_separated": "変更前の回答は、フォームの概要に含まれないか、部分的にしか含まれない場合があります。",
"caution_explanation_only_new_responses_in_summary": "過去の回答を含むすべてのデータは、フォームの概要ページでダウンロード可能です。", "caution_explanation_only_new_responses_in_summary": "過去の回答を含むすべてのデータは、フォームの概要ページでダウンロード可能です。",
"caution_explanation_responses_are_safe": "古い回答と新しい回答が混ざり、データの概要が誤解を招く可能性があります。", "caution_explanation_responses_are_safe": "古い回答と新しい回答が混ざり、データの概要が誤解を招く可能性があります。",
@@ -1453,6 +1430,7 @@
"follow_ups_modal_updated_successfull_toast": "フォローアップ が 更新され、 アンケートを 保存すると保存されます。", "follow_ups_modal_updated_successfull_toast": "フォローアップ が 更新され、 アンケートを 保存すると保存されます。",
"follow_ups_new": "新しいフォローアップ", "follow_ups_new": "新しいフォローアップ",
"follow_ups_upgrade_button_text": "フォローアップを有効にするためにアップグレード", "follow_ups_upgrade_button_text": "フォローアップを有効にするためにアップグレード",
"form_styling": "フォームのスタイル",
"formbricks_sdk_is_not_connected": "Formbricks SDKが接続されていません", "formbricks_sdk_is_not_connected": "Formbricks SDKが接続されていません",
"four_points": "4点", "four_points": "4点",
"heading": "見出し", "heading": "見出し",
@@ -1542,7 +1520,7 @@
"option_idx": "オプション {choiceIndex}", "option_idx": "オプション {choiceIndex}",
"option_used_in_logic_error": "このオプションは質問 {questionIndex} のロジックで使用されています。まず、ロジックから削除してください。", "option_used_in_logic_error": "このオプションは質問 {questionIndex} のロジックで使用されています。まず、ロジックから削除してください。",
"optional": "オプション", "optional": "オプション",
"options": "オプション*", "options": "オプション",
"options_used_in_logic_bulk_error": "以下のオプションはロジックで使用されています:{questionIndexes}。まず、ロジックから削除してください。", "options_used_in_logic_bulk_error": "以下のオプションはロジックで使用されています:{questionIndexes}。まず、ロジックから削除してください。",
"override_theme_with_individual_styles_for_this_survey": "このフォームの個別のスタイルでテーマを上書きします。", "override_theme_with_individual_styles_for_this_survey": "このフォームの個別のスタイルでテーマを上書きします。",
"overwrite_global_waiting_time": "カスタムクールダウン期間を設定", "overwrite_global_waiting_time": "カスタムクールダウン期間を設定",
@@ -1567,7 +1545,6 @@
"question_deleted": "質問を削除しました。", "question_deleted": "質問を削除しました。",
"question_duplicated": "質問を複製しました。", "question_duplicated": "質問を複製しました。",
"question_id_updated": "質問IDを更新しました", "question_id_updated": "質問IDを更新しました",
"question_number": "質問 {number}",
"question_used_in_logic_warning_text": "このブロックの要素はロジックルールで使用されていますが、本当に削除しますか?", "question_used_in_logic_warning_text": "このブロックの要素はロジックルールで使用されていますが、本当に削除しますか?",
"question_used_in_logic_warning_title": "ロジックの不整合", "question_used_in_logic_warning_title": "ロジックの不整合",
"question_used_in_quota": "この質問は“{quotaName}”クォータで使用されています", "question_used_in_quota": "この質問は“{quotaName}”クォータで使用されています",
@@ -1626,7 +1603,7 @@
"response_limits_redirections_and_more": "回答数の上限、リダイレクトなど。", "response_limits_redirections_and_more": "回答数の上限、リダイレクトなど。",
"response_options": "回答オプション", "response_options": "回答オプション",
"roundness": "丸み", "roundness": "丸み",
"roundness_description": "角の丸みを調整します。", "roundness_description": "カードの角の丸みを調整します。",
"row_used_in_logic_error": "この行は質問 {questionIndex} のロジックで使用されています。まず、ロジックから削除してください。", "row_used_in_logic_error": "この行は質問 {questionIndex} のロジックで使用されています。まず、ロジックから削除してください。",
"rows": "行", "rows": "行",
"save_and_close": "保存して閉じる", "save_and_close": "保存して閉じる",
@@ -1672,7 +1649,6 @@
"survey_completed_subheading": "この無料のオープンソースフォームは閉鎖されました", "survey_completed_subheading": "この無料のオープンソースフォームは閉鎖されました",
"survey_display_settings": "フォーム表示設定", "survey_display_settings": "フォーム表示設定",
"survey_placement": "フォームの配置", "survey_placement": "フォームの配置",
"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": "対象ブロックが見つかりません",
@@ -1735,7 +1711,7 @@
"pattern": "正規表現パターンに一致する", "pattern": "正規表現パターンに一致する",
"phone": "有効な電話番号である", "phone": "有効な電話番号である",
"rank_all_options": "すべてのオプションをランク付け", "rank_all_options": "すべてのオプションをランク付け",
"select_file_extensions": "ファイル拡張子を選択", "select_file_extensions": "ファイル拡張子を選択...",
"select_option": "オプションを選択", "select_option": "オプションを選択",
"start_date": "開始日", "start_date": "開始日",
"url": "有効なURLである" "url": "有効なURLである"
@@ -1761,9 +1737,9 @@
"waiting_time_across_surveys": "クールダウン期間(アンケート全体)", "waiting_time_across_surveys": "クールダウン期間(アンケート全体)",
"waiting_time_across_surveys_description": "アンケート疲れを防ぐため、このアンケートがワークスペース全体のクールダウン期間とどのように連動するかを選択してください。", "waiting_time_across_surveys_description": "アンケート疲れを防ぐため、このアンケートがワークスペース全体のクールダウン期間とどのように連動するかを選択してください。",
"welcome_message": "ウェルカムメッセージ", "welcome_message": "ウェルカムメッセージ",
"when": "条件",
"without_a_filter_all_of_your_users_can_be_surveyed": "フィルターがなければ、すべてのユーザーがフォームに回答できます。", "without_a_filter_all_of_your_users_can_be_surveyed": "フィルターがなければ、すべてのユーザーがフォームに回答できます。",
"you_have_not_created_a_segment_yet": "まだセグメントを作成していません", "you_have_not_created_a_segment_yet": "まだセグメントを作成していません",
"you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "翻訳を使用するには、ワークスペースに2つ以上の言語を設定する必要があります。",
"your_description_here_recall_information_with": "ここにあなたの説明。@ で情報を呼び出す", "your_description_here_recall_information_with": "ここにあなたの説明。@ で情報を呼び出す",
"your_question_here_recall_information_with": "ここにあなたの質問。@ で情報を呼び出す", "your_question_here_recall_information_with": "ここにあなたの質問。@ で情報を呼び出す",
"your_web_app": "あなたのウェブアプリ", "your_web_app": "あなたのウェブアプリ",
@@ -1833,7 +1809,7 @@
"disable_single_use_link_modal_button": "単一使用リンクを無効にする", "disable_single_use_link_modal_button": "単一使用リンクを無効にする",
"disable_single_use_link_modal_description": "単一使用リンクを共有した場合、参加者はフォームに回答できなくなります。", "disable_single_use_link_modal_description": "単一使用リンクを共有した場合、参加者はフォームに回答できなくなります。",
"generate_and_download_links": "リンクを生成&ダウンロード", "generate_and_download_links": "リンクを生成&ダウンロード",
"generate_links_error": "1回限りのリンクを生成できませんでした。APIを直接ご利用ください", "generate_links_error": "単一使用リンクを生成できませんでした。APIを直接使用してください",
"multi_use_link": "複数使用リンク", "multi_use_link": "複数使用リンク",
"multi_use_link_description": "1つのリンクで匿名の回答者から複数の回答を収集します。", "multi_use_link_description": "1つのリンクで匿名の回答者から複数の回答を収集します。",
"multi_use_powers_other_channels_description": "無効にすると、これらの他の配布チャネルも無効になります。", "multi_use_powers_other_channels_description": "無効にすると、これらの他の配布チャネルも無効になります。",
@@ -1944,7 +1920,7 @@
} }
}, },
"summary": { "summary": {
"added_filter_for_responses_where_answer_to_question": "質問{questionIdx}の答えが{filterComboBoxValue} - {filterValue}回答フィルターを追加しました", "added_filter_for_responses_where_answer_to_question": "質問 {questionIdx} の回答が {filterComboBoxValue} - {filterValue} である回答フィルターを追加しました",
"added_filter_for_responses_where_answer_to_question_is_skipped": "質問 {questionIdx} の回答がスキップされた回答のフィルターを追加しました", "added_filter_for_responses_where_answer_to_question_is_skipped": "質問 {questionIdx} の回答がスキップされた回答のフィルターを追加しました",
"aggregated": "集計済み", "aggregated": "集計済み",
"all_responses_csv": "すべての回答 (CSV)", "all_responses_csv": "すべての回答 (CSV)",
@@ -1971,7 +1947,6 @@
"filtered_responses_excel": "フィルター済み回答 (Excel)", "filtered_responses_excel": "フィルター済み回答 (Excel)",
"generating_qr_code": "QRコードを生成中", "generating_qr_code": "QRコードを生成中",
"impressions": "表示回数", "impressions": "表示回数",
"impressions_identified_only": "識別済みコンタクトからのインプレッションのみを表示しています",
"impressions_tooltip": "フォームが表示された回数。", "impressions_tooltip": "フォームが表示された回数。",
"in_app": { "in_app": {
"connection_description": "このフォームは、以下の条件に一致するあなたのウェブサイトのユーザーに表示されます", "connection_description": "このフォームは、以下の条件に一致するあなたのウェブサイトのユーザーに表示されます",
@@ -2014,7 +1989,6 @@
"last_quarter": "前四半期", "last_quarter": "前四半期",
"last_year": "昨年", "last_year": "昨年",
"limit": "制限", "limit": "制限",
"no_identified_impressions": "識別済みコンタクトからのインプレッションはありません",
"no_responses_found": "回答が見つかりません", "no_responses_found": "回答が見つかりません",
"other_values_found": "他の値が見つかりました", "other_values_found": "他の値が見つかりました",
"overall": "全体", "overall": "全体",
@@ -2037,7 +2011,6 @@
"starts": "開始", "starts": "開始",
"starts_tooltip": "フォームが開始された回数。", "starts_tooltip": "フォームが開始された回数。",
"survey_reset_successfully": "フォームを正常にリセットしました!{responseCount} 件の回答と {displayCount} 件の表示が削除されました。", "survey_reset_successfully": "フォームを正常にリセットしました!{responseCount} 件の回答と {displayCount} 件の表示が削除されました。",
"survey_results": "{surveyName}の結果",
"this_month": "今月", "this_month": "今月",
"this_quarter": "今四半期", "this_quarter": "今四半期",
"this_year": "今年", "this_year": "今年",
@@ -2157,7 +2130,7 @@
"add_background_color_description": "ロゴコンテナに背景色を追加します。", "add_background_color_description": "ロゴコンテナに背景色を追加します。",
"advanced_styling_field_border_radius": "境界線の丸み", "advanced_styling_field_border_radius": "境界線の丸み",
"advanced_styling_field_button_bg": "ボタンの背景", "advanced_styling_field_button_bg": "ボタンの背景",
"advanced_styling_field_button_bg_description": "次へ” / “送信ボタンを塗りつぶします。", "advanced_styling_field_button_bg_description": "次へ/送信ボタンを塗りつぶします。",
"advanced_styling_field_button_border_radius_description": "ボタンの角を丸めます。", "advanced_styling_field_button_border_radius_description": "ボタンの角を丸めます。",
"advanced_styling_field_button_font_size_description": "ボタンラベルのテキストサイズを調整します。", "advanced_styling_field_button_font_size_description": "ボタンラベルのテキストサイズを調整します。",
"advanced_styling_field_button_font_weight_description": "ボタンテキストを細くまたは太くします。", "advanced_styling_field_button_font_weight_description": "ボタンテキストを細くまたは太くします。",
@@ -2171,7 +2144,7 @@
"advanced_styling_field_description_size": "説明文のフォントサイズ", "advanced_styling_field_description_size": "説明文のフォントサイズ",
"advanced_styling_field_description_size_description": "説明テキストのサイズを調整します。", "advanced_styling_field_description_size_description": "説明テキストのサイズを調整します。",
"advanced_styling_field_description_weight": "説明文のフォントの太さ", "advanced_styling_field_description_weight": "説明文のフォントの太さ",
"advanced_styling_field_description_weight_description": "説明文の太さを細くしたり太くしたりします。", "advanced_styling_field_description_weight_description": "説明テキストを細くまたは太くします。",
"advanced_styling_field_font_size": "フォントサイズ", "advanced_styling_field_font_size": "フォントサイズ",
"advanced_styling_field_font_weight": "フォントの太さ", "advanced_styling_field_font_weight": "フォントの太さ",
"advanced_styling_field_headline_color": "見出しの色", "advanced_styling_field_headline_color": "見出しの色",
@@ -2185,7 +2158,7 @@
"advanced_styling_field_indicator_bg_description": "バーの塗りつぶし部分に色を付けます。", "advanced_styling_field_indicator_bg_description": "バーの塗りつぶし部分に色を付けます。",
"advanced_styling_field_input_border_radius_description": "入力フィールドの角を丸めます。", "advanced_styling_field_input_border_radius_description": "入力フィールドの角を丸めます。",
"advanced_styling_field_input_font_size_description": "入力フィールド内の入力テキストのサイズを調整します。", "advanced_styling_field_input_font_size_description": "入力フィールド内の入力テキストのサイズを調整します。",
"advanced_styling_field_input_height_description": "入力の最小高さを調整します。", "advanced_styling_field_input_height_description": "入力フィールドの最小高さを制御します。",
"advanced_styling_field_input_padding_x_description": "左右にスペースを追加します。", "advanced_styling_field_input_padding_x_description": "左右にスペースを追加します。",
"advanced_styling_field_input_padding_y_description": "上下にスペースを追加します。", "advanced_styling_field_input_padding_y_description": "上下にスペースを追加します。",
"advanced_styling_field_input_placeholder_opacity_description": "プレースホルダーのヒントテキストを薄くします。", "advanced_styling_field_input_placeholder_opacity_description": "プレースホルダーのヒントテキストを薄くします。",
@@ -2194,8 +2167,6 @@
"advanced_styling_field_input_text_description": "入力フィールドに入力されたテキストの色を設定します。", "advanced_styling_field_input_text_description": "入力フィールドに入力されたテキストの色を設定します。",
"advanced_styling_field_option_bg": "背景", "advanced_styling_field_option_bg": "背景",
"advanced_styling_field_option_bg_description": "オプション項目を塗りつぶします。", "advanced_styling_field_option_bg_description": "オプション項目を塗りつぶします。",
"advanced_styling_field_option_border": "枠線の色",
"advanced_styling_field_option_border_description": "ラジオボタンとチェックボックスの選択肢の輪郭を設定します。",
"advanced_styling_field_option_border_radius_description": "オプションの角を丸くします。", "advanced_styling_field_option_border_radius_description": "オプションの角を丸くします。",
"advanced_styling_field_option_font_size_description": "オプションラベルのテキストサイズを調整します。", "advanced_styling_field_option_font_size_description": "オプションラベルのテキストサイズを調整します。",
"advanced_styling_field_option_label": "ラベルの色", "advanced_styling_field_option_label": "ラベルの色",
@@ -2250,7 +2221,7 @@
"show_powered_by_formbricks": "「Powered by Formbricks」署名を表示", "show_powered_by_formbricks": "「Powered by Formbricks」署名を表示",
"styling_updated_successfully": "スタイルを正常に更新しました", "styling_updated_successfully": "スタイルを正常に更新しました",
"suggest_colors": "カラーを提案", "suggest_colors": "カラーを提案",
"suggested_colors_applied_please_save": "推奨カラーが正常に生成されました。変更を保存するには保存を押してください。", "suggested_colors_applied_please_save": "推奨カラーが正常に生成されました。変更を保存するには保存を押してください。",
"theme": "テーマ", "theme": "テーマ",
"theme_settings_description": "すべてのアンケート用のスタイルテーマを作成します。各アンケートでカスタムスタイルを有効にできます。" "theme_settings_description": "すべてのアンケート用のスタイルテーマを作成します。各アンケートでカスタムスタイルを有効にできます。"
}, },
@@ -2334,7 +2305,7 @@
"check_inbox_or_spam": "受信トレイにメールがない場合は、迷惑メールフォルダも確認してください。", "check_inbox_or_spam": "受信トレイにメールがない場合は、迷惑メールフォルダも確認してください。",
"completed": "このフォームはクローズしました。", "completed": "このフォームはクローズしました。",
"create_your_own": "独自のオープンソースフォームを作成", "create_your_own": "独自のオープンソースフォームを作成",
"enter_pin": "このアンケートは保護されています。以下にPINを入力してください", "enter_pin": "このフォームは保護されています。以下にPINを入力してください",
"just_curious": "ただ興味があるだけですか?", "just_curious": "ただ興味があるだけですか?",
"link_invalid": "このフォームは招待によってのみ回答できます。", "link_invalid": "このフォームは招待によってのみ回答できます。",
"paused": "このフォームは一時的に一時停止されています。", "paused": "このフォームは一時的に一時停止されています。",
@@ -2405,16 +2376,6 @@
"alignment_and_engagement_survey_question_4_headline": "会社はビジョンと戦略の整合性をどのように改善できますか?", "alignment_and_engagement_survey_question_4_headline": "会社はビジョンと戦略の整合性をどのように改善できますか?",
"alignment_and_engagement_survey_question_4_placeholder": "ここに回答を入力してください...", "alignment_and_engagement_survey_question_4_placeholder": "ここに回答を入力してください...",
"back": "戻る", "back": "戻る",
"block_1": "ブロック 1",
"block_10": "ブロック 10",
"block_2": "ブロック 2",
"block_3": "ブロック 3",
"block_4": "ブロック 4",
"block_5": "ブロック 5",
"block_6": "ブロック 6",
"block_7": "ブロック 7",
"block_8": "ブロック 8",
"block_9": "ブロック 9",
"book_interview": "面談を予約する", "book_interview": "面談を予約する",
"build_product_roadmap_description": "ユーザーが最も望んでいる「たった一つ」のものを特定し、構築する。", "build_product_roadmap_description": "ユーザーが最も望んでいる「たった一つ」のものを特定し、構築する。",
"build_product_roadmap_name": "製品ロードマップの構築", "build_product_roadmap_name": "製品ロードマップの構築",
@@ -2622,6 +2583,7 @@
"csat_survey_question_3_headline": "申し訳ありません!体験を改善するために何かできることはありますか?", "csat_survey_question_3_headline": "申し訳ありません!体験を改善するために何かできることはありますか?",
"csat_survey_question_3_placeholder": "ここに回答を入力してください...", "csat_survey_question_3_placeholder": "ここに回答を入力してください...",
"cta_description": "情報を表示し、特定の行動を促す", "cta_description": "情報を表示し、特定の行動を促す",
"custom_survey_block_1_name": "ブロック1",
"custom_survey_description": "テンプレートを使わずにアンケートを作成する。", "custom_survey_description": "テンプレートを使わずにアンケートを作成する。",
"custom_survey_name": "最初から始める", "custom_survey_name": "最初から始める",
"custom_survey_question_1_headline": "何を知りたいですか?", "custom_survey_question_1_headline": "何を知りたいですか?",
@@ -3024,9 +2986,6 @@
"preview_survey_question_2_choice_2_label": "いいえ、結構です!", "preview_survey_question_2_choice_2_label": "いいえ、結構です!",
"preview_survey_question_2_headline": "最新情報を知りたいですか?", "preview_survey_question_2_headline": "最新情報を知りたいですか?",
"preview_survey_question_2_subheader": "これは説明の例です。", "preview_survey_question_2_subheader": "これは説明の例です。",
"preview_survey_question_open_text_headline": "他に共有したいことはありますか?",
"preview_survey_question_open_text_placeholder": "ここに回答を入力してください...",
"preview_survey_question_open_text_subheader": "あなたのフィードバックは、私たちの改善に役立ちます。",
"preview_survey_welcome_card_headline": "ようこそ!", "preview_survey_welcome_card_headline": "ようこそ!",
"prioritize_features_description": "ユーザーが最も必要とする機能と最も必要としない機能を特定する。", "prioritize_features_description": "ユーザーが最も必要とする機能と最も必要としない機能を特定する。",
"prioritize_features_name": "機能の優先順位付け", "prioritize_features_name": "機能の優先順位付け",
@@ -3096,7 +3055,7 @@
"professional_development_survey_question_2_choice_5": "個人研究", "professional_development_survey_question_2_choice_5": "個人研究",
"professional_development_survey_question_2_choice_6": "その他", "professional_development_survey_question_2_choice_6": "その他",
"professional_development_survey_question_2_headline": "あなたの成長にとって最も価値があると思う専門能力開発活動の種類は何ですか?", "professional_development_survey_question_2_headline": "あなたの成長にとって最も価値があると思う専門能力開発活動の種類は何ですか?",
"professional_development_survey_question_2_subheader": "当てはまるものをすべて選択してください", "professional_development_survey_question_2_subheader": "当てはまるものをすべて選択してください",
"professional_development_survey_question_3_choice_1": "はい", "professional_development_survey_question_3_choice_1": "はい",
"professional_development_survey_question_3_choice_2": "いいえ", "professional_development_survey_question_3_choice_2": "いいえ",
"professional_development_survey_question_3_headline": "過去に専門能力開発のために時間を割きましたか?", "professional_development_survey_question_3_headline": "過去に専門能力開発のために時間を割きましたか?",
@@ -3275,18 +3234,5 @@
"usability_question_9_headline": "システムを使っている間、自信がありました。", "usability_question_9_headline": "システムを使っている間、自信がありました。",
"usability_rating_description": "標準化された10の質問アンケートを使用して、製品に対するユーザーの体験を評価し、知覚された使いやすさを測定する。", "usability_rating_description": "標準化された10の質問アンケートを使用して、製品に対するユーザーの体験を評価し、知覚された使いやすさを測定する。",
"usability_score_name": "システムユーザビリティスコア(SUS)" "usability_score_name": "システムユーザビリティスコア(SUS)"
},
"workflows": {
"coming_soon_description": "ワークフローのアイデアを共有していただきありがとうございます!現在この機能を設計中で、あなたのフィードバックは私たちが必要とされる機能を構築するのに役立ちます。",
"coming_soon_title": "もうすぐ完成です!",
"follow_up_label": "他に追加したいことはありますか?",
"follow_up_placeholder": "自動化したい具体的な作業や使用したいツール・連携があればご記入ください。",
"generate_button": "ワークフローを生成",
"heading": "どのようなワークフローを作成しますか?",
"placeholder": "作成したいワークフローについて説明してください…",
"subheading": "数秒でワークフローを生成します。",
"submit_button": "詳細を追加",
"thank_you_description": "ご入力いただいた内容は、より実用的なWorkflows機能の開発に役立てます。進捗は順次ご案内します。",
"thank_you_title": "フィードバックありがとうございます!"
} }
} }
+32 -86
View File
@@ -175,12 +175,9 @@
"copy": "Kopiëren", "copy": "Kopiëren",
"copy_code": "Kopieer code", "copy_code": "Kopieer code",
"copy_link": "Kopieer link", "copy_link": "Kopieer link",
"count_attributes": "{count, plural, one {{count} attribuut} other {{count} attributen}}", "count_attributes": "{value, plural, one {{value} attribuut} other {{value} attributen}}",
"count_contacts": "{count, plural, one {{count} contact} other {{count} contacten}}", "count_contacts": "{value, plural, one {{value} contact} other {{value} contacten}}",
"count_members": "{count, plural, one {{count} lid} other {{count} leden}}", "count_responses": "{value, plural, one {{value} reactie} other {{value} reacties}}",
"count_questions": "{count, plural, one {{count} vraag} other {{count} vragen}}",
"count_responses": "{count, plural, one {{count} reactie} other {{count} reacties}}",
"count_selections": "{count, plural, one {{count} selectie} other {{count} selecties}}",
"create_new_organization": "Creëer een nieuwe organisatie", "create_new_organization": "Creëer een nieuwe organisatie",
"create_segment": "Segment maken", "create_segment": "Segment maken",
"create_survey": "Enquête maken", "create_survey": "Enquête maken",
@@ -194,7 +191,6 @@
"days": "dagen", "days": "dagen",
"default": "Standaard", "default": "Standaard",
"delete": "Verwijderen", "delete": "Verwijderen",
"delete_what": "Verwijder {deleteWhat}",
"description": "Beschrijving", "description": "Beschrijving",
"dev_env": "Ontwikkelomgeving", "dev_env": "Ontwikkelomgeving",
"development": "Ontwikkeling", "development": "Ontwikkeling",
@@ -210,8 +206,6 @@
"download": "Downloaden", "download": "Downloaden",
"draft": "Voorlopige versie", "draft": "Voorlopige versie",
"duplicate": "Duplicaat", "duplicate": "Duplicaat",
"duplicate_copy": "(kopie)",
"duplicate_copy_number": "(kopie {copyNumber})",
"e_commerce": "E-commerce", "e_commerce": "E-commerce",
"edit": "Bewerking", "edit": "Bewerking",
"email": "E-mail", "email": "E-mail",
@@ -224,16 +218,13 @@
"error": "Fout", "error": "Fout",
"error_component_description": "Deze bron bestaat niet of u beschikt niet over de benodigde toegangsrechten.", "error_component_description": "Deze bron bestaat niet of u beschikt niet over de benodigde toegangsrechten.",
"error_component_title": "Fout bij het laden van bronnen", "error_component_title": "Fout bij het laden van bronnen",
"error_loading_data": "Fout bij het laden van gegevens",
"error_rate_limit_description": "Maximaal aantal verzoeken bereikt. Probeer het later opnieuw.", "error_rate_limit_description": "Maximaal aantal verzoeken bereikt. Probeer het later opnieuw.",
"error_rate_limit_title": "Tarieflimiet overschreden", "error_rate_limit_title": "Tarieflimiet overschreden",
"expand_rows": "Vouw rijen uit", "expand_rows": "Vouw rijen uit",
"failed_to_copy_to_clipboard": "Kopiëren naar klembord mislukt", "failed_to_copy_to_clipboard": "Kopiëren naar klembord mislukt",
"failed_to_load_organizations": "Laden van organisaties mislukt", "failed_to_load_organizations": "Laden van organisaties mislukt",
"failed_to_load_workspaces": "Laden van werkruimtes mislukt", "failed_to_load_workspaces": "Laden van werkruimtes mislukt",
"filter": "Filter",
"finish": "Finish", "finish": "Finish",
"first_name": "Voornaam",
"follow_these": "Volg deze", "follow_these": "Volg deze",
"formbricks_version": "Formbricks-versie", "formbricks_version": "Formbricks-versie",
"full_name": "Volledige naam", "full_name": "Volledige naam",
@@ -246,7 +237,6 @@
"hidden_field": "Verborgen veld", "hidden_field": "Verborgen veld",
"hidden_fields": "Verborgen velden", "hidden_fields": "Verborgen velden",
"hide_column": "Kolom verbergen", "hide_column": "Kolom verbergen",
"id": "ID",
"image": "Afbeelding", "image": "Afbeelding",
"images": "Afbeeldingen", "images": "Afbeeldingen",
"import": "Importeren", "import": "Importeren",
@@ -264,7 +254,6 @@
"key": "Sleutel", "key": "Sleutel",
"label": "Label", "label": "Label",
"language": "Taal", "language": "Taal",
"last_name": "Achternaam",
"learn_more": "Meer informatie", "learn_more": "Meer informatie",
"license_expired": "License Expired", "license_expired": "License Expired",
"light_overlay": "Lichte overlay", "light_overlay": "Lichte overlay",
@@ -279,6 +268,7 @@
"look_and_feel": "Kijk & voel", "look_and_feel": "Kijk & voel",
"manage": "Beheren", "manage": "Beheren",
"marketing": "Marketing", "marketing": "Marketing",
"member": "Lid",
"members": "Leden", "members": "Leden",
"members_and_teams": "Leden & teams", "members_and_teams": "Leden & teams",
"membership_not_found": "Lidmaatschap niet gevonden", "membership_not_found": "Lidmaatschap niet gevonden",
@@ -290,7 +280,6 @@
"move_down": "Ga naar beneden", "move_down": "Ga naar beneden",
"move_up": "Ga omhoog", "move_up": "Ga omhoog",
"multiple_languages": "Meerdere talen", "multiple_languages": "Meerdere talen",
"my_product": "mijn product",
"name": "Naam", "name": "Naam",
"new": "Nieuw", "new": "Nieuw",
"new_version_available": "Formbricks {version} is hier. Upgrade nu!", "new_version_available": "Formbricks {version} is hier. Upgrade nu!",
@@ -386,6 +375,8 @@
"select_teams": "Selecteer teams", "select_teams": "Selecteer teams",
"selected": "Gekozen", "selected": "Gekozen",
"selected_questions": "Geselecteerde vragen", "selected_questions": "Geselecteerde vragen",
"selection": "Selectie",
"selections": "Selecties",
"send_test_email": "Test-e-mail verzenden", "send_test_email": "Test-e-mail verzenden",
"session_not_found": "Sessie niet gevonden", "session_not_found": "Sessie niet gevonden",
"settings": "Instellingen", "settings": "Instellingen",
@@ -394,7 +385,6 @@
"show_response_count": "Toon het aantal reacties", "show_response_count": "Toon het aantal reacties",
"shown": "Getoond", "shown": "Getoond",
"size": "Maat", "size": "Maat",
"skip": "Overslaan",
"skipped": "Overgeslagen", "skipped": "Overgeslagen",
"skips": "Overslaan", "skips": "Overslaan",
"some_files_failed_to_upload": "Sommige bestanden konden niet worden geüpload", "some_files_failed_to_upload": "Sommige bestanden konden niet worden geüpload",
@@ -438,7 +428,6 @@
"top_right": "Rechtsboven", "top_right": "Rechtsboven",
"try_again": "Probeer het opnieuw", "try_again": "Probeer het opnieuw",
"type": "Type", "type": "Type",
"unknown_survey": "Onbekende enquête",
"unlock_more_workspaces_with_a_higher_plan": "Ontgrendel meer werkruimtes met een hoger abonnement.", "unlock_more_workspaces_with_a_higher_plan": "Ontgrendel meer werkruimtes met een hoger abonnement.",
"update": "Update", "update": "Update",
"updated": "Bijgewerkt", "updated": "Bijgewerkt",
@@ -464,7 +453,6 @@
"website_survey": "Website-enquête", "website_survey": "Website-enquête",
"weeks": "weken", "weeks": "weken",
"welcome_card": "Welkomstkaart", "welcome_card": "Welkomstkaart",
"workflows": "Workflows",
"workspace_configuration": "Werkruimte-configuratie", "workspace_configuration": "Werkruimte-configuratie",
"workspace_created_successfully": "Project succesvol aangemaakt", "workspace_created_successfully": "Project succesvol aangemaakt",
"workspace_creation_description": "Organiseer enquêtes in werkruimtes voor beter toegangsbeheer.", "workspace_creation_description": "Organiseer enquêtes in werkruimtes voor beter toegangsbeheer.",
@@ -647,16 +635,17 @@
"attribute_value": "Waarde", "attribute_value": "Waarde",
"attribute_value_placeholder": "Attribuutwaarde", "attribute_value_placeholder": "Attribuutwaarde",
"attributes_msg_attribute_limit_exceeded": "Kon {count} nieuwe attribu(u)t(en) niet aanmaken omdat dit de maximale limiet van {limit} attribuutklassen zou overschrijden. Bestaande attributen zijn succesvol bijgewerkt.", "attributes_msg_attribute_limit_exceeded": "Kon {count} nieuwe attribu(u)t(en) niet aanmaken omdat dit de maximale limiet van {limit} attribuutklassen zou overschrijden. Bestaande attributen zijn succesvol bijgewerkt.",
"attributes_msg_attribute_type_validation_error": "{error} (attribuut {key} heeft dataType: {dataType})", "attributes_msg_attribute_type_validation_error": "{error} (attribuut '{key}' heeft dataType: {dataType})",
"attributes_msg_email_already_exists": "Het e-mailadres bestaat al voor deze omgeving en is niet bijgewerkt.", "attributes_msg_email_already_exists": "Het e-mailadres bestaat al voor deze omgeving en is niet bijgewerkt.",
"attributes_msg_email_or_userid_required": "E-mail of gebruikers-ID is vereist. De bestaande waarden zijn behouden.", "attributes_msg_email_or_userid_required": "E-mailadres of userId is vereist. De bestaande waarden zijn behouden.",
"attributes_msg_new_attribute_created": "Nieuw attribuut {key} aangemaakt met type {dataType}", "attributes_msg_new_attribute_created": "Nieuw attribuut '{key}' aangemaakt met type '{dataType}'",
"attributes_msg_userid_already_exists": "De gebruikers-ID bestaat al voor deze omgeving en is niet bijgewerkt.", "attributes_msg_userid_already_exists": "De userId bestaat al voor deze omgeving en is niet bijgewerkt.",
"contact_deleted_successfully": "Contact succesvol verwijderd", "contact_deleted_successfully": "Contact succesvol verwijderd",
"contact_not_found": "Er is geen dergelijk contact gevonden", "contact_not_found": "Er is geen dergelijk contact gevonden",
"contacts_table_refresh": "Vernieuw contacten", "contacts_table_refresh": "Vernieuw contacten",
"contacts_table_refresh_success": "Contacten zijn vernieuwd", "contacts_table_refresh_success": "Contacten zijn vernieuwd",
"create_attribute": "Attribuut aanmaken", "create_attribute": "Attribuut aanmaken",
"create_key": "Sleutel aanmaken",
"create_new_attribute": "Nieuw attribuut aanmaken", "create_new_attribute": "Nieuw attribuut aanmaken",
"create_new_attribute_description": "Maak een nieuw attribuut aan voor segmentatiedoeleinden.", "create_new_attribute_description": "Maak een nieuw attribuut aan voor segmentatiedoeleinden.",
"custom_attributes": "Aangepaste kenmerken", "custom_attributes": "Aangepaste kenmerken",
@@ -667,7 +656,6 @@
"delete_attribute_confirmation": "{value, plural, one {Dit verwijdert het geselecteerde attribuut. Alle contactgegevens die aan dit attribuut zijn gekoppeld, gaan verloren.} other {Dit verwijdert de geselecteerde attributen. Alle contactgegevens die aan deze attributen zijn gekoppeld, gaan verloren.}}", "delete_attribute_confirmation": "{value, plural, one {Dit verwijdert het geselecteerde attribuut. Alle contactgegevens die aan dit attribuut zijn gekoppeld, gaan verloren.} other {Dit verwijdert de geselecteerde attributen. Alle contactgegevens die aan deze attributen zijn gekoppeld, gaan verloren.}}",
"delete_contact_confirmation": "Hierdoor worden alle enquêtereacties en contactkenmerken verwijderd die aan dit contact zijn gekoppeld. Elke targeting en personalisatie op basis van de gegevens van dit contact gaat verloren.", "delete_contact_confirmation": "Hierdoor worden alle enquêtereacties en contactkenmerken verwijderd die aan dit contact zijn gekoppeld. Elke targeting en personalisatie op basis van de gegevens van dit contact gaat verloren.",
"delete_contact_confirmation_with_quotas": "{value, plural, one {Dit verwijdert alle enquêteresultaten en contactattributen die aan dit contact zijn gekoppeld. Alle targeting en personalisatie op basis van de gegevens van dit contact gaan verloren. Als dit contact reacties heeft die meetellen voor enquêtekvota, worden de quotawaarden verlaagd maar blijven de limieten ongewijzigd.} other {Dit verwijdert alle enquêteresultaten en contactattributen die aan deze contacten zijn gekoppeld. Alle targeting en personalisatie op basis van de gegevens van deze contacten gaan verloren. Als deze contacten reacties hebben die meetellen voor enquêtekvota, worden de quotawaarden verlaagd maar blijven de limieten ongewijzigd.}}", "delete_contact_confirmation_with_quotas": "{value, plural, one {Dit verwijdert alle enquêteresultaten en contactattributen die aan dit contact zijn gekoppeld. Alle targeting en personalisatie op basis van de gegevens van dit contact gaan verloren. Als dit contact reacties heeft die meetellen voor enquêtekvota, worden de quotawaarden verlaagd maar blijven de limieten ongewijzigd.} other {Dit verwijdert alle enquêteresultaten en contactattributen die aan deze contacten zijn gekoppeld. Alle targeting en personalisatie op basis van de gegevens van deze contacten gaan verloren. Als deze contacten reacties hebben die meetellen voor enquêtekvota, worden de quotawaarden verlaagd maar blijven de limieten ongewijzigd.}}",
"displays": "Weergaven",
"edit_attribute": "Attribuut bewerken", "edit_attribute": "Attribuut bewerken",
"edit_attribute_description": "Werk het label en de beschrijving voor dit attribuut bij.", "edit_attribute_description": "Werk het label en de beschrijving voor dit attribuut bij.",
"edit_attribute_values": "Attributen bewerken", "edit_attribute_values": "Attributen bewerken",
@@ -679,7 +667,6 @@
"invalid_csv_column_names": "Ongeldige CSV-kolomna(a)m(en): {columns}. Kolomnamen die nieuwe kenmerken worden, mogen alleen kleine letters, cijfers en underscores bevatten en moeten beginnen met een letter.", "invalid_csv_column_names": "Ongeldige CSV-kolomna(a)m(en): {columns}. Kolomnamen die nieuwe kenmerken worden, mogen alleen kleine letters, cijfers en underscores bevatten en moeten beginnen met een letter.",
"invalid_date_format": "Ongeldig datumformaat. Gebruik een geldige datum.", "invalid_date_format": "Ongeldig datumformaat. Gebruik een geldige datum.",
"invalid_number_format": "Ongeldig getalformaat. Voer een geldig getal in.", "invalid_number_format": "Ongeldig getalformaat. Voer een geldig getal in.",
"no_activity_yet": "Nog geen activiteit",
"no_published_link_surveys_available": "Geen gepubliceerde link-enquêtes beschikbaar. Publiceer eerst een link-enquête.", "no_published_link_surveys_available": "Geen gepubliceerde link-enquêtes beschikbaar. Publiceer eerst een link-enquête.",
"no_published_surveys": "Geen gepubliceerde enquêtes", "no_published_surveys": "Geen gepubliceerde enquêtes",
"no_responses_found": "Geen reacties gevonden", "no_responses_found": "Geen reacties gevonden",
@@ -694,12 +681,10 @@
"select_a_survey": "Selecteer een enquête", "select_a_survey": "Selecteer een enquête",
"select_attribute": "Selecteer Kenmerk", "select_attribute": "Selecteer Kenmerk",
"select_attribute_key": "Selecteer kenmerksleutel", "select_attribute_key": "Selecteer kenmerksleutel",
"survey_viewed": "Enquête bekeken",
"survey_viewed_at": "Bekeken op",
"system_attributes": "Systeemkenmerken", "system_attributes": "Systeemkenmerken",
"unlock_contacts_description": "Beheer contacten en verstuur gerichte enquêtes", "unlock_contacts_description": "Beheer contacten en verstuur gerichte enquêtes",
"unlock_contacts_title": "Ontgrendel contacten met een hoger abonnement", "unlock_contacts_title": "Ontgrendel contacten met een hoger abonnement",
"upload_contacts_error_attribute_type_mismatch": "Attribuut {key} is getypeerd als {dataType} maar CSV bevat ongeldige waarden: {values}", "upload_contacts_error_attribute_type_mismatch": "Attribuut \"{key}\" is getypeerd als \"{dataType}\" maar CSV bevat ongeldige waarden: {values}",
"upload_contacts_error_duplicate_mappings": "Dubbele koppelingen gevonden voor de volgende attributen: {attributes}", "upload_contacts_error_duplicate_mappings": "Dubbele koppelingen gevonden voor de volgende attributen: {attributes}",
"upload_contacts_error_file_too_large": "Bestandsgrootte overschrijdt de maximale limiet van 800KB", "upload_contacts_error_file_too_large": "Bestandsgrootte overschrijdt de maximale limiet van 800KB",
"upload_contacts_error_generic": "Er is een fout opgetreden bij het uploaden van de contacten. Probeer het later opnieuw.", "upload_contacts_error_generic": "Er is een fout opgetreden bij het uploaden van de contacten. Probeer het later opnieuw.",
@@ -767,12 +752,7 @@
"link_google_sheet": "Link Google Spreadsheet", "link_google_sheet": "Link Google Spreadsheet",
"link_new_sheet": "Nieuw blad koppelen", "link_new_sheet": "Nieuw blad koppelen",
"no_integrations_yet": "Uw Google Spreadsheet-integraties verschijnen hier zodra u ze toevoegt. ⏲️", "no_integrations_yet": "Uw Google Spreadsheet-integraties verschijnen hier zodra u ze toevoegt. ⏲️",
"reconnect_button": "Maak opnieuw verbinding", "spreadsheet_url": "Spreadsheet-URL"
"reconnect_button_description": "Je Google Sheets-verbinding is verlopen. Maak opnieuw verbinding om door te gaan met het synchroniseren van antwoorden. Je bestaande spreadsheetlinks en gegevens blijven behouden.",
"reconnect_button_tooltip": "Maak opnieuw verbinding met de integratie om je toegang te vernieuwen. Je bestaande spreadsheetlinks en gegevens blijven behouden.",
"spreadsheet_permission_error": "Je hebt geen toegang tot dit spreadsheet. Zorg ervoor dat het spreadsheet is gedeeld met je Google-account en dat je schrijftoegang hebt tot het spreadsheet.",
"spreadsheet_url": "Spreadsheet-URL",
"token_expired_error": "Het vernieuwingstoken van Google Sheets is verlopen of ingetrokken. Maak opnieuw verbinding met de integratie."
}, },
"include_created_at": "Inclusief gemaakt op", "include_created_at": "Inclusief gemaakt op",
"include_hidden_fields": "Inclusief verborgen velden", "include_hidden_fields": "Inclusief verborgen velden",
@@ -981,7 +961,7 @@
"current_plan": "Huidig abonnement", "current_plan": "Huidig abonnement",
"current_tier_limit": "Huidige niveaulimiet", "current_tier_limit": "Huidige niveaulimiet",
"custom": "Aangepast en schaal", "custom": "Aangepast en schaal",
"custom_contacts_limit": "Aangepaste contactenlimiet", "custom_contacts_limit": "Aangepaste contactlimiet",
"custom_response_limit": "Aangepaste reactielimiet", "custom_response_limit": "Aangepaste reactielimiet",
"custom_workspace_limit": "Aangepaste werkruimtelimiet", "custom_workspace_limit": "Aangepaste werkruimtelimiet",
"email_embedded_surveys": "Ingebedde enquêtes per e-mail", "email_embedded_surveys": "Ingebedde enquêtes per e-mail",
@@ -1059,7 +1039,7 @@
"recheck_license_invalid": "De licentiesleutel is ongeldig. Controleer je ENTERPRISE_LICENSE_KEY.", "recheck_license_invalid": "De licentiesleutel is ongeldig. Controleer je ENTERPRISE_LICENSE_KEY.",
"recheck_license_success": "Licentiecontrole geslaagd", "recheck_license_success": "Licentiecontrole geslaagd",
"recheck_license_unreachable": "Licentieserver is niet bereikbaar. Probeer het later opnieuw.", "recheck_license_unreachable": "Licentieserver is niet bereikbaar. Probeer het later opnieuw.",
"rechecking": "Opnieuw controleren", "rechecking": "Opnieuw controleren...",
"request_30_day_trial_license": "Vraag een proeflicentie van 30 dagen aan", "request_30_day_trial_license": "Vraag een proeflicentie van 30 dagen aan",
"saml_sso": "SAML-SSO", "saml_sso": "SAML-SSO",
"service_level_agreement": "Service Level Overeenkomst", "service_level_agreement": "Service Level Overeenkomst",
@@ -1087,7 +1067,7 @@
"email_customization_preview_email_heading": "Hé {userName}", "email_customization_preview_email_heading": "Hé {userName}",
"email_customization_preview_email_text": "Dit is een e-mailvoorbeeld om u te laten zien welk logo in de e-mails wordt weergegeven.", "email_customization_preview_email_text": "Dit is een e-mailvoorbeeld om u te laten zien welk logo in de e-mails wordt weergegeven.",
"error_deleting_organization_please_try_again": "Fout bij verwijderen van organisatie. Probeer het opnieuw.", "error_deleting_organization_please_try_again": "Fout bij verwijderen van organisatie. Probeer het opnieuw.",
"from_your_organization": "{memberName} uit je organisatie", "from_your_organization": "vanuit uw organisatie",
"invitation_sent_once_more": "Uitnodiging nogmaals verzonden.", "invitation_sent_once_more": "Uitnodiging nogmaals verzonden.",
"invite_deleted_successfully": "Uitnodiging succesvol verwijderd", "invite_deleted_successfully": "Uitnodiging succesvol verwijderd",
"invite_expires_on": "Uitnodiging verloopt op {date}", "invite_expires_on": "Uitnodiging verloopt op {date}",
@@ -1136,7 +1116,7 @@
"notification_settings_updated": "Meldingsinstellingen bijgewerkt", "notification_settings_updated": "Meldingsinstellingen bijgewerkt",
"set_up_an_alert_to_get_an_email_on_new_responses": "Stel een waarschuwing in om een e-mail te ontvangen bij nieuwe reacties", "set_up_an_alert_to_get_an_email_on_new_responses": "Stel een waarschuwing in om een e-mail te ontvangen bij nieuwe reacties",
"use_the_integration": "Gebruik de integratie", "use_the_integration": "Gebruik de integratie",
"want_to_loop_in_organization_mates": "Wil je organisatiegenoten op de hoogte brengen?", "want_to_loop_in_organization_mates": "Ik wil organisatiegenoten inschakelen",
"you_will_not_be_auto_subscribed_to_this_organizations_surveys_anymore": "U wordt niet meer automatisch ingeschreven voor de enquêtes van deze organisatie!", "you_will_not_be_auto_subscribed_to_this_organizations_surveys_anymore": "U wordt niet meer automatisch ingeschreven voor de enquêtes van deze organisatie!",
"you_will_not_receive_any_more_emails_for_responses_on_this_survey": "U ontvangt geen e-mails meer voor reacties op deze enquête!" "you_will_not_receive_any_more_emails_for_responses_on_this_survey": "U ontvangt geen e-mails meer voor reacties op deze enquête!"
}, },
@@ -1252,7 +1232,6 @@
"add_fallback_placeholder": "Voeg een tijdelijke aanduiding toe om aan te geven of er geen waarde is om te onthouden.", "add_fallback_placeholder": "Voeg een tijdelijke aanduiding toe om aan te geven of er geen waarde is om te onthouden.",
"add_hidden_field_id": "Voeg een verborgen veld-ID toe", "add_hidden_field_id": "Voeg een verborgen veld-ID toe",
"add_highlight_border": "Markeerrand toevoegen", "add_highlight_border": "Markeerrand toevoegen",
"add_highlight_border_description": "Geldt alleen voor in-product enquêtes.",
"add_logic": "Voeg logica toe", "add_logic": "Voeg logica toe",
"add_none_of_the_above": "Voeg 'Geen van bovenstaande' toe", "add_none_of_the_above": "Voeg 'Geen van bovenstaande' toe",
"add_option": "Optie toevoegen", "add_option": "Optie toevoegen",
@@ -1269,14 +1248,12 @@
"adjust_survey_closed_message": "Pas het bericht 'Enquête gesloten' aan", "adjust_survey_closed_message": "Pas het bericht 'Enquête gesloten' aan",
"adjust_survey_closed_message_description": "Wijzig het bericht dat bezoekers zien wanneer de enquête wordt gesloten.", "adjust_survey_closed_message_description": "Wijzig het bericht dat bezoekers zien wanneer de enquête wordt gesloten.",
"adjust_the_theme_in_the": "Pas het thema aan in de", "adjust_the_theme_in_the": "Pas het thema aan in de",
"all_are_true": "alle zijn waar",
"all_other_answers_will_continue_to": "Alle andere antwoorden blijven hetzelfde", "all_other_answers_will_continue_to": "Alle andere antwoorden blijven hetzelfde",
"allow_multi_select": "Multi-select toestaan", "allow_multi_select": "Multi-select toestaan",
"allow_multiple_files": "Meerdere bestanden toestaan", "allow_multiple_files": "Meerdere bestanden toestaan",
"allow_users_to_select_more_than_one_image": "Sta gebruikers toe meer dan één afbeelding te selecteren", "allow_users_to_select_more_than_one_image": "Sta gebruikers toe meer dan één afbeelding te selecteren",
"and_launch_surveys_in_your_website_or_app": "en start enquêtes op uw website of app.", "and_launch_surveys_in_your_website_or_app": "en start enquêtes op uw website of app.",
"animation": "Animatie", "animation": "Animatie",
"any_is_true": "een is waar",
"app_survey_description": "Sluit een enquête in uw web-app of website in om reacties te verzamelen.", "app_survey_description": "Sluit een enquête in uw web-app of website in om reacties te verzamelen.",
"assign": "Toewijzen =", "assign": "Toewijzen =",
"audience": "Publiek", "audience": "Publiek",
@@ -1307,7 +1284,7 @@
"calculate": "Berekenen", "calculate": "Berekenen",
"capture_a_new_action_to_trigger_a_survey_on": "Leg een nieuwe actie vast om een enquête over te activeren.", "capture_a_new_action_to_trigger_a_survey_on": "Leg een nieuwe actie vast om een enquête over te activeren.",
"capture_ip_address": "IP-adres vastleggen", "capture_ip_address": "IP-adres vastleggen",
"capture_ip_address_description": "Bewaar het IP-adres van de respondent in de metadata van het antwoord voor duplicaatdetectie en beveiligingsdoeleinden", "capture_ip_address_description": "Sla het IP-adres van de respondent op in de metadata van het antwoord voor detectie van duplicaten en beveiligingsdoeleinden",
"capture_new_action": "Leg nieuwe actie vast", "capture_new_action": "Leg nieuwe actie vast",
"card_arrangement_for_survey_type_derived": "Kaartarrangement voor {surveyTypeDerived} enquêtes", "card_arrangement_for_survey_type_derived": "Kaartarrangement voor {surveyTypeDerived} enquêtes",
"card_background_color": "Achtergrondkleur van de kaart", "card_background_color": "Achtergrondkleur van de kaart",
@@ -1318,7 +1295,7 @@
"casual": "Casual", "casual": "Casual",
"caution_edit_duplicate": "Dupliceren en bewerken", "caution_edit_duplicate": "Dupliceren en bewerken",
"caution_edit_published_survey": "Een gepubliceerde enquête bewerken?", "caution_edit_published_survey": "Een gepubliceerde enquête bewerken?",
"caution_explanation_intro": "We begrijpen dat je mogelijk nog wijzigingen wilt aanbrengen. Dit gebeurt er als je dat doet:", "caution_explanation_intro": "We begrijpen dat u misschien nog wijzigingen wilt aanbrengen. Dit is wat er gebeurt als je dat doet:",
"caution_explanation_new_responses_separated": "Reacties van vóór de wijziging zijn mogelijk niet of slechts gedeeltelijk opgenomen in de onderzoekssamenvatting.", "caution_explanation_new_responses_separated": "Reacties van vóór de wijziging zijn mogelijk niet of slechts gedeeltelijk opgenomen in de onderzoekssamenvatting.",
"caution_explanation_only_new_responses_in_summary": "Alle gegevens, inclusief eerdere antwoorden, blijven beschikbaar als download op de overzichtspagina van de enquête.", "caution_explanation_only_new_responses_in_summary": "Alle gegevens, inclusief eerdere antwoorden, blijven beschikbaar als download op de overzichtspagina van de enquête.",
"caution_explanation_responses_are_safe": "Oudere en nieuwere antwoorden lopen door elkaar heen, wat kan leiden tot misleidende gegevenssamenvattingen.", "caution_explanation_responses_are_safe": "Oudere en nieuwere antwoorden lopen door elkaar heen, wat kan leiden tot misleidende gegevenssamenvattingen.",
@@ -1453,6 +1430,7 @@
"follow_ups_modal_updated_successfull_toast": "Follow-up bijgewerkt en wordt opgeslagen zodra u de enquête opslaat.", "follow_ups_modal_updated_successfull_toast": "Follow-up bijgewerkt en wordt opgeslagen zodra u de enquête opslaat.",
"follow_ups_new": "Nieuw vervolg", "follow_ups_new": "Nieuw vervolg",
"follow_ups_upgrade_button_text": "Upgrade om follow-ups mogelijk te maken", "follow_ups_upgrade_button_text": "Upgrade om follow-ups mogelijk te maken",
"form_styling": "Vorm styling",
"formbricks_sdk_is_not_connected": "Formbricks SDK is niet verbonden", "formbricks_sdk_is_not_connected": "Formbricks SDK is niet verbonden",
"four_points": "4 punten", "four_points": "4 punten",
"heading": "Rubriek", "heading": "Rubriek",
@@ -1542,7 +1520,7 @@
"option_idx": "Optie {choiceIndex}", "option_idx": "Optie {choiceIndex}",
"option_used_in_logic_error": "Deze optie wordt gebruikt in de logica van vraag {questionIndex}. Verwijder het eerst uit de logica.", "option_used_in_logic_error": "Deze optie wordt gebruikt in de logica van vraag {questionIndex}. Verwijder het eerst uit de logica.",
"optional": "Optioneel", "optional": "Optioneel",
"options": "Opties*", "options": "Opties",
"options_used_in_logic_bulk_error": "De volgende opties worden gebruikt in logica: {questionIndexes}. Verwijder ze eerst uit de logica.", "options_used_in_logic_bulk_error": "De volgende opties worden gebruikt in logica: {questionIndexes}. Verwijder ze eerst uit de logica.",
"override_theme_with_individual_styles_for_this_survey": "Overschrijf het thema met individuele stijlen voor deze enquête.", "override_theme_with_individual_styles_for_this_survey": "Overschrijf het thema met individuele stijlen voor deze enquête.",
"overwrite_global_waiting_time": "Aangepaste afkoelperiode instellen", "overwrite_global_waiting_time": "Aangepaste afkoelperiode instellen",
@@ -1567,7 +1545,6 @@
"question_deleted": "Vraag verwijderd.", "question_deleted": "Vraag verwijderd.",
"question_duplicated": "Vraag dubbel gesteld.", "question_duplicated": "Vraag dubbel gesteld.",
"question_id_updated": "Vraag-ID bijgewerkt", "question_id_updated": "Vraag-ID bijgewerkt",
"question_number": "Vraag {number}",
"question_used_in_logic_warning_text": "Elementen uit dit blok worden gebruikt in een logische regel, weet je zeker dat je het wilt verwijderen?", "question_used_in_logic_warning_text": "Elementen uit dit blok worden gebruikt in een logische regel, weet je zeker dat je het wilt verwijderen?",
"question_used_in_logic_warning_title": "Logica-inconsistentie", "question_used_in_logic_warning_title": "Logica-inconsistentie",
"question_used_in_quota": "Deze vraag wordt gebruikt in het quotum “{quotaName}”", "question_used_in_quota": "Deze vraag wordt gebruikt in het quotum “{quotaName}”",
@@ -1626,7 +1603,7 @@
"response_limits_redirections_and_more": "Reactielimieten, omleidingen en meer.", "response_limits_redirections_and_more": "Reactielimieten, omleidingen en meer.",
"response_options": "Reactieopties", "response_options": "Reactieopties",
"roundness": "Rondheid", "roundness": "Rondheid",
"roundness_description": "Bepaalt hoe afgerond de hoeken zijn.", "roundness_description": "Bepaalt hoe afgerond de kaarthoeken zijn.",
"row_used_in_logic_error": "Deze rij wordt gebruikt in de logica van vraag {questionIndex}. Verwijder het eerst uit de logica.", "row_used_in_logic_error": "Deze rij wordt gebruikt in de logica van vraag {questionIndex}. Verwijder het eerst uit de logica.",
"rows": "Rijen", "rows": "Rijen",
"save_and_close": "Opslaan en sluiten", "save_and_close": "Opslaan en sluiten",
@@ -1672,7 +1649,6 @@
"survey_completed_subheading": "Deze gratis en open source-enquête is gesloten", "survey_completed_subheading": "Deze gratis en open source-enquête is gesloten",
"survey_display_settings": "Enquêteweergave-instellingen", "survey_display_settings": "Enquêteweergave-instellingen",
"survey_placement": "Enquête plaatsing", "survey_placement": "Enquête plaatsing",
"survey_styling": "Vorm styling",
"survey_trigger": "Enquêtetrigger", "survey_trigger": "Enquêtetrigger",
"switch_multi_language_on_to_get_started": "Schakel meertaligheid in om te beginnen 👉", "switch_multi_language_on_to_get_started": "Schakel meertaligheid in om te beginnen 👉",
"target_block_not_found": "Doelblok niet gevonden", "target_block_not_found": "Doelblok niet gevonden",
@@ -1735,7 +1711,7 @@
"pattern": "Komt overeen met regex-patroon", "pattern": "Komt overeen met regex-patroon",
"phone": "Is geldig telefoonnummer", "phone": "Is geldig telefoonnummer",
"rank_all_options": "Rangschik alle opties", "rank_all_options": "Rangschik alle opties",
"select_file_extensions": "Selecteer bestandsextensies", "select_file_extensions": "Selecteer bestandsextensies...",
"select_option": "Optie selecteren", "select_option": "Optie selecteren",
"start_date": "Startdatum", "start_date": "Startdatum",
"url": "Is geldige URL" "url": "Is geldige URL"
@@ -1761,9 +1737,9 @@
"waiting_time_across_surveys": "Afkoelperiode (voor alle enquêtes)", "waiting_time_across_surveys": "Afkoelperiode (voor alle enquêtes)",
"waiting_time_across_surveys_description": "Om enquêtemoeheid te voorkomen, kies hoe deze enquête omgaat met de workspace-brede afkoelperiode.", "waiting_time_across_surveys_description": "Om enquêtemoeheid te voorkomen, kies hoe deze enquête omgaat met de workspace-brede afkoelperiode.",
"welcome_message": "Welkomstbericht", "welcome_message": "Welkomstbericht",
"when": "Wanneer",
"without_a_filter_all_of_your_users_can_be_surveyed": "Zonder filter kunnen al uw gebruikers worden bevraagd.", "without_a_filter_all_of_your_users_can_be_surveyed": "Zonder filter kunnen al uw gebruikers worden bevraagd.",
"you_have_not_created_a_segment_yet": "U heeft nog geen segment aangemaakt", "you_have_not_created_a_segment_yet": "U heeft nog geen segment aangemaakt",
"you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "Je moet twee of meer talen hebben ingesteld in je werkruimte om met vertalingen te kunnen werken.",
"your_description_here_recall_information_with": "Uw beschrijving hier. Roep informatie op met @", "your_description_here_recall_information_with": "Uw beschrijving hier. Roep informatie op met @",
"your_question_here_recall_information_with": "Uw vraag hier. Roep informatie op met @", "your_question_here_recall_information_with": "Uw vraag hier. Roep informatie op met @",
"your_web_app": "Uw web-app", "your_web_app": "Uw web-app",
@@ -1833,7 +1809,7 @@
"disable_single_use_link_modal_button": "Schakel links voor eenmalig gebruik uit", "disable_single_use_link_modal_button": "Schakel links voor eenmalig gebruik uit",
"disable_single_use_link_modal_description": "Als u links voor eenmalig gebruik hebt gedeeld, kunnen deelnemers niet langer op de enquête reageren.", "disable_single_use_link_modal_description": "Als u links voor eenmalig gebruik hebt gedeeld, kunnen deelnemers niet langer op de enquête reageren.",
"generate_and_download_links": "Genereer en download links", "generate_and_download_links": "Genereer en download links",
"generate_links_error": "Eenmalige links konden niet worden gegenereerd. Werk alstublieft direct met de API.", "generate_links_error": "Er konden geen links voor eenmalig gebruik worden gegenereerd. Werk rechtstreeks met de API",
"multi_use_link": "Multifunctionele koppeling", "multi_use_link": "Multifunctionele koppeling",
"multi_use_link_description": "Verzamel meerdere reacties van anonieme respondenten met één link.", "multi_use_link_description": "Verzamel meerdere reacties van anonieme respondenten met één link.",
"multi_use_powers_other_channels_description": "Als u dit uitschakelt, worden deze andere distributiekanalen ook uitgeschakeld.", "multi_use_powers_other_channels_description": "Als u dit uitschakelt, worden deze andere distributiekanalen ook uitgeschakeld.",
@@ -1944,7 +1920,7 @@
} }
}, },
"summary": { "summary": {
"added_filter_for_responses_where_answer_to_question": "Filter toegevoegd voor reacties waar het antwoord op vraag {questionIdx} {filterComboBoxValue} - {filterValue} is", "added_filter_for_responses_where_answer_to_question": "Filter toegevoegd voor antwoorden waarbij het antwoord op vraag {questionIdx} {filterComboBoxValue} - {filterValue} is",
"added_filter_for_responses_where_answer_to_question_is_skipped": "Filter toegevoegd voor antwoorden waarbij het antwoord op vraag {questionIdx} wordt overgeslagen", "added_filter_for_responses_where_answer_to_question_is_skipped": "Filter toegevoegd voor antwoorden waarbij het antwoord op vraag {questionIdx} wordt overgeslagen",
"aggregated": "Geaggregeerd", "aggregated": "Geaggregeerd",
"all_responses_csv": "Alle reacties (CSV)", "all_responses_csv": "Alle reacties (CSV)",
@@ -1971,7 +1947,6 @@
"filtered_responses_excel": "Gefilterde reacties (Excel)", "filtered_responses_excel": "Gefilterde reacties (Excel)",
"generating_qr_code": "QR-code genereren", "generating_qr_code": "QR-code genereren",
"impressions": "Indrukken", "impressions": "Indrukken",
"impressions_identified_only": "Alleen weergaven van geïdentificeerde contacten worden getoond",
"impressions_tooltip": "Aantal keren dat de enquête is bekeken.", "impressions_tooltip": "Aantal keren dat de enquête is bekeken.",
"in_app": { "in_app": {
"connection_description": "De enquête wordt getoond aan gebruikers van uw website die voldoen aan de onderstaande criteria", "connection_description": "De enquête wordt getoond aan gebruikers van uw website die voldoen aan de onderstaande criteria",
@@ -2014,7 +1989,6 @@
"last_quarter": "Laatste kwartaal", "last_quarter": "Laatste kwartaal",
"last_year": "Vorig jaar", "last_year": "Vorig jaar",
"limit": "Beperken", "limit": "Beperken",
"no_identified_impressions": "Geen weergaven van geïdentificeerde contacten",
"no_responses_found": "Geen reacties gevonden", "no_responses_found": "Geen reacties gevonden",
"other_values_found": "Andere waarden gevonden", "other_values_found": "Andere waarden gevonden",
"overall": "Algemeen", "overall": "Algemeen",
@@ -2037,7 +2011,6 @@
"starts": "Begint", "starts": "Begint",
"starts_tooltip": "Aantal keren dat de enquête is gestart.", "starts_tooltip": "Aantal keren dat de enquête is gestart.",
"survey_reset_successfully": "Enquête opnieuw ingesteld! {responseCount} reacties en {displayCount} displays zijn verwijderd.", "survey_reset_successfully": "Enquête opnieuw ingesteld! {responseCount} reacties en {displayCount} displays zijn verwijderd.",
"survey_results": "Resultaten van {surveyName}",
"this_month": "Deze maand", "this_month": "Deze maand",
"this_quarter": "Dit kwartaal", "this_quarter": "Dit kwartaal",
"this_year": "Dit jaar", "this_year": "Dit jaar",
@@ -2157,7 +2130,7 @@
"add_background_color_description": "Voeg een achtergrondkleur toe aan de logocontainer.", "add_background_color_description": "Voeg een achtergrondkleur toe aan de logocontainer.",
"advanced_styling_field_border_radius": "Hoekradius", "advanced_styling_field_border_radius": "Hoekradius",
"advanced_styling_field_button_bg": "Knopachtergrond", "advanced_styling_field_button_bg": "Knopachtergrond",
"advanced_styling_field_button_bg_description": "Vult de “Volgende” / “Verzenden” knop.", "advanced_styling_field_button_bg_description": "Vult de volgende/verzend-knop.",
"advanced_styling_field_button_border_radius_description": "Rondt de knophoeken af.", "advanced_styling_field_button_border_radius_description": "Rondt de knophoeken af.",
"advanced_styling_field_button_font_size_description": "Schaalt de tekst van het knoplabel.", "advanced_styling_field_button_font_size_description": "Schaalt de tekst van het knoplabel.",
"advanced_styling_field_button_font_weight_description": "Maakt knoptekst lichter of vetter.", "advanced_styling_field_button_font_weight_description": "Maakt knoptekst lichter of vetter.",
@@ -2171,7 +2144,7 @@
"advanced_styling_field_description_size": "Lettergrootte beschrijving", "advanced_styling_field_description_size": "Lettergrootte beschrijving",
"advanced_styling_field_description_size_description": "Schaalt de beschrijvingstekst.", "advanced_styling_field_description_size_description": "Schaalt de beschrijvingstekst.",
"advanced_styling_field_description_weight": "Letterdikte beschrijving", "advanced_styling_field_description_weight": "Letterdikte beschrijving",
"advanced_styling_field_description_weight_description": "Maakt de beschrijvingstekst lichter of vetter.", "advanced_styling_field_description_weight_description": "Maakt beschrijvingstekst lichter of vetter.",
"advanced_styling_field_font_size": "Lettergrootte", "advanced_styling_field_font_size": "Lettergrootte",
"advanced_styling_field_font_weight": "Letterdikte", "advanced_styling_field_font_weight": "Letterdikte",
"advanced_styling_field_headline_color": "Kopkleur", "advanced_styling_field_headline_color": "Kopkleur",
@@ -2194,8 +2167,6 @@
"advanced_styling_field_input_text_description": "Kleurt de getypte tekst in invoervelden.", "advanced_styling_field_input_text_description": "Kleurt de getypte tekst in invoervelden.",
"advanced_styling_field_option_bg": "Achtergrond", "advanced_styling_field_option_bg": "Achtergrond",
"advanced_styling_field_option_bg_description": "Vult de optie-items.", "advanced_styling_field_option_bg_description": "Vult de optie-items.",
"advanced_styling_field_option_border": "Randkleur",
"advanced_styling_field_option_border_description": "Omlijnt radio- en checkboxopties.",
"advanced_styling_field_option_border_radius_description": "Rondt de hoeken van opties af.", "advanced_styling_field_option_border_radius_description": "Rondt de hoeken van opties af.",
"advanced_styling_field_option_font_size_description": "Schaalt de tekst van optielabels.", "advanced_styling_field_option_font_size_description": "Schaalt de tekst van optielabels.",
"advanced_styling_field_option_label": "Labelkleur", "advanced_styling_field_option_label": "Labelkleur",
@@ -2250,7 +2221,7 @@
"show_powered_by_formbricks": "Toon 'Powered by Formbricks' handtekening", "show_powered_by_formbricks": "Toon 'Powered by Formbricks' handtekening",
"styling_updated_successfully": "Styling succesvol bijgewerkt", "styling_updated_successfully": "Styling succesvol bijgewerkt",
"suggest_colors": "Kleuren voorstellen", "suggest_colors": "Kleuren voorstellen",
"suggested_colors_applied_please_save": "Voorgestelde kleuren succesvol gegenereerd. Druk op Opslaan om de wijzigingen te behouden.", "suggested_colors_applied_please_save": "Voorgestelde kleuren succesvol gegenereerd. Druk op \"Opslaan\" om de wijzigingen te behouden.",
"theme": "Thema", "theme": "Thema",
"theme_settings_description": "Maak een stijlthema voor alle enquêtes. Je kunt aangepaste styling inschakelen voor elke enquête." "theme_settings_description": "Maak een stijlthema voor alle enquêtes. Je kunt aangepaste styling inschakelen voor elke enquête."
}, },
@@ -2334,7 +2305,7 @@
"check_inbox_or_spam": "Controleer ook uw spammap als u de e-mail niet in uw inbox ziet.", "check_inbox_or_spam": "Controleer ook uw spammap als u de e-mail niet in uw inbox ziet.",
"completed": "Deze enquête is gesloten.", "completed": "Deze enquête is gesloten.",
"create_your_own": "Creëer uw eigen open source-enquête", "create_your_own": "Creëer uw eigen open source-enquête",
"enter_pin": "Deze enquête is beveiligd. Voer hieronder de pincode in.", "enter_pin": "Deze enquête is beveiligd. Voer hieronder de pincode in",
"just_curious": "Gewoon nieuwsgierig?", "just_curious": "Gewoon nieuwsgierig?",
"link_invalid": "Aan deze enquête kan alleen op uitnodiging worden deelgenomen.", "link_invalid": "Aan deze enquête kan alleen op uitnodiging worden deelgenomen.",
"paused": "Deze enquête is tijdelijk onderbroken.", "paused": "Deze enquête is tijdelijk onderbroken.",
@@ -2405,16 +2376,6 @@
"alignment_and_engagement_survey_question_4_headline": "Hoe kan het bedrijf de afstemming van zijn visie en strategie verbeteren?", "alignment_and_engagement_survey_question_4_headline": "Hoe kan het bedrijf de afstemming van zijn visie en strategie verbeteren?",
"alignment_and_engagement_survey_question_4_placeholder": "Typ hier uw antwoord...", "alignment_and_engagement_survey_question_4_placeholder": "Typ hier uw antwoord...",
"back": "Rug", "back": "Rug",
"block_1": "Blok 1",
"block_10": "Blok 10",
"block_2": "Blok 2",
"block_3": "Blok 3",
"block_4": "Blok 4",
"block_5": "Blok 5",
"block_6": "Blok 6",
"block_7": "Blok 7",
"block_8": "Blok 8",
"block_9": "Blok 9",
"book_interview": "Boek interview", "book_interview": "Boek interview",
"build_product_roadmap_description": "Identificeer het ENE wat uw gebruikers het liefst willen en bouw het.", "build_product_roadmap_description": "Identificeer het ENE wat uw gebruikers het liefst willen en bouw het.",
"build_product_roadmap_name": "Productroadmap opstellen", "build_product_roadmap_name": "Productroadmap opstellen",
@@ -2622,6 +2583,7 @@
"csat_survey_question_3_headline": "Euh, sorry! Kunnen we iets doen om uw ervaring te verbeteren?", "csat_survey_question_3_headline": "Euh, sorry! Kunnen we iets doen om uw ervaring te verbeteren?",
"csat_survey_question_3_placeholder": "Typ hier uw antwoord...", "csat_survey_question_3_placeholder": "Typ hier uw antwoord...",
"cta_description": "Geef informatie weer en vraag gebruikers om een specifieke actie te ondernemen", "cta_description": "Geef informatie weer en vraag gebruikers om een specifieke actie te ondernemen",
"custom_survey_block_1_name": "Blok 1",
"custom_survey_description": "Maak een enquête zonder sjabloon.", "custom_survey_description": "Maak een enquête zonder sjabloon.",
"custom_survey_name": "Begin helemaal opnieuw", "custom_survey_name": "Begin helemaal opnieuw",
"custom_survey_question_1_headline": "Wat zou je willen weten?", "custom_survey_question_1_headline": "Wat zou je willen weten?",
@@ -3024,9 +2986,6 @@
"preview_survey_question_2_choice_2_label": "Nee, dank je!", "preview_survey_question_2_choice_2_label": "Nee, dank je!",
"preview_survey_question_2_headline": "Wil je op de hoogte blijven?", "preview_survey_question_2_headline": "Wil je op de hoogte blijven?",
"preview_survey_question_2_subheader": "Dit is een voorbeeldbeschrijving.", "preview_survey_question_2_subheader": "Dit is een voorbeeldbeschrijving.",
"preview_survey_question_open_text_headline": "Wil je nog iets delen?",
"preview_survey_question_open_text_placeholder": "Typ hier je antwoord...",
"preview_survey_question_open_text_subheader": "Je feedback helpt ons verbeteren.",
"preview_survey_welcome_card_headline": "Welkom!", "preview_survey_welcome_card_headline": "Welkom!",
"prioritize_features_description": "Identificeer functies die uw gebruikers het meest en het minst nodig hebben.", "prioritize_features_description": "Identificeer functies die uw gebruikers het meest en het minst nodig hebben.",
"prioritize_features_name": "Geef prioriteit aan functies", "prioritize_features_name": "Geef prioriteit aan functies",
@@ -3096,7 +3055,7 @@
"professional_development_survey_question_2_choice_5": "Individueel onderzoek", "professional_development_survey_question_2_choice_5": "Individueel onderzoek",
"professional_development_survey_question_2_choice_6": "Ander", "professional_development_survey_question_2_choice_6": "Ander",
"professional_development_survey_question_2_headline": "Welke soorten professionele ontwikkelingsactiviteiten zouden volgens u het meest waardevol zijn voor uw groei?", "professional_development_survey_question_2_headline": "Welke soorten professionele ontwikkelingsactiviteiten zouden volgens u het meest waardevol zijn voor uw groei?",
"professional_development_survey_question_2_subheader": "Selecteer alles wat van toepassing is:", "professional_development_survey_question_2_subheader": "Selecteer alles wat van toepassing is",
"professional_development_survey_question_3_choice_1": "Ja", "professional_development_survey_question_3_choice_1": "Ja",
"professional_development_survey_question_3_choice_2": "Nee", "professional_development_survey_question_3_choice_2": "Nee",
"professional_development_survey_question_3_headline": "Heeft u in het verleden tijd besteed aan uw professionele ontwikkeling?", "professional_development_survey_question_3_headline": "Heeft u in het verleden tijd besteed aan uw professionele ontwikkeling?",
@@ -3275,18 +3234,5 @@
"usability_question_9_headline": "Ik voelde me zelfverzekerd tijdens het gebruik van het systeem.", "usability_question_9_headline": "Ik voelde me zelfverzekerd tijdens het gebruik van het systeem.",
"usability_rating_description": "Meet de waargenomen bruikbaarheid door gebruikers te vragen hun ervaring met uw product te beoordelen met behulp van een gestandaardiseerde enquête met tien vragen.", "usability_rating_description": "Meet de waargenomen bruikbaarheid door gebruikers te vragen hun ervaring met uw product te beoordelen met behulp van een gestandaardiseerde enquête met tien vragen.",
"usability_score_name": "Systeembruikbaarheidsscore (SUS)" "usability_score_name": "Systeembruikbaarheidsscore (SUS)"
},
"workflows": {
"coming_soon_description": "Bedankt voor het delen van je workflow-idee met ons! We zijn momenteel bezig met het ontwerpen van deze functie en jouw feedback helpt ons om precies te bouwen wat je nodig hebt.",
"coming_soon_title": "We zijn er bijna!",
"follow_up_label": "Is er nog iets dat je wilt toevoegen?",
"follow_up_placeholder": "Welke specifieke taken wil je automatiseren? Zijn er tools of integraties die je wilt meenemen?",
"generate_button": "Genereer workflow",
"heading": "Welke workflow wil je maken?",
"placeholder": "Beschrijf de workflow die je wilt genereren…",
"subheading": "Genereer je workflow in enkele seconden.",
"submit_button": "Voeg details toe",
"thank_you_description": "Jouw input helpt ons om de Workflows-functie te bouwen die jij echt nodig hebt. We houden je op de hoogte van onze voortgang.",
"thank_you_title": "Bedankt voor je feedback!"
} }
} }

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