mirror of
https://github.com/formbricks/formbricks.git
synced 2026-05-20 19:48:52 -05:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c6cbcd8280 | |||
| 23ef11d228 | |||
| 6c07e71b47 | |||
| 242b003048 |
+2
-7
@@ -184,13 +184,8 @@ ENTERPRISE_LICENSE_KEY=
|
|||||||
# Ignore Rate Limiting across the Formbricks app
|
# Ignore Rate Limiting across the Formbricks app
|
||||||
# RATE_LIMITING_DISABLED=1
|
# RATE_LIMITING_DISABLED=1
|
||||||
|
|
||||||
# OpenTelemetry OTLP endpoint (base URL, exporters append /v1/traces and /v1/metrics)
|
# OpenTelemetry URL for tracing
|
||||||
# OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
|
# OPENTELEMETRY_LISTENER_URL=http://localhost:4318/v1/traces
|
||||||
# OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
|
|
||||||
# OTEL_SERVICE_NAME=formbricks
|
|
||||||
# OTEL_RESOURCE_ATTRIBUTES=deployment.environment=development
|
|
||||||
# OTEL_TRACES_SAMPLER=parentbased_traceidratio
|
|
||||||
# OTEL_TRACES_SAMPLER_ARG=1
|
|
||||||
|
|
||||||
# Unsplash API Key
|
# Unsplash API Key
|
||||||
UNSPLASH_ACCESS_KEY=
|
UNSPLASH_ACCESS_KEY=
|
||||||
|
|||||||
@@ -65,8 +65,8 @@ jobs:
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
echo "Updating Chart.yaml with version: ${VERSION}"
|
echo "Updating Chart.yaml with version: ${VERSION}"
|
||||||
yq -i ".version = \"${VERSION}\"" charts/formbricks/Chart.yaml
|
yq -i ".version = \"${VERSION}\"" helm-chart/Chart.yaml
|
||||||
yq -i ".appVersion = \"${VERSION}\"" charts/formbricks/Chart.yaml
|
yq -i ".appVersion = \"${VERSION}\"" helm-chart/Chart.yaml
|
||||||
|
|
||||||
echo "✅ Successfully updated Chart.yaml"
|
echo "✅ Successfully updated Chart.yaml"
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ jobs:
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
echo "Packaging Helm chart version: ${VERSION}"
|
echo "Packaging Helm chart version: ${VERSION}"
|
||||||
helm package ./charts/formbricks
|
helm package ./helm-chart
|
||||||
|
|
||||||
echo "✅ Successfully packaged formbricks-${VERSION}.tgz"
|
echo "✅ Successfully packaged formbricks-${VERSION}.tgz"
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ on:
|
|||||||
merge_group:
|
merge_group:
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
pull-requests: read
|
|
||||||
jobs:
|
jobs:
|
||||||
sonarqube:
|
sonarqube:
|
||||||
name: SonarQube
|
name: SonarQube
|
||||||
@@ -51,9 +50,6 @@ jobs:
|
|||||||
pnpm test:coverage
|
pnpm test:coverage
|
||||||
- name: SonarQube Scan
|
- name: SonarQube Scan
|
||||||
uses: SonarSource/sonarqube-scan-action@2500896589ef8f7247069a56136f8dc177c27ccf
|
uses: SonarSource/sonarqube-scan-action@2500896589ef8f7247069a56136f8dc177c27ccf
|
||||||
with:
|
|
||||||
args: >
|
|
||||||
-Dsonar.verbose=true
|
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
|
||||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||||
|
|||||||
@@ -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:
|
||||||
@@ -22,39 +32,32 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Check for relevant changes
|
- name: Setup Node.js
|
||||||
id: changes
|
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
|
||||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
|
||||||
with:
|
with:
|
||||||
filters: |
|
node-version: 18
|
||||||
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 pnpm
|
||||||
if: steps.changes.outputs.translations == 'true'
|
uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d # v3.0.0
|
||||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af
|
|
||||||
with:
|
with:
|
||||||
node-version: 22.x
|
version: 9.15.9
|
||||||
|
|
||||||
- name: Install pnpm
|
|
||||||
if: steps.changes.outputs.translations == 'true'
|
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
if: steps.changes.outputs.translations == 'true'
|
run: pnpm install --frozen-lockfile
|
||||||
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 ""
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
**/.next/
|
**/.next/
|
||||||
**/out/
|
**/out/
|
||||||
**/build
|
**/build
|
||||||
**/next-env.d.ts
|
|
||||||
|
|
||||||
# node
|
# node
|
||||||
**/dist/
|
**/dist/
|
||||||
@@ -64,5 +63,3 @@ packages/ios/FormbricksSDK/FormbricksSDK.xcodeproj/project.xcworkspace/xcuserdat
|
|||||||
.cursorrules
|
.cursorrules
|
||||||
i18n.cache
|
i18n.cache
|
||||||
stats.html
|
stats.html
|
||||||
# next-agents-md
|
|
||||||
.next-docs/
|
|
||||||
|
|||||||
+40
-1
@@ -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
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
# Fix Summary: Custom Script Error Handling (FORMBRICKS-P5)
|
|
||||||
|
|
||||||
## Problem
|
|
||||||
User-provided custom scripts injected via `customHeadScripts` were calling deprecated MetaMask internal APIs (`window.ethereum._handleChainChanged`), causing unhandled TypeErrors that:
|
|
||||||
- Were reported to Sentry as production errors
|
|
||||||
- Could potentially break the survey experience for users
|
|
||||||
- Occurred when the survey page loaded with certain browser extensions (MetaMask)
|
|
||||||
|
|
||||||
## Root Cause
|
|
||||||
The `CustomScriptsInjector` component was correctly injecting user-provided scripts, but had no protection against runtime errors that occurred *after* the scripts were executed. The existing try-catch block only caught errors during the injection process itself, not runtime errors from the injected code.
|
|
||||||
|
|
||||||
## Solution
|
|
||||||
Enhanced error handling in the `CustomScriptsInjector` component to wrap all user-provided scripts in defensive error handling:
|
|
||||||
|
|
||||||
### Changes Made
|
|
||||||
1. **Inline Script Wrapping** (lines 64-72):
|
|
||||||
- All inline script content is now wrapped in try-catch blocks
|
|
||||||
- Runtime errors are caught and logged to console with `[Formbricks]` prefix
|
|
||||||
- Errors don't break the survey or propagate to error tracking
|
|
||||||
|
|
||||||
2. **External Script Error Handling** (lines 75-77):
|
|
||||||
- Added `onerror` event handlers to all external scripts loaded via `src` attribute
|
|
||||||
- Loading errors are logged to console but don't break functionality
|
|
||||||
|
|
||||||
3. **Documentation**:
|
|
||||||
- Updated JSDoc to clearly state that user scripts are error-isolated
|
|
||||||
- Added inline comments explaining the defensive programming approach
|
|
||||||
|
|
||||||
## Benefits
|
|
||||||
- ✅ Prevents third-party script errors from breaking surveys
|
|
||||||
- ✅ Reduces noise in error tracking (Sentry)
|
|
||||||
- ✅ Maintains debuggability via console warnings
|
|
||||||
- ✅ No breaking changes to existing functionality
|
|
||||||
- ✅ Works for both inline and external scripts
|
|
||||||
|
|
||||||
## Testing Considerations
|
|
||||||
- User scripts with errors will execute up to the point of failure, then stop gracefully
|
|
||||||
- Error messages are visible in browser console for debugging by self-hosted admins
|
|
||||||
- Survey functionality remains intact even if custom scripts fail completely
|
|
||||||
|
|
||||||
## Example
|
|
||||||
**Before**: User script calling `window.ethereum._handleChainChanged()` would throw an unhandled TypeError
|
|
||||||
|
|
||||||
**After**: The error is caught, logged to console as a warning, and the survey continues to function normally
|
|
||||||
|
|
||||||
## Files Modified
|
|
||||||
- `apps/web/modules/survey/link/components/custom-scripts-injector.tsx`
|
|
||||||
|
|
||||||
## Commit
|
|
||||||
- `fix: wrap custom scripts in try-catch to prevent runtime errors` (Fixes FORMBRICKS-P5)
|
|
||||||
@@ -1,4 +1,20 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
extends: ["@formbricks/eslint-config/legacy-next.js"],
|
extends: ["@formbricks/eslint-config/legacy-next.js"],
|
||||||
ignorePatterns: ["**/package.json", "**/tsconfig.json"],
|
ignorePatterns: ["**/package.json", "**/tsconfig.json"],
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ["locales/*.json"],
|
||||||
|
plugins: ["i18n-json"],
|
||||||
|
rules: {
|
||||||
|
"i18n-json/identical-keys": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
filePath: require("path").join(__dirname, "locales", "en-US.json"),
|
||||||
|
checkExtraKeys: false,
|
||||||
|
checkMissingKeys: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
+18
-26
@@ -1,4 +1,4 @@
|
|||||||
FROM node:24-alpine3.23 AS base
|
FROM node:22-alpine3.22 AS base
|
||||||
|
|
||||||
#
|
#
|
||||||
## step 1: Prune monorepo
|
## step 1: Prune monorepo
|
||||||
@@ -20,7 +20,7 @@ FROM base AS installer
|
|||||||
# Enable corepack and prepare pnpm
|
# Enable corepack and prepare pnpm
|
||||||
RUN npm install --ignore-scripts -g corepack@latest
|
RUN npm install --ignore-scripts -g corepack@latest
|
||||||
RUN corepack enable
|
RUN corepack enable
|
||||||
RUN corepack prepare pnpm@10.28.2 --activate
|
RUN corepack prepare pnpm@9.15.9 --activate
|
||||||
|
|
||||||
# Install necessary build tools and compilers
|
# Install necessary build tools and compilers
|
||||||
RUN apk update && apk add --no-cache cmake g++ gcc jq make openssl-dev python3
|
RUN apk update && apk add --no-cache cmake g++ gcc jq make openssl-dev python3
|
||||||
@@ -69,14 +69,20 @@ RUN --mount=type=secret,id=database_url \
|
|||||||
--mount=type=secret,id=sentry_auth_token \
|
--mount=type=secret,id=sentry_auth_token \
|
||||||
/tmp/read-secrets.sh pnpm build --filter=@formbricks/web...
|
/tmp/read-secrets.sh pnpm build --filter=@formbricks/web...
|
||||||
|
|
||||||
|
# Extract Prisma version
|
||||||
|
RUN jq -r '.devDependencies.prisma' packages/database/package.json > /prisma_version.txt
|
||||||
|
|
||||||
#
|
#
|
||||||
## step 3: setup production runner
|
## step 3: setup production runner
|
||||||
#
|
#
|
||||||
FROM base AS runner
|
FROM base AS runner
|
||||||
|
|
||||||
# Update npm to latest, then create user
|
RUN npm install --ignore-scripts -g corepack@latest && \
|
||||||
# Note: npm's bundled tar has a known vulnerability but npm is only used during build, not at runtime
|
corepack enable
|
||||||
RUN npm install --ignore-scripts -g npm@latest \
|
|
||||||
|
RUN apk add --no-cache curl \
|
||||||
|
&& apk add --no-cache supercronic \
|
||||||
|
# && addgroup --system --gid 1001 nodejs \
|
||||||
&& addgroup -S nextjs \
|
&& addgroup -S nextjs \
|
||||||
&& adduser -S -u 1001 -G nextjs nextjs
|
&& adduser -S -u 1001 -G nextjs nextjs
|
||||||
|
|
||||||
@@ -107,13 +113,15 @@ RUN chown nextjs:nextjs ./packages/database/schema.prisma && chmod 644 ./package
|
|||||||
COPY --from=installer /app/packages/database/dist ./packages/database/dist
|
COPY --from=installer /app/packages/database/dist ./packages/database/dist
|
||||||
RUN chown -R nextjs:nextjs ./packages/database/dist && chmod -R 755 ./packages/database/dist
|
RUN chown -R nextjs:nextjs ./packages/database/dist && chmod -R 755 ./packages/database/dist
|
||||||
|
|
||||||
# Copy prisma client packages
|
|
||||||
COPY --from=installer /app/node_modules/@prisma/client ./node_modules/@prisma/client
|
COPY --from=installer /app/node_modules/@prisma/client ./node_modules/@prisma/client
|
||||||
RUN chown -R nextjs:nextjs ./node_modules/@prisma/client && chmod -R 755 ./node_modules/@prisma/client
|
RUN chown -R nextjs:nextjs ./node_modules/@prisma/client && chmod -R 755 ./node_modules/@prisma/client
|
||||||
|
|
||||||
COPY --from=installer /app/node_modules/.prisma ./node_modules/.prisma
|
COPY --from=installer /app/node_modules/.prisma ./node_modules/.prisma
|
||||||
RUN chown -R nextjs:nextjs ./node_modules/.prisma && chmod -R 755 ./node_modules/.prisma
|
RUN chown -R nextjs:nextjs ./node_modules/.prisma && chmod -R 755 ./node_modules/.prisma
|
||||||
|
|
||||||
|
COPY --from=installer /prisma_version.txt .
|
||||||
|
RUN chown nextjs:nextjs ./prisma_version.txt && chmod 644 ./prisma_version.txt
|
||||||
|
|
||||||
COPY --from=installer /app/node_modules/@paralleldrive/cuid2 ./node_modules/@paralleldrive/cuid2
|
COPY --from=installer /app/node_modules/@paralleldrive/cuid2 ./node_modules/@paralleldrive/cuid2
|
||||||
RUN chmod -R 755 ./node_modules/@paralleldrive/cuid2
|
RUN chmod -R 755 ./node_modules/@paralleldrive/cuid2
|
||||||
|
|
||||||
@@ -126,25 +134,7 @@ RUN chmod -R 755 ./node_modules/@noble/hashes
|
|||||||
COPY --from=installer /app/node_modules/zod ./node_modules/zod
|
COPY --from=installer /app/node_modules/zod ./node_modules/zod
|
||||||
RUN chmod -R 755 ./node_modules/zod
|
RUN chmod -R 755 ./node_modules/zod
|
||||||
|
|
||||||
# Pino loads transport code in worker threads via dynamic require().
|
RUN npm install -g prisma@6
|
||||||
# Next.js file tracing only traces static imports, missing runtime-loaded files
|
|
||||||
# (e.g. pino/lib/transport-stream.js, transport targets).
|
|
||||||
# Copy the full packages to ensure all runtime files are available.
|
|
||||||
COPY --from=installer /app/node_modules/pino ./node_modules/pino
|
|
||||||
RUN chmod -R 755 ./node_modules/pino
|
|
||||||
|
|
||||||
COPY --from=installer /app/node_modules/pino-opentelemetry-transport ./node_modules/pino-opentelemetry-transport
|
|
||||||
RUN chmod -R 755 ./node_modules/pino-opentelemetry-transport
|
|
||||||
|
|
||||||
COPY --from=installer /app/node_modules/pino-abstract-transport ./node_modules/pino-abstract-transport
|
|
||||||
RUN chmod -R 755 ./node_modules/pino-abstract-transport
|
|
||||||
|
|
||||||
COPY --from=installer /app/node_modules/otlp-logger ./node_modules/otlp-logger
|
|
||||||
RUN chmod -R 755 ./node_modules/otlp-logger
|
|
||||||
|
|
||||||
# Install prisma CLI globally for database migrations and fix permissions for nextjs user
|
|
||||||
RUN npm install --ignore-scripts -g prisma@6 \
|
|
||||||
&& chown -R nextjs:nextjs /usr/local/lib/node_modules/prisma
|
|
||||||
|
|
||||||
# Create a startup script to handle the conditional logic
|
# Create a startup script to handle the conditional logic
|
||||||
COPY --from=installer /app/apps/web/scripts/docker/next-start.sh /home/nextjs/start.sh
|
COPY --from=installer /app/apps/web/scripts/docker/next-start.sh /home/nextjs/start.sh
|
||||||
@@ -154,8 +144,10 @@ EXPOSE 3000
|
|||||||
ENV HOSTNAME="0.0.0.0"
|
ENV HOSTNAME="0.0.0.0"
|
||||||
USER nextjs
|
USER nextjs
|
||||||
|
|
||||||
|
# Prepare pnpm as the nextjs user to ensure it's available at runtime
|
||||||
# Prepare volumes for uploads and SAML connections
|
# Prepare volumes for uploads and SAML connections
|
||||||
RUN mkdir -p /home/nextjs/apps/web/uploads/ && \
|
RUN corepack prepare pnpm@9.15.9 --activate && \
|
||||||
|
mkdir -p /home/nextjs/apps/web/uploads/ && \
|
||||||
mkdir -p /home/nextjs/apps/web/saml-connection
|
mkdir -p /home/nextjs/apps/web/saml-connection
|
||||||
|
|
||||||
VOLUME /home/nextjs/apps/web/uploads/
|
VOLUME /home/nextjs/apps/web/uploads/
|
||||||
|
|||||||
+1
-1
@@ -25,7 +25,7 @@ const mockProject: TProject = {
|
|||||||
},
|
},
|
||||||
placement: "bottomRight",
|
placement: "bottomRight",
|
||||||
clickOutsideClose: true,
|
clickOutsideClose: true,
|
||||||
overlay: "none",
|
darkOverlay: false,
|
||||||
environments: [],
|
environments: [],
|
||||||
languages: [],
|
languages: [],
|
||||||
logo: null,
|
logo: null,
|
||||||
|
|||||||
+3
-12
@@ -3,7 +3,7 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useMemo, useState } from "react";
|
import { useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -17,7 +17,6 @@ import {
|
|||||||
import { createProjectAction } from "@/app/(app)/environments/[environmentId]/actions";
|
import { createProjectAction } from "@/app/(app)/environments/[environmentId]/actions";
|
||||||
import { previewSurvey } from "@/app/lib/templates";
|
import { previewSurvey } from "@/app/lib/templates";
|
||||||
import { FORMBRICKS_SURVEYS_FILTERS_KEY_LS } from "@/lib/localStorage";
|
import { FORMBRICKS_SURVEYS_FILTERS_KEY_LS } from "@/lib/localStorage";
|
||||||
import { buildStylingFromBrandColor } from "@/lib/styling/constants";
|
|
||||||
import { getFormattedErrorMessage } from "@/lib/utils/helper";
|
import { getFormattedErrorMessage } from "@/lib/utils/helper";
|
||||||
import { TOrganizationTeam } from "@/modules/ee/teams/project-teams/types/team";
|
import { TOrganizationTeam } from "@/modules/ee/teams/project-teams/types/team";
|
||||||
import { CreateTeamModal } from "@/modules/ee/teams/team-list/components/create-team-modal";
|
import { CreateTeamModal } from "@/modules/ee/teams/team-list/components/create-team-modal";
|
||||||
@@ -65,17 +64,10 @@ export const ProjectSettings = ({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const addProject = async (data: TProjectUpdateInput) => {
|
const addProject = async (data: TProjectUpdateInput) => {
|
||||||
try {
|
try {
|
||||||
// Build the full styling from the chosen brand color so all derived
|
|
||||||
// colours (question, button, input, option, progress, etc.) are persisted.
|
|
||||||
// Without this, only brandColor is saved and the look-and-feel page falls
|
|
||||||
// back to STYLE_DEFAULTS computed from the default brand (#64748b).
|
|
||||||
const fullStyling = buildStylingFromBrandColor(data.styling?.brandColor?.light);
|
|
||||||
|
|
||||||
const createProjectResponse = await createProjectAction({
|
const createProjectResponse = await createProjectAction({
|
||||||
organizationId,
|
organizationId,
|
||||||
data: {
|
data: {
|
||||||
...data,
|
...data,
|
||||||
styling: fullStyling,
|
|
||||||
config: { channel, industry },
|
config: { channel, industry },
|
||||||
teamIds: data.teamIds,
|
teamIds: data.teamIds,
|
||||||
},
|
},
|
||||||
@@ -120,7 +112,6 @@ export const ProjectSettings = ({
|
|||||||
const projectName = form.watch("name");
|
const projectName = form.watch("name");
|
||||||
const logoUrl = form.watch("logo.url");
|
const logoUrl = form.watch("logo.url");
|
||||||
const brandColor = form.watch("styling.brandColor.light") ?? defaultBrandColor;
|
const brandColor = form.watch("styling.brandColor.light") ?? defaultBrandColor;
|
||||||
const previewStyling = useMemo(() => buildStylingFromBrandColor(brandColor), [brandColor]);
|
|
||||||
const { isSubmitting } = form.formState;
|
const { isSubmitting } = form.formState;
|
||||||
|
|
||||||
const organizationTeamsOptions = organizationTeams.map((team) => ({
|
const organizationTeamsOptions = organizationTeams.map((team) => ({
|
||||||
@@ -235,7 +226,7 @@ export const ProjectSettings = ({
|
|||||||
alt="Logo"
|
alt="Logo"
|
||||||
width={256}
|
width={256}
|
||||||
height={56}
|
height={56}
|
||||||
className="absolute left-2 top-2 -mb-6 h-20 w-auto max-w-64 rounded-lg border object-contain p-1"
|
className="absolute top-2 left-2 -mb-6 h-20 w-auto max-w-64 rounded-lg border object-contain p-1"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<p className="text-sm text-slate-400">{t("common.preview")}</p>
|
<p className="text-sm text-slate-400">{t("common.preview")}</p>
|
||||||
@@ -244,7 +235,7 @@ export const ProjectSettings = ({
|
|||||||
appUrl={publicDomain}
|
appUrl={publicDomain}
|
||||||
isPreviewMode={true}
|
isPreviewMode={true}
|
||||||
survey={previewSurvey(projectName || "my Product", t)}
|
survey={previewSurvey(projectName || "my Product", t)}
|
||||||
styling={previewStyling}
|
styling={{ brandColor: { light: brandColor } }}
|
||||||
isBrandingEnabled={false}
|
isBrandingEnabled={false}
|
||||||
languageCode="default"
|
languageCode="default"
|
||||||
onFileUpload={async (file) => file.name}
|
onFileUpload={async (file) => file.name}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { ZId } from "@formbricks/types/common";
|
import { ZId } from "@formbricks/types/common";
|
||||||
import { AuthorizationError, OperationNotAllowedError } from "@formbricks/types/errors";
|
import { OperationNotAllowedError } from "@formbricks/types/errors";
|
||||||
import { ZProjectUpdateInput } from "@formbricks/types/project";
|
import { ZProjectUpdateInput } from "@formbricks/types/project";
|
||||||
import { getMembershipByUserIdOrganizationId } from "@/lib/membership/service";
|
import { getMembershipByUserIdOrganizationId } from "@/lib/membership/service";
|
||||||
import { getOrganization } from "@/lib/organization/service";
|
import { getOrganization } from "@/lib/organization/service";
|
||||||
@@ -138,7 +138,7 @@ export const getProjectsForSwitcherAction = authenticatedActionClient
|
|||||||
// Need membership for getProjectsByUserId (1 DB query)
|
// Need membership for getProjectsByUserId (1 DB query)
|
||||||
const membership = await getMembershipByUserIdOrganizationId(ctx.user.id, parsedInput.organizationId);
|
const membership = await getMembershipByUserIdOrganizationId(ctx.user.id, parsedInput.organizationId);
|
||||||
if (!membership) {
|
if (!membership) {
|
||||||
throw new AuthorizationError("Membership not found");
|
throw new Error("Membership not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
return await getProjectsByUserId(ctx.user.id, membership);
|
return await getProjectsByUserId(ctx.user.id, membership);
|
||||||
|
|||||||
@@ -109,10 +109,7 @@ export const MainNavigation = ({
|
|||||||
href: `/environments/${environment.id}/contacts`,
|
href: `/environments/${environment.id}/contacts`,
|
||||||
name: t("common.contacts"),
|
name: t("common.contacts"),
|
||||||
icon: UserIcon,
|
icon: UserIcon,
|
||||||
isActive:
|
isActive: pathname?.includes("/contacts") || pathname?.includes("/segments"),
|
||||||
pathname?.includes("/contacts") ||
|
|
||||||
pathname?.includes("/segments") ||
|
|
||||||
pathname?.includes("/attributes"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: t("common.configuration"),
|
name: t("common.configuration"),
|
||||||
|
|||||||
+4
-7
@@ -30,7 +30,7 @@ export const NotificationSwitch = ({
|
|||||||
const isChecked =
|
const isChecked =
|
||||||
notificationType === "unsubscribedOrganizationIds"
|
notificationType === "unsubscribedOrganizationIds"
|
||||||
? !notificationSettings.unsubscribedOrganizationIds?.includes(surveyOrProjectOrOrganizationId)
|
? !notificationSettings.unsubscribedOrganizationIds?.includes(surveyOrProjectOrOrganizationId)
|
||||||
: notificationSettings[notificationType]?.[surveyOrProjectOrOrganizationId] === true;
|
: notificationSettings[notificationType][surveyOrProjectOrOrganizationId] === true;
|
||||||
|
|
||||||
const handleSwitchChange = async () => {
|
const handleSwitchChange = async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
@@ -49,11 +49,8 @@ export const NotificationSwitch = ({
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
updatedNotificationSettings[notificationType] = {
|
updatedNotificationSettings[notificationType][surveyOrProjectOrOrganizationId] =
|
||||||
...updatedNotificationSettings[notificationType],
|
!updatedNotificationSettings[notificationType][surveyOrProjectOrOrganizationId];
|
||||||
[surveyOrProjectOrOrganizationId]:
|
|
||||||
!updatedNotificationSettings[notificationType]?.[surveyOrProjectOrOrganizationId],
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedNotificationSettingsActionResponse = await updateNotificationSettingsAction({
|
const updatedNotificationSettingsActionResponse = await updateNotificationSettingsAction({
|
||||||
@@ -81,7 +78,7 @@ export const NotificationSwitch = ({
|
|||||||
) {
|
) {
|
||||||
switch (notificationType) {
|
switch (notificationType) {
|
||||||
case "alert":
|
case "alert":
|
||||||
if (notificationSettings[notificationType]?.[surveyOrProjectOrOrganizationId] === true) {
|
if (notificationSettings[notificationType][surveyOrProjectOrOrganizationId] === true) {
|
||||||
handleSwitchChange();
|
handleSwitchChange();
|
||||||
toast.success(
|
toast.success(
|
||||||
t(
|
t(
|
||||||
|
|||||||
-142
@@ -1,142 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { TFunction } from "i18next";
|
|
||||||
import { RotateCcwIcon } from "lucide-react";
|
|
||||||
import { useRouter } from "next/navigation";
|
|
||||||
import { useState } from "react";
|
|
||||||
import toast from "react-hot-toast";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { recheckLicenseAction } from "@/modules/ee/license-check/actions";
|
|
||||||
import { Alert, AlertDescription } from "@/modules/ui/components/alert";
|
|
||||||
import { Badge } from "@/modules/ui/components/badge";
|
|
||||||
import { Button } from "@/modules/ui/components/button";
|
|
||||||
import { SettingsCard } from "../../../components/SettingsCard";
|
|
||||||
|
|
||||||
type LicenseStatus = "active" | "expired" | "unreachable" | "invalid_license";
|
|
||||||
|
|
||||||
interface EnterpriseLicenseStatusProps {
|
|
||||||
status: LicenseStatus;
|
|
||||||
gracePeriodEnd?: Date;
|
|
||||||
environmentId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getBadgeConfig = (
|
|
||||||
status: LicenseStatus,
|
|
||||||
t: TFunction
|
|
||||||
): { type: "success" | "error" | "warning" | "gray"; label: string } => {
|
|
||||||
switch (status) {
|
|
||||||
case "active":
|
|
||||||
return { type: "success", label: t("environments.settings.enterprise.license_status_active") };
|
|
||||||
case "expired":
|
|
||||||
return { type: "error", label: t("environments.settings.enterprise.license_status_expired") };
|
|
||||||
case "unreachable":
|
|
||||||
return { type: "warning", label: t("environments.settings.enterprise.license_status_unreachable") };
|
|
||||||
case "invalid_license":
|
|
||||||
return { type: "error", label: t("environments.settings.enterprise.license_status_invalid") };
|
|
||||||
default:
|
|
||||||
return { type: "gray", label: t("environments.settings.enterprise.license_status") };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const EnterpriseLicenseStatus = ({
|
|
||||||
status,
|
|
||||||
gracePeriodEnd,
|
|
||||||
environmentId,
|
|
||||||
}: EnterpriseLicenseStatusProps) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const router = useRouter();
|
|
||||||
const [isRechecking, setIsRechecking] = useState(false);
|
|
||||||
|
|
||||||
const handleRecheck = async () => {
|
|
||||||
setIsRechecking(true);
|
|
||||||
try {
|
|
||||||
const result = await recheckLicenseAction({ environmentId });
|
|
||||||
if (result?.serverError) {
|
|
||||||
toast.error(result.serverError || t("environments.settings.enterprise.recheck_license_failed"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result?.data) {
|
|
||||||
if (result.data.status === "unreachable") {
|
|
||||||
toast.error(t("environments.settings.enterprise.recheck_license_unreachable"));
|
|
||||||
} else if (result.data.status === "invalid_license") {
|
|
||||||
toast.error(t("environments.settings.enterprise.recheck_license_invalid"));
|
|
||||||
} else {
|
|
||||||
toast.success(t("environments.settings.enterprise.recheck_license_success"));
|
|
||||||
}
|
|
||||||
router.refresh();
|
|
||||||
} else {
|
|
||||||
toast.error(t("environments.settings.enterprise.recheck_license_failed"));
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
toast.error(
|
|
||||||
error instanceof Error ? error.message : t("environments.settings.enterprise.recheck_license_failed")
|
|
||||||
);
|
|
||||||
} finally {
|
|
||||||
setIsRechecking(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const badgeConfig = getBadgeConfig(status, t);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SettingsCard
|
|
||||||
title={t("environments.settings.enterprise.license_status")}
|
|
||||||
description={t("environments.settings.enterprise.license_status_description")}>
|
|
||||||
<div className="flex flex-col gap-4">
|
|
||||||
<div className="flex items-center justify-between gap-3">
|
|
||||||
<div className="flex flex-col gap-1.5">
|
|
||||||
<Badge type={badgeConfig.type} text={badgeConfig.label} size="normal" className="w-fit" />
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
onClick={handleRecheck}
|
|
||||||
disabled={isRechecking}
|
|
||||||
className="shrink-0">
|
|
||||||
{isRechecking ? (
|
|
||||||
<>
|
|
||||||
<RotateCcwIcon className="mr-2 h-4 w-4 animate-spin" />
|
|
||||||
{t("environments.settings.enterprise.rechecking")}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<RotateCcwIcon className="mr-2 h-4 w-4" />
|
|
||||||
{t("environments.settings.enterprise.recheck_license")}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
{status === "unreachable" && gracePeriodEnd && (
|
|
||||||
<Alert variant="warning" size="small">
|
|
||||||
<AlertDescription className="overflow-visible whitespace-normal">
|
|
||||||
{t("environments.settings.enterprise.license_unreachable_grace_period", {
|
|
||||||
gracePeriodEnd: new Date(gracePeriodEnd).toLocaleDateString(undefined, {
|
|
||||||
year: "numeric",
|
|
||||||
month: "short",
|
|
||||||
day: "numeric",
|
|
||||||
}),
|
|
||||||
})}
|
|
||||||
</AlertDescription>
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
{status === "invalid_license" && (
|
|
||||||
<Alert variant="error" size="small">
|
|
||||||
<AlertDescription className="overflow-visible whitespace-normal">
|
|
||||||
{t("environments.settings.enterprise.license_invalid_description")}
|
|
||||||
</AlertDescription>
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
<p className="border-t border-slate-100 pt-4 text-sm text-slate-500">
|
|
||||||
{t("environments.settings.enterprise.questions_please_reach_out_to")}{" "}
|
|
||||||
<a
|
|
||||||
className="font-medium text-slate-700 underline hover:text-slate-900"
|
|
||||||
href="mailto:hola@formbricks.com">
|
|
||||||
hola@formbricks.com
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</SettingsCard>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
+28
-17
@@ -2,10 +2,9 @@ import { CheckIcon } from "lucide-react";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import { OrganizationSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(organization)/components/OrganizationSettingsNavbar";
|
import { OrganizationSettingsNavbar } from "@/app/(app)/environments/[environmentId]/settings/(organization)/components/OrganizationSettingsNavbar";
|
||||||
import { EnterpriseLicenseStatus } from "@/app/(app)/environments/[environmentId]/settings/(organization)/enterprise/components/EnterpriseLicenseStatus";
|
|
||||||
import { IS_FORMBRICKS_CLOUD } from "@/lib/constants";
|
import { IS_FORMBRICKS_CLOUD } from "@/lib/constants";
|
||||||
import { getTranslate } from "@/lingodotdev/server";
|
import { getTranslate } from "@/lingodotdev/server";
|
||||||
import { GRACE_PERIOD_MS, getEnterpriseLicense } from "@/modules/ee/license-check/lib/license";
|
import { getEnterpriseLicense } from "@/modules/ee/license-check/lib/license";
|
||||||
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
|
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
|
||||||
import { Button } from "@/modules/ui/components/button";
|
import { Button } from "@/modules/ui/components/button";
|
||||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||||
@@ -26,8 +25,7 @@ const Page = async (props) => {
|
|||||||
return notFound();
|
return notFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
const licenseState = await getEnterpriseLicense();
|
const { active: isEnterpriseEdition } = await getEnterpriseLicense();
|
||||||
const hasLicense = licenseState.status !== "no-license";
|
|
||||||
|
|
||||||
const paidFeatures = [
|
const paidFeatures = [
|
||||||
{
|
{
|
||||||
@@ -92,22 +90,35 @@ const Page = async (props) => {
|
|||||||
activeId="enterprise"
|
activeId="enterprise"
|
||||||
/>
|
/>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
{hasLicense ? (
|
{isEnterpriseEdition ? (
|
||||||
<EnterpriseLicenseStatus
|
<div>
|
||||||
status={licenseState.status as "active" | "expired" | "unreachable" | "invalid_license"}
|
<div className="mt-8 max-w-4xl rounded-lg border border-slate-300 bg-slate-100 shadow-sm">
|
||||||
gracePeriodEnd={
|
<div className="space-y-4 p-8">
|
||||||
licenseState.status === "unreachable"
|
<div className="flex items-center gap-x-2">
|
||||||
? new Date(licenseState.lastChecked.getTime() + GRACE_PERIOD_MS)
|
<div className="rounded-full border border-green-300 bg-green-100 p-0.5 dark:bg-green-800">
|
||||||
: undefined
|
<CheckIcon className="h-5 w-5 p-0.5 text-green-500 dark:text-green-400" />
|
||||||
}
|
</div>
|
||||||
environmentId={params.environmentId}
|
<p className="text-slate-800">
|
||||||
/>
|
{t(
|
||||||
|
"environments.settings.enterprise.your_enterprise_license_is_active_all_features_unlocked"
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-slate-500">
|
||||||
|
{t("environments.settings.enterprise.questions_please_reach_out_to")}{" "}
|
||||||
|
<a className="font-semibold underline" href="mailto:hola@formbricks.com">
|
||||||
|
hola@formbricks.com
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
<div className="relative isolate mt-8 overflow-hidden rounded-lg bg-slate-900 px-3 pt-8 shadow-2xl sm:px-8 md:pt-12 lg:flex lg:gap-x-10 lg:px-12 lg:pt-0">
|
<div className="relative isolate mt-8 overflow-hidden rounded-lg bg-slate-900 px-3 pt-8 shadow-2xl sm:px-8 md:pt-12 lg:flex lg:gap-x-10 lg:px-12 lg:pt-0">
|
||||||
<svg
|
<svg
|
||||||
viewBox="0 0 1024 1024"
|
viewBox="0 0 1024 1024"
|
||||||
className="absolute left-1/2 top-1/2 -z-10 h-[64rem] w-[64rem] -translate-y-1/2 [mask-image:radial-gradient(closest-side,white,transparent)] sm:left-full sm:-ml-80 lg:left-1/2 lg:ml-0 lg:-translate-x-1/2 lg:translate-y-0"
|
className="absolute top-1/2 left-1/2 -z-10 h-[64rem] w-[64rem] -translate-y-1/2 [mask-image:radial-gradient(closest-side,white,transparent)] sm:left-full sm:-ml-80 lg:left-1/2 lg:ml-0 lg:-translate-x-1/2 lg:translate-y-0"
|
||||||
aria-hidden="true">
|
aria-hidden="true">
|
||||||
<circle
|
<circle
|
||||||
cx={512}
|
cx={512}
|
||||||
@@ -142,8 +153,8 @@ const Page = async (props) => {
|
|||||||
{t("environments.settings.enterprise.enterprise_features")}
|
{t("environments.settings.enterprise.enterprise_features")}
|
||||||
</h2>
|
</h2>
|
||||||
<ul className="my-4 space-y-4">
|
<ul className="my-4 space-y-4">
|
||||||
{paidFeatures.map((feature) => (
|
{paidFeatures.map((feature, index) => (
|
||||||
<li key={feature.title} className="flex items-center">
|
<li key={index} className="flex items-center">
|
||||||
<div className="rounded-full border border-green-300 bg-green-100 p-0.5 dark:bg-green-800">
|
<div className="rounded-full border border-green-300 bg-green-100 p-0.5 dark:bg-green-800">
|
||||||
<CheckIcon className="h-5 w-5 p-0.5 text-green-500 dark:text-green-400" />
|
<CheckIcon className="h-5 w-5 p-0.5 text-green-500 dark:text-green-400" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+1
-7
@@ -7,7 +7,6 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { TOrganization } from "@formbricks/types/organizations";
|
import { TOrganization } from "@formbricks/types/organizations";
|
||||||
import { deleteOrganizationAction } from "@/app/(app)/environments/[environmentId]/settings/(organization)/general/actions";
|
import { deleteOrganizationAction } from "@/app/(app)/environments/[environmentId]/settings/(organization)/general/actions";
|
||||||
import { FORMBRICKS_ENVIRONMENT_ID_LS } from "@/lib/localStorage";
|
import { FORMBRICKS_ENVIRONMENT_ID_LS } from "@/lib/localStorage";
|
||||||
import { getFormattedErrorMessage } from "@/lib/utils/helper";
|
|
||||||
import { Alert, AlertDescription } from "@/modules/ui/components/alert";
|
import { Alert, 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";
|
||||||
@@ -33,12 +32,7 @@ export const DeleteOrganization = ({
|
|||||||
setIsDeleting(true);
|
setIsDeleting(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await deleteOrganizationAction({ organizationId: organization.id });
|
await deleteOrganizationAction({ organizationId: organization.id });
|
||||||
if (result?.serverError) {
|
|
||||||
toast.error(getFormattedErrorMessage(result));
|
|
||||||
setIsDeleting(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
toast.success(t("environments.settings.general.organization_deleted_successfully"));
|
toast.success(t("environments.settings.general.organization_deleted_successfully"));
|
||||||
if (typeof localStorage !== "undefined") {
|
if (typeof localStorage !== "undefined") {
|
||||||
localStorage.removeItem(FORMBRICKS_ENVIRONMENT_ID_LS);
|
localStorage.removeItem(FORMBRICKS_ENVIRONMENT_ID_LS);
|
||||||
|
|||||||
-29
@@ -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";
|
||||||
@@ -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.number().int().min(1).max(100),
|
|
||||||
offset: z.number().int().nonnegative(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const getDisplaysWithContactAction = authenticatedActionClient
|
|
||||||
.schema(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);
|
|
||||||
});
|
|
||||||
|
|||||||
-9
@@ -316,14 +316,6 @@ export const generateResponseTableColumns = (
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const responseIdColumn: ColumnDef<TResponseTableData> = {
|
|
||||||
accessorKey: "responseId",
|
|
||||||
header: () => <div className="gap-x-1.5">{t("common.response_id")}</div>,
|
|
||||||
cell: ({ row }) => {
|
|
||||||
return <IdBadge id={row.original.responseId} />;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const quotasColumn: ColumnDef<TResponseTableData> = {
|
const quotasColumn: ColumnDef<TResponseTableData> = {
|
||||||
accessorKey: "quota",
|
accessorKey: "quota",
|
||||||
header: t("common.quota"),
|
header: t("common.quota"),
|
||||||
@@ -422,7 +414,6 @@ export const generateResponseTableColumns = (
|
|||||||
const baseColumns = [
|
const baseColumns = [
|
||||||
personColumn,
|
personColumn,
|
||||||
singleUseIdColumn,
|
singleUseIdColumn,
|
||||||
responseIdColumn,
|
|
||||||
dateColumn,
|
dateColumn,
|
||||||
...(showQuotasColumn ? [quotasColumn] : []),
|
...(showQuotasColumn ? [quotasColumn] : []),
|
||||||
statusColumn,
|
statusColumn,
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@ import { TSurvey, TSurveyElementSummaryFileUpload } from "@formbricks/types/surv
|
|||||||
import { TUserLocale } from "@formbricks/types/user";
|
import { TUserLocale } from "@formbricks/types/user";
|
||||||
import { timeSince } from "@/lib/time";
|
import { timeSince } from "@/lib/time";
|
||||||
import { getContactIdentifier } from "@/lib/utils/contact";
|
import { getContactIdentifier } from "@/lib/utils/contact";
|
||||||
import { getOriginalFileNameFromUrl } from "@/modules/storage/url-helpers";
|
import { getOriginalFileNameFromUrl } from "@/modules/storage/utils";
|
||||||
import { PersonAvatar } from "@/modules/ui/components/avatars";
|
import { PersonAvatar } from "@/modules/ui/components/avatars";
|
||||||
import { Button } from "@/modules/ui/components/button";
|
import { Button } from "@/modules/ui/components/button";
|
||||||
import { EmptyState } from "@/modules/ui/components/empty-state";
|
import { EmptyState } from "@/modules/ui/components/empty-state";
|
||||||
|
|||||||
-125
@@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
+4
-8
@@ -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")}
|
||||||
|
|||||||
+3
-84
@@ -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">
|
||||||
|
|||||||
+2
-2
@@ -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;
|
||||||
|
|||||||
+2
-14
@@ -21,7 +21,6 @@ import { createOrUpdateIntegrationAction } from "@/app/(app)/environments/[envir
|
|||||||
import { BaseSelectDropdown } from "@/app/(app)/environments/[environmentId]/workspace/integrations/airtable/components/BaseSelectDropdown";
|
import { BaseSelectDropdown } from "@/app/(app)/environments/[environmentId]/workspace/integrations/airtable/components/BaseSelectDropdown";
|
||||||
import { fetchTables } from "@/app/(app)/environments/[environmentId]/workspace/integrations/airtable/lib/airtable";
|
import { fetchTables } from "@/app/(app)/environments/[environmentId]/workspace/integrations/airtable/lib/airtable";
|
||||||
import AirtableLogo from "@/images/airtableLogo.svg";
|
import AirtableLogo from "@/images/airtableLogo.svg";
|
||||||
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";
|
||||||
import { AdditionalIntegrationSettings } from "@/modules/ui/components/additional-integration-settings";
|
import { AdditionalIntegrationSettings } from "@/modules/ui/components/additional-integration-settings";
|
||||||
@@ -269,14 +268,7 @@ export const AddIntegrationModal = ({
|
|||||||
airtableIntegrationData.config?.data.push(integrationData);
|
airtableIntegrationData.config?.data.push(integrationData);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await createOrUpdateIntegrationAction({
|
await createOrUpdateIntegrationAction({ environmentId, integrationData: airtableIntegrationData });
|
||||||
environmentId,
|
|
||||||
integrationData: airtableIntegrationData,
|
|
||||||
});
|
|
||||||
if (result?.serverError) {
|
|
||||||
toast.error(getFormattedErrorMessage(result));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isEditMode) {
|
if (isEditMode) {
|
||||||
toast.success(t("environments.integrations.integration_updated_successfully"));
|
toast.success(t("environments.integrations.integration_updated_successfully"));
|
||||||
} else {
|
} else {
|
||||||
@@ -312,11 +304,7 @@ export const AddIntegrationModal = ({
|
|||||||
const integrationData = structuredClone(airtableIntegrationData);
|
const integrationData = structuredClone(airtableIntegrationData);
|
||||||
integrationData.config.data.splice(index, 1);
|
integrationData.config.data.splice(index, 1);
|
||||||
|
|
||||||
const result = await createOrUpdateIntegrationAction({ environmentId, integrationData });
|
await createOrUpdateIntegrationAction({ environmentId, integrationData });
|
||||||
if (result?.serverError) {
|
|
||||||
toast.error(getFormattedErrorMessage(result));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleClose();
|
handleClose();
|
||||||
router.refresh();
|
router.refresh();
|
||||||
|
|
||||||
|
|||||||
+2
-39
@@ -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
|
|
||||||
.schema(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(),
|
||||||
|
|||||||
+7
-35
@@ -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;
|
||||||
@@ -179,14 +165,7 @@ export const AddIntegrationModal = ({
|
|||||||
// create action
|
// create action
|
||||||
googleSheetIntegrationData.config.data.push(integrationData);
|
googleSheetIntegrationData.config.data.push(integrationData);
|
||||||
}
|
}
|
||||||
const result = await createOrUpdateIntegrationAction({
|
await createOrUpdateIntegrationAction({ environmentId, integrationData: googleSheetIntegrationData });
|
||||||
environmentId,
|
|
||||||
integrationData: googleSheetIntegrationData,
|
|
||||||
});
|
|
||||||
if (result?.serverError) {
|
|
||||||
toast.error(getFormattedErrorMessage(result));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (selectedIntegration) {
|
if (selectedIntegration) {
|
||||||
toast.success(t("environments.integrations.integration_updated_successfully"));
|
toast.success(t("environments.integrations.integration_updated_successfully"));
|
||||||
} else {
|
} else {
|
||||||
@@ -226,14 +205,7 @@ export const AddIntegrationModal = ({
|
|||||||
googleSheetIntegrationData.config.data.splice(selectedIntegration!.index, 1);
|
googleSheetIntegrationData.config.data.splice(selectedIntegration!.index, 1);
|
||||||
try {
|
try {
|
||||||
setIsDeleting(true);
|
setIsDeleting(true);
|
||||||
const result = await createOrUpdateIntegrationAction({
|
await createOrUpdateIntegrationAction({ environmentId, integrationData: googleSheetIntegrationData });
|
||||||
environmentId,
|
|
||||||
integrationData: googleSheetIntegrationData,
|
|
||||||
});
|
|
||||||
if (result?.serverError) {
|
|
||||||
toast.error(getFormattedErrorMessage(result));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
toast.success(t("environments.integrations.integration_removed_successfully"));
|
toast.success(t("environments.integrations.integration_removed_successfully"));
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -294,7 +266,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
-18
@@ -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}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|||||||
+2
-31
@@ -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);
|
||||||
|
|||||||
+2
-17
@@ -22,7 +22,6 @@ import {
|
|||||||
createEmptyMapping,
|
createEmptyMapping,
|
||||||
} from "@/app/(app)/environments/[environmentId]/workspace/integrations/notion/components/MappingRow";
|
} from "@/app/(app)/environments/[environmentId]/workspace/integrations/notion/components/MappingRow";
|
||||||
import NotionLogo from "@/images/notion.png";
|
import NotionLogo from "@/images/notion.png";
|
||||||
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";
|
||||||
import { Button } from "@/modules/ui/components/button";
|
import { Button } from "@/modules/ui/components/button";
|
||||||
@@ -218,14 +217,7 @@ export const AddIntegrationModal = ({
|
|||||||
notionIntegrationData.config.data.push(integrationData);
|
notionIntegrationData.config.data.push(integrationData);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await createOrUpdateIntegrationAction({
|
await createOrUpdateIntegrationAction({ environmentId, integrationData: notionIntegrationData });
|
||||||
environmentId,
|
|
||||||
integrationData: notionIntegrationData,
|
|
||||||
});
|
|
||||||
if (result?.serverError) {
|
|
||||||
toast.error(getFormattedErrorMessage(result));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (selectedIntegration) {
|
if (selectedIntegration) {
|
||||||
toast.success(t("environments.integrations.integration_updated_successfully"));
|
toast.success(t("environments.integrations.integration_updated_successfully"));
|
||||||
} else {
|
} else {
|
||||||
@@ -244,14 +236,7 @@ export const AddIntegrationModal = ({
|
|||||||
notionIntegrationData.config.data.splice(selectedIntegration!.index, 1);
|
notionIntegrationData.config.data.splice(selectedIntegration!.index, 1);
|
||||||
try {
|
try {
|
||||||
setIsDeleting(true);
|
setIsDeleting(true);
|
||||||
const result = await createOrUpdateIntegrationAction({
|
await createOrUpdateIntegrationAction({ environmentId, integrationData: notionIntegrationData });
|
||||||
environmentId,
|
|
||||||
integrationData: notionIntegrationData,
|
|
||||||
});
|
|
||||||
if (result?.serverError) {
|
|
||||||
toast.error(getFormattedErrorMessage(result));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
toast.success(t("environments.integrations.integration_removed_successfully"));
|
toast.success(t("environments.integrations.integration_removed_successfully"));
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
+2
-17
@@ -17,7 +17,6 @@ import { TSurvey } from "@formbricks/types/surveys/types";
|
|||||||
import { getTextContent } from "@formbricks/types/surveys/validation";
|
import { getTextContent } from "@formbricks/types/surveys/validation";
|
||||||
import { createOrUpdateIntegrationAction } from "@/app/(app)/environments/[environmentId]/workspace/integrations/actions";
|
import { createOrUpdateIntegrationAction } from "@/app/(app)/environments/[environmentId]/workspace/integrations/actions";
|
||||||
import SlackLogo from "@/images/slacklogo.png";
|
import SlackLogo from "@/images/slacklogo.png";
|
||||||
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";
|
||||||
import { AdditionalIntegrationSettings } from "@/modules/ui/components/additional-integration-settings";
|
import { AdditionalIntegrationSettings } from "@/modules/ui/components/additional-integration-settings";
|
||||||
@@ -145,14 +144,7 @@ export const AddChannelMappingModal = ({
|
|||||||
// create action
|
// create action
|
||||||
slackIntegrationData.config.data.push(integrationData);
|
slackIntegrationData.config.data.push(integrationData);
|
||||||
}
|
}
|
||||||
const result = await createOrUpdateIntegrationAction({
|
await createOrUpdateIntegrationAction({ environmentId, integrationData: slackIntegrationData });
|
||||||
environmentId,
|
|
||||||
integrationData: slackIntegrationData,
|
|
||||||
});
|
|
||||||
if (result?.serverError) {
|
|
||||||
toast.error(getFormattedErrorMessage(result));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (selectedIntegration) {
|
if (selectedIntegration) {
|
||||||
toast.success(t("environments.integrations.integration_updated_successfully"));
|
toast.success(t("environments.integrations.integration_updated_successfully"));
|
||||||
} else {
|
} else {
|
||||||
@@ -189,14 +181,7 @@ export const AddChannelMappingModal = ({
|
|||||||
slackIntegrationData.config.data.splice(selectedIntegration!.index, 1);
|
slackIntegrationData.config.data.splice(selectedIntegration!.index, 1);
|
||||||
try {
|
try {
|
||||||
setIsDeleting(true);
|
setIsDeleting(true);
|
||||||
const result = await createOrUpdateIntegrationAction({
|
await createOrUpdateIntegrationAction({ environmentId, integrationData: slackIntegrationData });
|
||||||
environmentId,
|
|
||||||
integrationData: slackIntegrationData,
|
|
||||||
});
|
|
||||||
if (result?.serverError) {
|
|
||||||
toast.error(getFormattedErrorMessage(result));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
toast.success(t("environments.integrations.integration_removed_successfully"));
|
toast.success(t("environments.integrations.integration_removed_successfully"));
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import { getElementsFromBlocks } from "@/lib/survey/utils";
|
|||||||
import { getFormattedDateTimeString } from "@/lib/utils/datetime";
|
import { getFormattedDateTimeString } from "@/lib/utils/datetime";
|
||||||
import { parseRecallInfo } from "@/lib/utils/recall";
|
import { parseRecallInfo } from "@/lib/utils/recall";
|
||||||
import { truncateText } from "@/lib/utils/strings";
|
import { truncateText } from "@/lib/utils/strings";
|
||||||
import { resolveStorageUrlAuto } from "@/modules/storage/utils";
|
|
||||||
|
|
||||||
const convertMetaObjectToString = (metadata: TResponseMeta): string => {
|
const convertMetaObjectToString = (metadata: TResponseMeta): string => {
|
||||||
let result: string[] = [];
|
let result: string[] = [];
|
||||||
@@ -257,16 +256,10 @@ const processElementResponse = (
|
|||||||
const selectedChoiceIds = responseValue as string[];
|
const selectedChoiceIds = responseValue as string[];
|
||||||
return element.choices
|
return element.choices
|
||||||
.filter((choice) => selectedChoiceIds.includes(choice.id))
|
.filter((choice) => selectedChoiceIds.includes(choice.id))
|
||||||
.map((choice) => resolveStorageUrlAuto(choice.imageUrl))
|
.map((choice) => choice.imageUrl)
|
||||||
.join("\n");
|
.join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element.type === TSurveyElementTypeEnum.FileUpload && Array.isArray(responseValue)) {
|
|
||||||
return responseValue
|
|
||||||
.map((url) => (typeof url === "string" ? resolveStorageUrlAuto(url) : url))
|
|
||||||
.join("; ");
|
|
||||||
}
|
|
||||||
|
|
||||||
return processResponseData(responseValue);
|
return processResponseData(responseValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -375,7 +368,7 @@ const buildNotionPayloadProperties = (
|
|||||||
|
|
||||||
responses[resp] = (pictureElement as any)?.choices
|
responses[resp] = (pictureElement as any)?.choices
|
||||||
.filter((choice) => selectedChoiceIds.includes(choice.id))
|
.filter((choice) => selectedChoiceIds.includes(choice.id))
|
||||||
.map((choice) => resolveStorageUrlAuto(choice.imageUrl));
|
.map((choice) => choice.imageUrl);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import { convertDatesInObject } from "@/lib/time";
|
|||||||
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";
|
||||||
import { resolveStorageUrlsInObject } from "@/modules/storage/utils";
|
|
||||||
import { sendFollowUpsForResponse } from "@/modules/survey/follow-ups/lib/follow-ups";
|
import { sendFollowUpsForResponse } from "@/modules/survey/follow-ups/lib/follow-ups";
|
||||||
import { FollowUpSendError } from "@/modules/survey/follow-ups/types/follow-up";
|
import { FollowUpSendError } from "@/modules/survey/follow-ups/types/follow-up";
|
||||||
import { handleIntegrations } from "./lib/handleIntegrations";
|
import { handleIntegrations } from "./lib/handleIntegrations";
|
||||||
@@ -31,10 +30,7 @@ export const POST = async (request: Request) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const jsonInput = await request.json();
|
const jsonInput = await request.json();
|
||||||
const convertedJsonInput = convertDatesInObject(
|
const convertedJsonInput = convertDatesInObject(jsonInput);
|
||||||
jsonInput,
|
|
||||||
new Set(["contactAttributes", "variables", "data", "meta"])
|
|
||||||
);
|
|
||||||
|
|
||||||
const inputValidation = ZPipelineInput.safeParse(convertedJsonInput);
|
const inputValidation = ZPipelineInput.safeParse(convertedJsonInput);
|
||||||
|
|
||||||
@@ -96,15 +92,12 @@ export const POST = async (request: Request) => {
|
|||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const resolvedResponseData = resolveStorageUrlsInObject(response.data);
|
|
||||||
|
|
||||||
const webhookPromises = webhooks.map((webhook) => {
|
const webhookPromises = webhooks.map((webhook) => {
|
||||||
const body = JSON.stringify({
|
const body = JSON.stringify({
|
||||||
webhookId: webhook.id,
|
webhookId: webhook.id,
|
||||||
event,
|
event,
|
||||||
data: {
|
data: {
|
||||||
...response,
|
...response,
|
||||||
data: resolvedResponseData,
|
|
||||||
survey: {
|
survey: {
|
||||||
title: survey.name,
|
title: survey.name,
|
||||||
type: survey.type,
|
type: survey.type,
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import { google } from "googleapis";
|
import { google } from "googleapis";
|
||||||
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,
|
||||||
@@ -8,29 +6,18 @@ import {
|
|||||||
GOOGLE_SHEETS_REDIRECT_URL,
|
GOOGLE_SHEETS_REDIRECT_URL,
|
||||||
WEBAPP_URL,
|
WEBAPP_URL,
|
||||||
} from "@/lib/constants";
|
} from "@/lib/constants";
|
||||||
import { hasUserEnvironmentAccess } from "@/lib/environment/auth";
|
import { createOrUpdateIntegration } from "@/lib/integration/service";
|
||||||
import { createOrUpdateIntegration, getIntegrationByType } from "@/lib/integration/service";
|
|
||||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
|
||||||
|
|
||||||
export const GET = async (req: Request) => {
|
export const GET = async (req: Request) => {
|
||||||
const url = new URL(req.url);
|
const url = req.url;
|
||||||
const environmentId = url.searchParams.get("state");
|
const queryParams = new URLSearchParams(url.split("?")[1]); // Split the URL and get the query parameters
|
||||||
const code = url.searchParams.get("code");
|
const environmentId = queryParams.get("state"); // Get the value of the 'state' parameter
|
||||||
|
const code = queryParams.get("code");
|
||||||
|
|
||||||
if (!environmentId) {
|
if (!environmentId) {
|
||||||
return responses.badRequestResponse("Invalid environmentId");
|
return responses.badRequestResponse("Invalid environmentId");
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = await getServerSession(authOptions);
|
|
||||||
if (!session) {
|
|
||||||
return responses.notAuthenticatedResponse();
|
|
||||||
}
|
|
||||||
|
|
||||||
const canUserAccessEnvironment = await hasUserEnvironmentAccess(session.user.id, environmentId);
|
|
||||||
if (!canUserAccessEnvironment) {
|
|
||||||
return responses.unauthorizedResponse();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (code && typeof code !== "string") {
|
if (code && typeof code !== "string") {
|
||||||
return responses.badRequestResponse("`code` must be a string");
|
return responses.badRequestResponse("`code` must be a string");
|
||||||
}
|
}
|
||||||
@@ -43,39 +30,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,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,180 @@
|
|||||||
|
// Deprecated: This api route is deprecated now and will be removed in the future.
|
||||||
|
// Deprecated: This is currently only being used for the older react native SDKs. Please upgrade to the latest SDKs.
|
||||||
|
import { NextRequest, userAgent } from "next/server";
|
||||||
|
import { prisma } from "@formbricks/database";
|
||||||
|
import { logger } from "@formbricks/logger";
|
||||||
|
import { TJsPeopleUserIdInput, ZJsPeopleUserIdInput } from "@formbricks/types/js";
|
||||||
|
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||||
|
import { getContactByUserId } from "@/app/api/v1/client/[environmentId]/app/sync/lib/contact";
|
||||||
|
import { getSyncSurveys } from "@/app/api/v1/client/[environmentId]/app/sync/lib/survey";
|
||||||
|
import { replaceAttributeRecall } from "@/app/api/v1/client/[environmentId]/app/sync/lib/utils";
|
||||||
|
import { responses } from "@/app/lib/api/response";
|
||||||
|
import { transformErrorToDetails } from "@/app/lib/api/validator";
|
||||||
|
import { withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
|
||||||
|
import { getActionClasses } from "@/lib/actionClass/service";
|
||||||
|
import { IS_FORMBRICKS_CLOUD } from "@/lib/constants";
|
||||||
|
import { getEnvironment, updateEnvironment } from "@/lib/environment/service";
|
||||||
|
import {
|
||||||
|
getMonthlyOrganizationResponseCount,
|
||||||
|
getOrganizationByEnvironmentId,
|
||||||
|
} from "@/lib/organization/service";
|
||||||
|
import { getProjectByEnvironmentId } from "@/lib/project/service";
|
||||||
|
import { COLOR_DEFAULTS } from "@/lib/styling/constants";
|
||||||
|
|
||||||
|
const validateInput = (
|
||||||
|
environmentId: string,
|
||||||
|
userId: string
|
||||||
|
): { isValid: true; data: TJsPeopleUserIdInput } | { isValid: false; error: Response } => {
|
||||||
|
const inputValidation = ZJsPeopleUserIdInput.safeParse({ environmentId, userId });
|
||||||
|
if (!inputValidation.success) {
|
||||||
|
return {
|
||||||
|
isValid: false,
|
||||||
|
error: responses.badRequestResponse(
|
||||||
|
"Fields are missing or incorrectly formatted",
|
||||||
|
transformErrorToDetails(inputValidation.error),
|
||||||
|
true
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return { isValid: true, data: inputValidation.data };
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkResponseLimit = async (environmentId: string): Promise<boolean> => {
|
||||||
|
if (!IS_FORMBRICKS_CLOUD) return false;
|
||||||
|
|
||||||
|
const organization = await getOrganizationByEnvironmentId(environmentId);
|
||||||
|
if (!organization) {
|
||||||
|
logger.error({ environmentId }, "Organization does not exist");
|
||||||
|
|
||||||
|
// fail closed if the organization does not exist
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentResponseCount = await getMonthlyOrganizationResponseCount(organization.id);
|
||||||
|
const monthlyResponseLimit = organization.billing.limits.monthly.responses;
|
||||||
|
const isLimitReached = monthlyResponseLimit !== null && currentResponseCount >= monthlyResponseLimit;
|
||||||
|
|
||||||
|
return isLimitReached;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const OPTIONS = async (): Promise<Response> => {
|
||||||
|
return responses.successResponse({}, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const GET = withV1ApiWrapper({
|
||||||
|
handler: async ({
|
||||||
|
req,
|
||||||
|
props,
|
||||||
|
}: {
|
||||||
|
req: NextRequest;
|
||||||
|
props: { params: Promise<{ environmentId: string; userId: string }> };
|
||||||
|
}) => {
|
||||||
|
const params = await props.params;
|
||||||
|
try {
|
||||||
|
const { device } = userAgent(req);
|
||||||
|
|
||||||
|
// validate using zod
|
||||||
|
const validation = validateInput(params.environmentId, params.userId);
|
||||||
|
if (!validation.isValid) {
|
||||||
|
return { response: validation.error };
|
||||||
|
}
|
||||||
|
|
||||||
|
const { environmentId, userId } = validation.data;
|
||||||
|
|
||||||
|
const environment = await getEnvironment(environmentId);
|
||||||
|
if (!environment) {
|
||||||
|
throw new Error("Environment does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
const project = await getProjectByEnvironmentId(environmentId);
|
||||||
|
|
||||||
|
if (!project) {
|
||||||
|
throw new Error("Project not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!environment.appSetupCompleted) {
|
||||||
|
await updateEnvironment(environment.id, { appSetupCompleted: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// check organization subscriptions and response limits
|
||||||
|
const isAppSurveyResponseLimitReached = await checkResponseLimit(environmentId);
|
||||||
|
|
||||||
|
let contact = await getContactByUserId(environmentId, userId);
|
||||||
|
if (!contact) {
|
||||||
|
contact = await prisma.contact.create({
|
||||||
|
data: {
|
||||||
|
attributes: {
|
||||||
|
create: {
|
||||||
|
attributeKey: {
|
||||||
|
connect: {
|
||||||
|
key_environmentId: {
|
||||||
|
key: "userId",
|
||||||
|
environmentId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
value: userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
environment: { connect: { id: environmentId } },
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
attributes: { select: { attributeKey: { select: { key: true } }, value: true } },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const contactAttributes = contact.attributes.reduce((acc, attribute) => {
|
||||||
|
acc[attribute.attributeKey.key] = attribute.value;
|
||||||
|
return acc;
|
||||||
|
}, {}) as Record<string, string>;
|
||||||
|
|
||||||
|
const [surveys, actionClasses] = await Promise.all([
|
||||||
|
getSyncSurveys(
|
||||||
|
environmentId,
|
||||||
|
contact.id,
|
||||||
|
contactAttributes,
|
||||||
|
device.type === "mobile" ? "phone" : "desktop"
|
||||||
|
),
|
||||||
|
getActionClasses(environmentId),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const updatedProject: any = {
|
||||||
|
...project,
|
||||||
|
brandColor: project.styling.brandColor?.light ?? COLOR_DEFAULTS.brandColor,
|
||||||
|
...(project.styling.highlightBorderColor?.light && {
|
||||||
|
highlightBorderColor: project.styling.highlightBorderColor.light,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const language = contactAttributes["language"];
|
||||||
|
|
||||||
|
// Scenario 1: Multi language and updated trigger action classes supported.
|
||||||
|
// Use the surveys as they are.
|
||||||
|
let transformedSurveys: TSurvey[] = surveys;
|
||||||
|
|
||||||
|
// creating state object
|
||||||
|
let state = {
|
||||||
|
surveys: !isAppSurveyResponseLimitReached
|
||||||
|
? transformedSurveys.map((survey) => replaceAttributeRecall(survey, contactAttributes))
|
||||||
|
: [],
|
||||||
|
actionClasses,
|
||||||
|
language,
|
||||||
|
project: updatedProject,
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
response: responses.successResponse({ ...state }, true),
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
logger.error({ error, url: req.url }, "Error in GET /api/v1/client/[environmentId]/app/sync/[userId]");
|
||||||
|
return {
|
||||||
|
response: responses.internalServerErrorResponse(
|
||||||
|
"Unable to handle the request: " + error.message,
|
||||||
|
true
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
import { afterEach, describe, expect, test, vi } from "vitest";
|
||||||
|
import { prisma } from "@formbricks/database";
|
||||||
|
import { TContact } from "@/modules/ee/contacts/types/contact";
|
||||||
|
import { getContactByUserId } from "./contact";
|
||||||
|
|
||||||
|
// Mock prisma
|
||||||
|
vi.mock("@formbricks/database", () => ({
|
||||||
|
prisma: {
|
||||||
|
contact: {
|
||||||
|
findFirst: vi.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const environmentId = "test-environment-id";
|
||||||
|
const userId = "test-user-id";
|
||||||
|
const contactId = "test-contact-id";
|
||||||
|
|
||||||
|
const contactMock: Partial<TContact> & {
|
||||||
|
attributes: { value: string; attributeKey: { key: string } }[];
|
||||||
|
} = {
|
||||||
|
id: contactId,
|
||||||
|
attributes: [
|
||||||
|
{ attributeKey: { key: "userId" }, value: userId },
|
||||||
|
{ attributeKey: { key: "email" }, value: "test@example.com" },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("getContactByUserId", () => {
|
||||||
|
afterEach(() => {
|
||||||
|
vi.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should return contact if found", async () => {
|
||||||
|
vi.mocked(prisma.contact.findFirst).mockResolvedValue(contactMock as any);
|
||||||
|
|
||||||
|
const contact = await getContactByUserId(environmentId, userId);
|
||||||
|
|
||||||
|
expect(prisma.contact.findFirst).toHaveBeenCalledWith({
|
||||||
|
where: {
|
||||||
|
attributes: {
|
||||||
|
some: {
|
||||||
|
attributeKey: {
|
||||||
|
key: "userId",
|
||||||
|
environmentId,
|
||||||
|
},
|
||||||
|
value: userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
attributes: { select: { attributeKey: { select: { key: true } }, value: true } },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(contact).toEqual(contactMock);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should return null if contact not found", async () => {
|
||||||
|
vi.mocked(prisma.contact.findFirst).mockResolvedValue(null);
|
||||||
|
|
||||||
|
const contact = await getContactByUserId(environmentId, userId);
|
||||||
|
|
||||||
|
expect(prisma.contact.findFirst).toHaveBeenCalledWith({
|
||||||
|
where: {
|
||||||
|
attributes: {
|
||||||
|
some: {
|
||||||
|
attributeKey: {
|
||||||
|
key: "userId",
|
||||||
|
environmentId,
|
||||||
|
},
|
||||||
|
value: userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
attributes: { select: { attributeKey: { select: { key: true } }, value: true } },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(contact).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import "server-only";
|
||||||
|
import { cache as reactCache } from "react";
|
||||||
|
import { prisma } from "@formbricks/database";
|
||||||
|
|
||||||
|
export const getContactByUserId = reactCache(
|
||||||
|
async (
|
||||||
|
environmentId: string,
|
||||||
|
userId: string
|
||||||
|
): Promise<{
|
||||||
|
attributes: {
|
||||||
|
value: string;
|
||||||
|
attributeKey: {
|
||||||
|
key: string;
|
||||||
|
};
|
||||||
|
}[];
|
||||||
|
id: string;
|
||||||
|
} | null> => {
|
||||||
|
const contact = await prisma.contact.findFirst({
|
||||||
|
where: {
|
||||||
|
attributes: {
|
||||||
|
some: {
|
||||||
|
attributeKey: {
|
||||||
|
key: "userId",
|
||||||
|
environmentId,
|
||||||
|
},
|
||||||
|
value: userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
attributes: { select: { attributeKey: { select: { key: true } }, value: true } },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!contact) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return contact;
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -0,0 +1,323 @@
|
|||||||
|
import { Prisma } from "@prisma/client";
|
||||||
|
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
||||||
|
import { prisma } from "@formbricks/database";
|
||||||
|
import { logger } from "@formbricks/logger";
|
||||||
|
import { DatabaseError } from "@formbricks/types/errors";
|
||||||
|
import { TProject } from "@formbricks/types/project";
|
||||||
|
import { TSegment } from "@formbricks/types/segment";
|
||||||
|
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||||
|
import { getProjectByEnvironmentId } from "@/lib/project/service";
|
||||||
|
import { getSurveys } from "@/lib/survey/service";
|
||||||
|
import { anySurveyHasFilters } from "@/lib/survey/utils";
|
||||||
|
import { diffInDays } from "@/lib/utils/datetime";
|
||||||
|
import { evaluateSegment } from "@/modules/ee/contacts/segments/lib/segments";
|
||||||
|
import { getSyncSurveys } from "./survey";
|
||||||
|
|
||||||
|
vi.mock("@/lib/project/service", () => ({
|
||||||
|
getProjectByEnvironmentId: vi.fn(),
|
||||||
|
}));
|
||||||
|
vi.mock("@/lib/survey/service", () => ({
|
||||||
|
getSurveys: vi.fn(),
|
||||||
|
}));
|
||||||
|
vi.mock("@/lib/survey/utils", () => ({
|
||||||
|
anySurveyHasFilters: vi.fn(),
|
||||||
|
}));
|
||||||
|
vi.mock("@/lib/utils/datetime", () => ({
|
||||||
|
diffInDays: vi.fn(),
|
||||||
|
}));
|
||||||
|
vi.mock("@/lib/utils/validate", () => ({
|
||||||
|
validateInputs: vi.fn(),
|
||||||
|
}));
|
||||||
|
vi.mock("@/modules/ee/contacts/segments/lib/segments", () => ({
|
||||||
|
evaluateSegment: vi.fn(),
|
||||||
|
}));
|
||||||
|
vi.mock("@formbricks/database", () => ({
|
||||||
|
prisma: {
|
||||||
|
display: {
|
||||||
|
findMany: vi.fn(),
|
||||||
|
},
|
||||||
|
response: {
|
||||||
|
findMany: vi.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
vi.mock("@formbricks/logger", () => ({
|
||||||
|
logger: {
|
||||||
|
error: vi.fn(),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const environmentId = "test-env-id";
|
||||||
|
const contactId = "test-contact-id";
|
||||||
|
const contactAttributes = { userId: "user1", email: "test@example.com" };
|
||||||
|
const deviceType = "desktop";
|
||||||
|
|
||||||
|
const mockProject = {
|
||||||
|
id: "proj1",
|
||||||
|
name: "Test Project",
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
organizationId: "org1",
|
||||||
|
environments: [],
|
||||||
|
recontactDays: 10,
|
||||||
|
inAppSurveyBranding: true,
|
||||||
|
linkSurveyBranding: true,
|
||||||
|
placement: "bottomRight",
|
||||||
|
clickOutsideClose: true,
|
||||||
|
darkOverlay: false,
|
||||||
|
languages: [],
|
||||||
|
} as unknown as TProject;
|
||||||
|
|
||||||
|
const baseSurvey: TSurvey = {
|
||||||
|
id: "survey1",
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
name: "Test Survey 1",
|
||||||
|
environmentId: environmentId,
|
||||||
|
type: "app",
|
||||||
|
status: "inProgress",
|
||||||
|
questions: [],
|
||||||
|
displayOption: "displayOnce",
|
||||||
|
recontactDays: null,
|
||||||
|
autoClose: null,
|
||||||
|
delay: 0,
|
||||||
|
displayPercentage: null,
|
||||||
|
autoComplete: null,
|
||||||
|
segment: null,
|
||||||
|
surveyClosedMessage: null,
|
||||||
|
singleUse: null,
|
||||||
|
styling: null,
|
||||||
|
pin: null,
|
||||||
|
displayLimit: null,
|
||||||
|
welcomeCard: { enabled: false } as TSurvey["welcomeCard"],
|
||||||
|
endings: [],
|
||||||
|
triggers: [],
|
||||||
|
languages: [],
|
||||||
|
variables: [],
|
||||||
|
hiddenFields: { enabled: false },
|
||||||
|
createdBy: null,
|
||||||
|
isSingleResponsePerEmailEnabled: false,
|
||||||
|
isVerifyEmailEnabled: false,
|
||||||
|
projectOverwrites: null,
|
||||||
|
showLanguageSwitch: false,
|
||||||
|
isBackButtonHidden: false,
|
||||||
|
followUps: [],
|
||||||
|
recaptcha: { enabled: false, threshold: 0.5 },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper function to create mock display objects
|
||||||
|
const createMockDisplay = (id: string, surveyId: string, contactId: string, createdAt?: Date) => ({
|
||||||
|
id,
|
||||||
|
createdAt: createdAt || new Date(),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
surveyId,
|
||||||
|
contactId,
|
||||||
|
responseId: null,
|
||||||
|
status: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Helper function to create mock response objects
|
||||||
|
const createMockResponse = (id: string, surveyId: string, contactId: string, createdAt?: Date) => ({
|
||||||
|
id,
|
||||||
|
createdAt: createdAt || new Date(),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
finished: false,
|
||||||
|
surveyId,
|
||||||
|
contactId,
|
||||||
|
endingId: null,
|
||||||
|
data: {},
|
||||||
|
variables: {},
|
||||||
|
ttc: {},
|
||||||
|
meta: {},
|
||||||
|
contactAttributes: null,
|
||||||
|
singleUseId: null,
|
||||||
|
language: null,
|
||||||
|
displayId: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("getSyncSurveys", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.mocked(getProjectByEnvironmentId).mockResolvedValue(mockProject);
|
||||||
|
vi.mocked(prisma.display.findMany).mockResolvedValue([]);
|
||||||
|
vi.mocked(prisma.response.findMany).mockResolvedValue([]);
|
||||||
|
vi.mocked(anySurveyHasFilters).mockReturnValue(false);
|
||||||
|
vi.mocked(evaluateSegment).mockResolvedValue(true);
|
||||||
|
vi.mocked(diffInDays).mockReturnValue(100); // Assume enough days passed
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should throw error if product not found", async () => {
|
||||||
|
vi.mocked(getProjectByEnvironmentId).mockResolvedValue(null);
|
||||||
|
await expect(getSyncSurveys(environmentId, contactId, contactAttributes, deviceType)).rejects.toThrow(
|
||||||
|
"Project not found"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should return empty array if no surveys found", async () => {
|
||||||
|
vi.mocked(getSurveys).mockResolvedValue([]);
|
||||||
|
const result = await getSyncSurveys(environmentId, contactId, contactAttributes, deviceType);
|
||||||
|
expect(result).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should return empty array if no 'app' type surveys in progress", async () => {
|
||||||
|
const surveys: TSurvey[] = [
|
||||||
|
{ ...baseSurvey, id: "s1", type: "link", status: "inProgress" },
|
||||||
|
{ ...baseSurvey, id: "s2", type: "app", status: "paused" },
|
||||||
|
];
|
||||||
|
vi.mocked(getSurveys).mockResolvedValue(surveys);
|
||||||
|
const result = await getSyncSurveys(environmentId, contactId, contactAttributes, deviceType);
|
||||||
|
expect(result).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should filter by displayOption 'displayOnce'", async () => {
|
||||||
|
const surveys: TSurvey[] = [{ ...baseSurvey, id: "s1", displayOption: "displayOnce" }];
|
||||||
|
vi.mocked(getSurveys).mockResolvedValue(surveys);
|
||||||
|
vi.mocked(prisma.display.findMany).mockResolvedValue([createMockDisplay("d1", "s1", contactId)]); // Already displayed
|
||||||
|
|
||||||
|
const result = await getSyncSurveys(environmentId, contactId, contactAttributes, deviceType);
|
||||||
|
expect(result).toEqual([]);
|
||||||
|
|
||||||
|
vi.mocked(prisma.display.findMany).mockResolvedValue([]); // Not displayed yet
|
||||||
|
const result2 = await getSyncSurveys(environmentId, contactId, contactAttributes, deviceType);
|
||||||
|
expect(result2).toEqual(surveys);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should filter by displayOption 'displayMultiple'", async () => {
|
||||||
|
const surveys: TSurvey[] = [{ ...baseSurvey, id: "s1", displayOption: "displayMultiple" }];
|
||||||
|
vi.mocked(getSurveys).mockResolvedValue(surveys);
|
||||||
|
vi.mocked(prisma.response.findMany).mockResolvedValue([createMockResponse("r1", "s1", contactId)]); // Already responded
|
||||||
|
|
||||||
|
const result = await getSyncSurveys(environmentId, contactId, contactAttributes, deviceType);
|
||||||
|
expect(result).toEqual([]);
|
||||||
|
|
||||||
|
vi.mocked(prisma.response.findMany).mockResolvedValue([]); // Not responded yet
|
||||||
|
const result2 = await getSyncSurveys(environmentId, contactId, contactAttributes, deviceType);
|
||||||
|
expect(result2).toEqual(surveys);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should filter by displayOption 'displaySome'", async () => {
|
||||||
|
const surveys: TSurvey[] = [{ ...baseSurvey, id: "s1", displayOption: "displaySome", displayLimit: 2 }];
|
||||||
|
vi.mocked(getSurveys).mockResolvedValue(surveys);
|
||||||
|
vi.mocked(prisma.display.findMany).mockResolvedValue([
|
||||||
|
createMockDisplay("d1", "s1", contactId),
|
||||||
|
createMockDisplay("d2", "s1", contactId),
|
||||||
|
]); // Display limit reached
|
||||||
|
|
||||||
|
const result = await getSyncSurveys(environmentId, contactId, contactAttributes, deviceType);
|
||||||
|
expect(result).toEqual([]);
|
||||||
|
|
||||||
|
vi.mocked(prisma.display.findMany).mockResolvedValue([createMockDisplay("d1", "s1", contactId)]); // Within limit
|
||||||
|
const result2 = await getSyncSurveys(environmentId, contactId, contactAttributes, deviceType);
|
||||||
|
expect(result2).toEqual(surveys);
|
||||||
|
|
||||||
|
// Test with response already submitted
|
||||||
|
vi.mocked(prisma.response.findMany).mockResolvedValue([createMockResponse("r1", "s1", contactId)]);
|
||||||
|
const result3 = await getSyncSurveys(environmentId, contactId, contactAttributes, deviceType);
|
||||||
|
expect(result3).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should not filter by displayOption 'respondMultiple'", async () => {
|
||||||
|
const surveys: TSurvey[] = [{ ...baseSurvey, id: "s1", displayOption: "respondMultiple" }];
|
||||||
|
vi.mocked(getSurveys).mockResolvedValue(surveys);
|
||||||
|
vi.mocked(prisma.display.findMany).mockResolvedValue([createMockDisplay("d1", "s1", contactId)]);
|
||||||
|
vi.mocked(prisma.response.findMany).mockResolvedValue([createMockResponse("r1", "s1", contactId)]);
|
||||||
|
|
||||||
|
const result = await getSyncSurveys(environmentId, contactId, contactAttributes, deviceType);
|
||||||
|
expect(result).toEqual(surveys);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should filter by product recontactDays if survey recontactDays is null", async () => {
|
||||||
|
const surveys: TSurvey[] = [{ ...baseSurvey, id: "s1", recontactDays: null }];
|
||||||
|
vi.mocked(getSurveys).mockResolvedValue(surveys);
|
||||||
|
const displayDate = new Date();
|
||||||
|
vi.mocked(prisma.display.findMany).mockResolvedValue([
|
||||||
|
createMockDisplay("d1", "s2", contactId, displayDate), // Display for another survey
|
||||||
|
]);
|
||||||
|
|
||||||
|
vi.mocked(diffInDays).mockReturnValue(5); // Not enough days passed (product.recontactDays = 10)
|
||||||
|
const result = await getSyncSurveys(environmentId, contactId, contactAttributes, deviceType);
|
||||||
|
expect(result).toEqual([]);
|
||||||
|
expect(diffInDays).toHaveBeenCalledWith(expect.any(Date), displayDate);
|
||||||
|
|
||||||
|
vi.mocked(diffInDays).mockReturnValue(15); // Enough days passed
|
||||||
|
const result2 = await getSyncSurveys(environmentId, contactId, contactAttributes, deviceType);
|
||||||
|
expect(result2).toEqual(surveys);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should return surveys if no segment filters exist", async () => {
|
||||||
|
const surveys: TSurvey[] = [{ ...baseSurvey, id: "s1" }];
|
||||||
|
vi.mocked(getSurveys).mockResolvedValue(surveys);
|
||||||
|
vi.mocked(anySurveyHasFilters).mockReturnValue(false);
|
||||||
|
|
||||||
|
const result = await getSyncSurveys(environmentId, contactId, contactAttributes, deviceType);
|
||||||
|
expect(result).toEqual(surveys);
|
||||||
|
expect(evaluateSegment).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should evaluate segment filters if they exist", async () => {
|
||||||
|
const segment = { id: "seg1", filters: [{}] } as TSegment; // Mock filter structure
|
||||||
|
const surveys: TSurvey[] = [{ ...baseSurvey, id: "s1", segment }];
|
||||||
|
vi.mocked(getSurveys).mockResolvedValue(surveys);
|
||||||
|
vi.mocked(anySurveyHasFilters).mockReturnValue(true);
|
||||||
|
|
||||||
|
// Case 1: Segment evaluation matches
|
||||||
|
vi.mocked(evaluateSegment).mockResolvedValue(true);
|
||||||
|
const result1 = await getSyncSurveys(environmentId, contactId, contactAttributes, deviceType);
|
||||||
|
expect(result1).toEqual(surveys);
|
||||||
|
expect(evaluateSegment).toHaveBeenCalledWith(
|
||||||
|
{
|
||||||
|
attributes: contactAttributes,
|
||||||
|
deviceType,
|
||||||
|
environmentId,
|
||||||
|
contactId,
|
||||||
|
userId: contactAttributes.userId,
|
||||||
|
},
|
||||||
|
segment.filters
|
||||||
|
);
|
||||||
|
|
||||||
|
// Case 2: Segment evaluation does not match
|
||||||
|
vi.mocked(evaluateSegment).mockResolvedValue(false);
|
||||||
|
const result2 = await getSyncSurveys(environmentId, contactId, contactAttributes, deviceType);
|
||||||
|
expect(result2).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should handle Prisma errors", async () => {
|
||||||
|
const prismaError = new Prisma.PrismaClientKnownRequestError("Test Prisma Error", {
|
||||||
|
code: "P2025",
|
||||||
|
clientVersion: "test",
|
||||||
|
});
|
||||||
|
vi.mocked(getSurveys).mockRejectedValue(prismaError);
|
||||||
|
|
||||||
|
await expect(getSyncSurveys(environmentId, contactId, contactAttributes, deviceType)).rejects.toThrow(
|
||||||
|
DatabaseError
|
||||||
|
);
|
||||||
|
expect(logger.error).toHaveBeenCalledWith(prismaError);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should handle general errors", async () => {
|
||||||
|
const generalError = new Error("Something went wrong");
|
||||||
|
vi.mocked(getSurveys).mockRejectedValue(generalError);
|
||||||
|
|
||||||
|
await expect(getSyncSurveys(environmentId, contactId, contactAttributes, deviceType)).rejects.toThrow(
|
||||||
|
generalError
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should throw ResourceNotFoundError if resolved surveys are null after filtering", async () => {
|
||||||
|
const segment = { id: "seg1", filters: [{}] } as TSegment; // Mock filter structure
|
||||||
|
const surveys: TSurvey[] = [{ ...baseSurvey, id: "s1", segment }];
|
||||||
|
vi.mocked(getSurveys).mockResolvedValue(surveys);
|
||||||
|
vi.mocked(anySurveyHasFilters).mockReturnValue(true);
|
||||||
|
vi.mocked(evaluateSegment).mockResolvedValue(false); // Ensure all surveys are filtered out
|
||||||
|
|
||||||
|
// This scenario is tricky to force directly as the code checks `if (!surveys)` before returning.
|
||||||
|
// However, if `Promise.all` somehow resolved to null/undefined (highly unlikely), it should throw.
|
||||||
|
// We can simulate this by mocking `Promise.all` if needed, but the current code structure makes this hard to test.
|
||||||
|
// Let's assume the filter logic works correctly and test the intended path.
|
||||||
|
const result = await getSyncSurveys(environmentId, contactId, contactAttributes, deviceType);
|
||||||
|
expect(result).toEqual([]); // Expect empty array, not an error in this case.
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
import "server-only";
|
||||||
|
import { Prisma } from "@prisma/client";
|
||||||
|
import { cache as reactCache } from "react";
|
||||||
|
import { prisma } from "@formbricks/database";
|
||||||
|
import { logger } from "@formbricks/logger";
|
||||||
|
import { ZId } from "@formbricks/types/common";
|
||||||
|
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors";
|
||||||
|
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||||
|
import { getProjectByEnvironmentId } from "@/lib/project/service";
|
||||||
|
import { getSurveys } from "@/lib/survey/service";
|
||||||
|
import { anySurveyHasFilters } from "@/lib/survey/utils";
|
||||||
|
import { diffInDays } from "@/lib/utils/datetime";
|
||||||
|
import { validateInputs } from "@/lib/utils/validate";
|
||||||
|
import { evaluateSegment } from "@/modules/ee/contacts/segments/lib/segments";
|
||||||
|
|
||||||
|
export const getSyncSurveys = reactCache(
|
||||||
|
async (
|
||||||
|
environmentId: string,
|
||||||
|
contactId: string,
|
||||||
|
contactAttributes: Record<string, string | number>,
|
||||||
|
deviceType: "phone" | "desktop" = "desktop"
|
||||||
|
): Promise<TSurvey[]> => {
|
||||||
|
validateInputs([environmentId, ZId]);
|
||||||
|
try {
|
||||||
|
const project = await getProjectByEnvironmentId(environmentId);
|
||||||
|
|
||||||
|
if (!project) {
|
||||||
|
throw new Error("Project not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
let surveys = await getSurveys(environmentId);
|
||||||
|
|
||||||
|
// filtered surveys for running and web
|
||||||
|
surveys = surveys.filter((survey) => survey.status === "inProgress" && survey.type === "app");
|
||||||
|
|
||||||
|
// if no surveys are left, return an empty array
|
||||||
|
if (surveys.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const displays = await prisma.display.findMany({
|
||||||
|
where: {
|
||||||
|
contactId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const responses = await prisma.response.findMany({
|
||||||
|
where: {
|
||||||
|
contactId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// filter surveys that meet the displayOption criteria
|
||||||
|
surveys = surveys.filter((survey) => {
|
||||||
|
switch (survey.displayOption) {
|
||||||
|
case "respondMultiple":
|
||||||
|
return true;
|
||||||
|
case "displayOnce":
|
||||||
|
return displays.filter((display) => display.surveyId === survey.id).length === 0;
|
||||||
|
case "displayMultiple":
|
||||||
|
if (!responses) return true;
|
||||||
|
else {
|
||||||
|
return responses.filter((response) => response.surveyId === survey.id).length === 0;
|
||||||
|
}
|
||||||
|
case "displaySome":
|
||||||
|
if (survey.displayLimit === null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (responses && responses.filter((response) => response.surveyId === survey.id).length !== 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return displays.filter((display) => display.surveyId === survey.id).length < survey.displayLimit;
|
||||||
|
default:
|
||||||
|
throw Error("Invalid displayOption");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const latestDisplay = displays[0];
|
||||||
|
|
||||||
|
// filter surveys that meet the recontactDays criteria
|
||||||
|
surveys = surveys.filter((survey) => {
|
||||||
|
if (!latestDisplay) {
|
||||||
|
return true;
|
||||||
|
} else if (survey.recontactDays !== null) {
|
||||||
|
const lastDisplaySurvey = displays.filter((display) => display.surveyId === survey.id)[0];
|
||||||
|
if (!lastDisplaySurvey) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return diffInDays(new Date(), new Date(lastDisplaySurvey.createdAt)) >= survey.recontactDays;
|
||||||
|
} else if (project.recontactDays !== null) {
|
||||||
|
return diffInDays(new Date(), new Date(latestDisplay.createdAt)) >= project.recontactDays;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// if no surveys are left, return an empty array
|
||||||
|
if (surveys.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no surveys have segment filters, return the surveys
|
||||||
|
if (!anySurveyHasFilters(surveys)) {
|
||||||
|
return surveys;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the surveys now have segment filters, so we need to evaluate them
|
||||||
|
const surveyPromises = surveys.map(async (survey) => {
|
||||||
|
const { segment } = survey;
|
||||||
|
// if the survey has no segment, or the segment has no filters, we return the survey
|
||||||
|
if (!segment || !segment.filters?.length) {
|
||||||
|
return survey;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluate the segment filters
|
||||||
|
const result = await evaluateSegment(
|
||||||
|
{
|
||||||
|
attributes: contactAttributes ?? {},
|
||||||
|
deviceType,
|
||||||
|
environmentId,
|
||||||
|
contactId,
|
||||||
|
userId: String(contactAttributes.userId),
|
||||||
|
},
|
||||||
|
segment.filters
|
||||||
|
);
|
||||||
|
|
||||||
|
return result ? survey : null;
|
||||||
|
});
|
||||||
|
|
||||||
|
const resolvedSurveys = await Promise.all(surveyPromises);
|
||||||
|
surveys = resolvedSurveys.filter((survey) => !!survey) as TSurvey[];
|
||||||
|
|
||||||
|
if (!surveys) {
|
||||||
|
throw new ResourceNotFoundError("Survey", environmentId);
|
||||||
|
}
|
||||||
|
return surveys;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||||
|
logger.error(error);
|
||||||
|
throw new DatabaseError(error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -0,0 +1,245 @@
|
|||||||
|
import { describe, expect, test, vi } from "vitest";
|
||||||
|
import { TAttributes } from "@formbricks/types/attributes";
|
||||||
|
import { TLanguage } from "@formbricks/types/project";
|
||||||
|
import {
|
||||||
|
TSurvey,
|
||||||
|
TSurveyEnding,
|
||||||
|
TSurveyQuestion,
|
||||||
|
TSurveyQuestionTypeEnum,
|
||||||
|
} from "@formbricks/types/surveys/types";
|
||||||
|
import { parseRecallInfo } from "@/lib/utils/recall";
|
||||||
|
import { replaceAttributeRecall } from "./utils";
|
||||||
|
|
||||||
|
vi.mock("@/lib/utils/recall", () => ({
|
||||||
|
parseRecallInfo: vi.fn((text, attributes) => {
|
||||||
|
const recallPattern = /recall:([a-zA-Z0-9_-]+)/;
|
||||||
|
const match = text.match(recallPattern);
|
||||||
|
if (match && match[1]) {
|
||||||
|
const recallKey = match[1];
|
||||||
|
const attributeValue = attributes[recallKey];
|
||||||
|
if (attributeValue !== undefined) {
|
||||||
|
return text.replace(recallPattern, `parsed-${attributeValue}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return text; // Return original text if no match or attribute not found
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const baseSurvey: TSurvey = {
|
||||||
|
id: "survey1",
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
name: "Test Survey",
|
||||||
|
environmentId: "env1",
|
||||||
|
type: "app",
|
||||||
|
status: "inProgress",
|
||||||
|
questions: [],
|
||||||
|
endings: [],
|
||||||
|
welcomeCard: { enabled: false } as TSurvey["welcomeCard"],
|
||||||
|
languages: [
|
||||||
|
{ language: { id: "lang1", code: "en" } as unknown as TLanguage, default: true, enabled: true },
|
||||||
|
],
|
||||||
|
triggers: [],
|
||||||
|
recontactDays: null,
|
||||||
|
displayLimit: null,
|
||||||
|
singleUse: null,
|
||||||
|
styling: null,
|
||||||
|
surveyClosedMessage: null,
|
||||||
|
hiddenFields: { enabled: false },
|
||||||
|
variables: [],
|
||||||
|
createdBy: null,
|
||||||
|
isSingleResponsePerEmailEnabled: false,
|
||||||
|
isVerifyEmailEnabled: false,
|
||||||
|
projectOverwrites: null,
|
||||||
|
showLanguageSwitch: false,
|
||||||
|
isBackButtonHidden: false,
|
||||||
|
followUps: [],
|
||||||
|
recaptcha: { enabled: false, threshold: 0.5 },
|
||||||
|
displayOption: "displayOnce",
|
||||||
|
autoClose: null,
|
||||||
|
delay: 0,
|
||||||
|
displayPercentage: null,
|
||||||
|
autoComplete: null,
|
||||||
|
segment: null,
|
||||||
|
pin: null,
|
||||||
|
metadata: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const attributes: TAttributes = {
|
||||||
|
name: "John Doe",
|
||||||
|
email: "john.doe@example.com",
|
||||||
|
plan: "premium",
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("replaceAttributeRecall", () => {
|
||||||
|
test("should replace recall info in question headlines and subheaders", () => {
|
||||||
|
const surveyWithRecall: TSurvey = {
|
||||||
|
...baseSurvey,
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
id: "q1",
|
||||||
|
type: TSurveyQuestionTypeEnum.OpenText,
|
||||||
|
headline: { default: "Hello recall:name!" },
|
||||||
|
subheader: { default: "Your email is recall:email" },
|
||||||
|
required: true,
|
||||||
|
buttonLabel: { default: "Next" },
|
||||||
|
placeholder: { default: "Type here..." },
|
||||||
|
longAnswer: false,
|
||||||
|
logic: [],
|
||||||
|
} as unknown as TSurveyQuestion,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = replaceAttributeRecall(surveyWithRecall, attributes);
|
||||||
|
expect(result.questions[0].headline.default).toBe("Hello parsed-John Doe!");
|
||||||
|
expect(result.questions[0].subheader?.default).toBe("Your email is parsed-john.doe@example.com");
|
||||||
|
expect(vi.mocked(parseRecallInfo)).toHaveBeenCalledWith("Hello recall:name!", attributes);
|
||||||
|
expect(vi.mocked(parseRecallInfo)).toHaveBeenCalledWith("Your email is recall:email", attributes);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should replace recall info in welcome card headline", () => {
|
||||||
|
const surveyWithRecall: TSurvey = {
|
||||||
|
...baseSurvey,
|
||||||
|
welcomeCard: {
|
||||||
|
enabled: true,
|
||||||
|
headline: { default: "Welcome, recall:name!" },
|
||||||
|
subheader: { default: "<p>Some content</p>" },
|
||||||
|
buttonLabel: { default: "Start" },
|
||||||
|
timeToFinish: false,
|
||||||
|
showResponseCount: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = replaceAttributeRecall(surveyWithRecall, attributes);
|
||||||
|
expect(result.welcomeCard.headline?.default).toBe("Welcome, parsed-John Doe!");
|
||||||
|
expect(vi.mocked(parseRecallInfo)).toHaveBeenCalledWith("Welcome, recall:name!", attributes);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should replace recall info in end screen headlines and subheaders", () => {
|
||||||
|
const surveyWithRecall: TSurvey = {
|
||||||
|
...baseSurvey,
|
||||||
|
endings: [
|
||||||
|
{
|
||||||
|
type: "endScreen",
|
||||||
|
headline: { default: "Thank you, recall:name!" },
|
||||||
|
subheader: { default: "Your plan: recall:plan" },
|
||||||
|
buttonLabel: { default: "Finish" },
|
||||||
|
buttonLink: "https://example.com",
|
||||||
|
} as unknown as TSurveyEnding,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = replaceAttributeRecall(surveyWithRecall, attributes);
|
||||||
|
expect(result.endings[0].type).toBe("endScreen");
|
||||||
|
if (result.endings[0].type === "endScreen") {
|
||||||
|
expect(result.endings[0].headline?.default).toBe("Thank you, parsed-John Doe!");
|
||||||
|
expect(result.endings[0].subheader?.default).toBe("Your plan: parsed-premium");
|
||||||
|
expect(vi.mocked(parseRecallInfo)).toHaveBeenCalledWith("Thank you, recall:name!", attributes);
|
||||||
|
expect(vi.mocked(parseRecallInfo)).toHaveBeenCalledWith("Your plan: recall:plan", attributes);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should handle multiple languages", () => {
|
||||||
|
const surveyMultiLang: TSurvey = {
|
||||||
|
...baseSurvey,
|
||||||
|
languages: [
|
||||||
|
{ language: { id: "lang1", code: "en" } as unknown as TLanguage, default: true, enabled: true },
|
||||||
|
{ language: { id: "lang2", code: "es" } as unknown as TLanguage, default: false, enabled: true },
|
||||||
|
],
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
id: "q1",
|
||||||
|
type: TSurveyQuestionTypeEnum.OpenText,
|
||||||
|
headline: { default: "Hello recall:name!", es: "Hola recall:name!" },
|
||||||
|
required: true,
|
||||||
|
buttonLabel: { default: "Next", es: "Siguiente" },
|
||||||
|
placeholder: { default: "Type here...", es: "Escribe aquí..." },
|
||||||
|
longAnswer: false,
|
||||||
|
logic: [],
|
||||||
|
} as unknown as TSurveyQuestion,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = replaceAttributeRecall(surveyMultiLang, attributes);
|
||||||
|
expect(result.questions[0].headline.default).toBe("Hello parsed-John Doe!");
|
||||||
|
expect(result.questions[0].headline.es).toBe("Hola parsed-John Doe!");
|
||||||
|
expect(vi.mocked(parseRecallInfo)).toHaveBeenCalledWith("Hello recall:name!", attributes);
|
||||||
|
expect(vi.mocked(parseRecallInfo)).toHaveBeenCalledWith("Hola recall:name!", attributes);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should not replace if recall key is not in attributes", () => {
|
||||||
|
const surveyWithRecall: TSurvey = {
|
||||||
|
...baseSurvey,
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
id: "q1",
|
||||||
|
type: TSurveyQuestionTypeEnum.OpenText,
|
||||||
|
headline: { default: "Your company: recall:company" },
|
||||||
|
required: true,
|
||||||
|
buttonLabel: { default: "Next" },
|
||||||
|
placeholder: { default: "Type here..." },
|
||||||
|
longAnswer: false,
|
||||||
|
logic: [],
|
||||||
|
} as unknown as TSurveyQuestion,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = replaceAttributeRecall(surveyWithRecall, attributes);
|
||||||
|
expect(result.questions[0].headline.default).toBe("Your company: recall:company");
|
||||||
|
expect(vi.mocked(parseRecallInfo)).toHaveBeenCalledWith("Your company: recall:company", attributes);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should handle surveys with no recall information", async () => {
|
||||||
|
const surveyNoRecall: TSurvey = {
|
||||||
|
...baseSurvey,
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
id: "q1",
|
||||||
|
type: TSurveyQuestionTypeEnum.OpenText,
|
||||||
|
headline: { default: "Just a regular question" },
|
||||||
|
required: true,
|
||||||
|
buttonLabel: { default: "Next" },
|
||||||
|
placeholder: { default: "Type here..." },
|
||||||
|
longAnswer: false,
|
||||||
|
logic: [],
|
||||||
|
} as unknown as TSurveyQuestion,
|
||||||
|
],
|
||||||
|
welcomeCard: {
|
||||||
|
enabled: true,
|
||||||
|
headline: { default: "Welcome!" },
|
||||||
|
subheader: { default: "<p>Some content</p>" },
|
||||||
|
buttonLabel: { default: "Start" },
|
||||||
|
timeToFinish: false,
|
||||||
|
showResponseCount: false,
|
||||||
|
},
|
||||||
|
endings: [
|
||||||
|
{
|
||||||
|
type: "endScreen",
|
||||||
|
headline: { default: "Thank you!" },
|
||||||
|
buttonLabel: { default: "Finish" },
|
||||||
|
} as unknown as TSurveyEnding,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const parseRecallInfoSpy = vi.spyOn(await import("@/lib/utils/recall"), "parseRecallInfo");
|
||||||
|
|
||||||
|
const result = replaceAttributeRecall(surveyNoRecall, attributes);
|
||||||
|
expect(result).toEqual(surveyNoRecall); // Should be unchanged
|
||||||
|
expect(parseRecallInfoSpy).not.toHaveBeenCalled();
|
||||||
|
parseRecallInfoSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should handle surveys with empty questions, endings, or disabled welcome card", async () => {
|
||||||
|
const surveyEmpty: TSurvey = {
|
||||||
|
...baseSurvey,
|
||||||
|
questions: [],
|
||||||
|
endings: [],
|
||||||
|
welcomeCard: { enabled: false } as TSurvey["welcomeCard"],
|
||||||
|
};
|
||||||
|
const parseRecallInfoSpy = vi.spyOn(await import("@/lib/utils/recall"), "parseRecallInfo");
|
||||||
|
|
||||||
|
const result = replaceAttributeRecall(surveyEmpty, attributes);
|
||||||
|
expect(result).toEqual(surveyEmpty);
|
||||||
|
expect(parseRecallInfoSpy).not.toHaveBeenCalled();
|
||||||
|
parseRecallInfoSpy.mockRestore();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import { TAttributes } from "@formbricks/types/attributes";
|
||||||
|
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||||
|
import { parseRecallInfo } from "@/lib/utils/recall";
|
||||||
|
|
||||||
|
export const replaceAttributeRecall = (survey: TSurvey, attributes: TAttributes): TSurvey => {
|
||||||
|
const surveyTemp = structuredClone(survey);
|
||||||
|
const languages = surveyTemp.languages
|
||||||
|
.map((surveyLanguage) => {
|
||||||
|
if (surveyLanguage.default) {
|
||||||
|
return "default";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (surveyLanguage.enabled) {
|
||||||
|
return surveyLanguage.language.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.filter((language): language is string => language !== null);
|
||||||
|
|
||||||
|
surveyTemp.questions.forEach((question) => {
|
||||||
|
languages.forEach((language) => {
|
||||||
|
if (question.headline[language]?.includes("recall:")) {
|
||||||
|
question.headline[language] = parseRecallInfo(question.headline[language], attributes);
|
||||||
|
}
|
||||||
|
if (question.subheader && question.subheader[language]?.includes("recall:")) {
|
||||||
|
question.subheader[language] = parseRecallInfo(question.subheader[language], attributes);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (surveyTemp.welcomeCard.enabled && surveyTemp.welcomeCard.headline) {
|
||||||
|
languages.forEach((language) => {
|
||||||
|
if (surveyTemp.welcomeCard.headline && surveyTemp.welcomeCard.headline[language]?.includes("recall:")) {
|
||||||
|
surveyTemp.welcomeCard.headline[language] = parseRecallInfo(
|
||||||
|
surveyTemp.welcomeCard.headline[language],
|
||||||
|
attributes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
surveyTemp.endings.forEach((ending) => {
|
||||||
|
if (ending.type === "endScreen") {
|
||||||
|
languages.forEach((language) => {
|
||||||
|
if (ending.headline && ending.headline[language]?.includes("recall:")) {
|
||||||
|
ending.headline[language] = parseRecallInfo(ending.headline[language], attributes);
|
||||||
|
if (ending.subheader && ending.subheader[language]?.includes("recall:")) {
|
||||||
|
ending.subheader[language] = parseRecallInfo(ending.subheader[language], attributes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return surveyTemp;
|
||||||
|
};
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import {
|
||||||
|
OPTIONS,
|
||||||
|
PUT,
|
||||||
|
} from "@/modules/ee/contacts/api/v1/client/[environmentId]/contacts/[userId]/attributes/route";
|
||||||
|
|
||||||
|
export { OPTIONS, PUT };
|
||||||
@@ -1,314 +0,0 @@
|
|||||||
import { Prisma } from "@prisma/client";
|
|
||||||
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
|
||||||
import { prisma } from "@formbricks/database";
|
|
||||||
import { logger } from "@formbricks/logger";
|
|
||||||
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors";
|
|
||||||
import { getEnvironmentStateData } from "./data";
|
|
||||||
|
|
||||||
// Mock dependencies
|
|
||||||
vi.mock("@formbricks/database", () => ({
|
|
||||||
prisma: {
|
|
||||||
environment: {
|
|
||||||
findUnique: vi.fn(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock("@formbricks/logger", () => ({
|
|
||||||
logger: {
|
|
||||||
error: vi.fn(),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock("@/modules/survey/lib/utils", () => ({
|
|
||||||
transformPrismaSurvey: vi.fn((survey) => survey),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const environmentId = "cjld2cjxh0000qzrmn831i7rn";
|
|
||||||
|
|
||||||
const mockEnvironmentData = {
|
|
||||||
id: environmentId,
|
|
||||||
type: "production",
|
|
||||||
appSetupCompleted: true,
|
|
||||||
project: {
|
|
||||||
id: "project-123",
|
|
||||||
recontactDays: 30,
|
|
||||||
clickOutsideClose: true,
|
|
||||||
overlay: "none",
|
|
||||||
placement: "bottomRight",
|
|
||||||
inAppSurveyBranding: true,
|
|
||||||
styling: { allowStyleOverwrite: false },
|
|
||||||
organization: {
|
|
||||||
id: "org-123",
|
|
||||||
billing: {
|
|
||||||
plan: "free",
|
|
||||||
limits: { monthly: { responses: 100 } },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
actionClasses: [
|
|
||||||
{
|
|
||||||
id: "action-1",
|
|
||||||
type: "code",
|
|
||||||
name: "Test Action",
|
|
||||||
key: "test-action",
|
|
||||||
noCodeConfig: null,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
surveys: [
|
|
||||||
{
|
|
||||||
id: "survey-1",
|
|
||||||
name: "Test Survey",
|
|
||||||
type: "app",
|
|
||||||
status: "inProgress",
|
|
||||||
welcomeCard: { enabled: false },
|
|
||||||
questions: [],
|
|
||||||
blocks: null,
|
|
||||||
variables: [],
|
|
||||||
showLanguageSwitch: false,
|
|
||||||
languages: [],
|
|
||||||
endings: [],
|
|
||||||
autoClose: null,
|
|
||||||
styling: null,
|
|
||||||
recaptcha: { enabled: false },
|
|
||||||
segment: null,
|
|
||||||
recontactDays: null,
|
|
||||||
displayLimit: null,
|
|
||||||
displayOption: "displayOnce",
|
|
||||||
hiddenFields: { enabled: false },
|
|
||||||
isBackButtonHidden: false,
|
|
||||||
triggers: [],
|
|
||||||
displayPercentage: null,
|
|
||||||
delay: 0,
|
|
||||||
projectOverwrites: null,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
describe("getEnvironmentStateData", () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
vi.resetAllMocks();
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
vi.resetAllMocks();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should return environment state data when environment exists", async () => {
|
|
||||||
vi.mocked(prisma.environment.findUnique).mockResolvedValue(mockEnvironmentData as never);
|
|
||||||
|
|
||||||
const result = await getEnvironmentStateData(environmentId);
|
|
||||||
|
|
||||||
expect(result).toEqual({
|
|
||||||
environment: {
|
|
||||||
id: environmentId,
|
|
||||||
type: "production",
|
|
||||||
appSetupCompleted: true,
|
|
||||||
project: {
|
|
||||||
id: "project-123",
|
|
||||||
recontactDays: 30,
|
|
||||||
clickOutsideClose: true,
|
|
||||||
overlay: "none",
|
|
||||||
placement: "bottomRight",
|
|
||||||
inAppSurveyBranding: true,
|
|
||||||
styling: { allowStyleOverwrite: false },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
organization: {
|
|
||||||
id: "org-123",
|
|
||||||
billing: {
|
|
||||||
plan: "free",
|
|
||||||
limits: { monthly: { responses: 100 } },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
surveys: mockEnvironmentData.surveys,
|
|
||||||
actionClasses: mockEnvironmentData.actionClasses,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(prisma.environment.findUnique).toHaveBeenCalledWith({
|
|
||||||
where: { id: environmentId },
|
|
||||||
select: expect.objectContaining({
|
|
||||||
id: true,
|
|
||||||
type: true,
|
|
||||||
appSetupCompleted: true,
|
|
||||||
project: expect.any(Object),
|
|
||||||
actionClasses: expect.any(Object),
|
|
||||||
surveys: expect.any(Object),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should throw ResourceNotFoundError when environment is not found", async () => {
|
|
||||||
vi.mocked(prisma.environment.findUnique).mockResolvedValue(null);
|
|
||||||
|
|
||||||
await expect(getEnvironmentStateData(environmentId)).rejects.toThrow(ResourceNotFoundError);
|
|
||||||
await expect(getEnvironmentStateData(environmentId)).rejects.toThrow("environment");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should throw ResourceNotFoundError when project is not found", async () => {
|
|
||||||
vi.mocked(prisma.environment.findUnique).mockResolvedValue({
|
|
||||||
...mockEnvironmentData,
|
|
||||||
project: null,
|
|
||||||
} as never);
|
|
||||||
|
|
||||||
await expect(getEnvironmentStateData(environmentId)).rejects.toThrow(ResourceNotFoundError);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should throw ResourceNotFoundError when organization is not found", async () => {
|
|
||||||
vi.mocked(prisma.environment.findUnique).mockResolvedValue({
|
|
||||||
...mockEnvironmentData,
|
|
||||||
project: {
|
|
||||||
...mockEnvironmentData.project,
|
|
||||||
organization: null,
|
|
||||||
},
|
|
||||||
} as never);
|
|
||||||
|
|
||||||
await expect(getEnvironmentStateData(environmentId)).rejects.toThrow(ResourceNotFoundError);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should throw DatabaseError on Prisma database errors", async () => {
|
|
||||||
const prismaError = new Prisma.PrismaClientKnownRequestError("Connection failed", {
|
|
||||||
code: "P2024",
|
|
||||||
clientVersion: "5.0.0",
|
|
||||||
});
|
|
||||||
vi.mocked(prisma.environment.findUnique).mockRejectedValue(prismaError);
|
|
||||||
|
|
||||||
await expect(getEnvironmentStateData(environmentId)).rejects.toThrow(DatabaseError);
|
|
||||||
expect(logger.error).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should rethrow unexpected errors", async () => {
|
|
||||||
const unexpectedError = new Error("Unexpected error");
|
|
||||||
vi.mocked(prisma.environment.findUnique).mockRejectedValue(unexpectedError);
|
|
||||||
|
|
||||||
await expect(getEnvironmentStateData(environmentId)).rejects.toThrow("Unexpected error");
|
|
||||||
expect(logger.error).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should handle empty surveys array", async () => {
|
|
||||||
vi.mocked(prisma.environment.findUnique).mockResolvedValue({
|
|
||||||
...mockEnvironmentData,
|
|
||||||
surveys: [],
|
|
||||||
} as never);
|
|
||||||
|
|
||||||
const result = await getEnvironmentStateData(environmentId);
|
|
||||||
|
|
||||||
expect(result.surveys).toEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should handle empty actionClasses array", async () => {
|
|
||||||
vi.mocked(prisma.environment.findUnique).mockResolvedValue({
|
|
||||||
...mockEnvironmentData,
|
|
||||||
actionClasses: [],
|
|
||||||
} as never);
|
|
||||||
|
|
||||||
const result = await getEnvironmentStateData(environmentId);
|
|
||||||
|
|
||||||
expect(result.actionClasses).toEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should transform surveys using transformPrismaSurvey", async () => {
|
|
||||||
const multipleSurveys = [
|
|
||||||
...mockEnvironmentData.surveys,
|
|
||||||
{
|
|
||||||
...mockEnvironmentData.surveys[0],
|
|
||||||
id: "survey-2",
|
|
||||||
name: "Second Survey",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
vi.mocked(prisma.environment.findUnique).mockResolvedValue({
|
|
||||||
...mockEnvironmentData,
|
|
||||||
surveys: multipleSurveys,
|
|
||||||
} as never);
|
|
||||||
|
|
||||||
const result = await getEnvironmentStateData(environmentId);
|
|
||||||
|
|
||||||
expect(result.surveys).toHaveLength(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should correctly map project properties to environment.project", async () => {
|
|
||||||
const customProject = {
|
|
||||||
...mockEnvironmentData.project,
|
|
||||||
recontactDays: 14,
|
|
||||||
clickOutsideClose: false,
|
|
||||||
overlay: "dark",
|
|
||||||
placement: "center",
|
|
||||||
inAppSurveyBranding: false,
|
|
||||||
styling: { allowStyleOverwrite: true, brandColor: "#ff0000" },
|
|
||||||
};
|
|
||||||
|
|
||||||
vi.mocked(prisma.environment.findUnique).mockResolvedValue({
|
|
||||||
...mockEnvironmentData,
|
|
||||||
project: customProject,
|
|
||||||
} as never);
|
|
||||||
|
|
||||||
const result = await getEnvironmentStateData(environmentId);
|
|
||||||
|
|
||||||
expect(result.environment.project).toEqual({
|
|
||||||
id: "project-123",
|
|
||||||
recontactDays: 14,
|
|
||||||
clickOutsideClose: false,
|
|
||||||
overlay: "dark",
|
|
||||||
placement: "center",
|
|
||||||
inAppSurveyBranding: false,
|
|
||||||
styling: { allowStyleOverwrite: true, brandColor: "#ff0000" },
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should validate environmentId input", async () => {
|
|
||||||
// Invalid CUID should throw validation error
|
|
||||||
await expect(getEnvironmentStateData("invalid-id")).rejects.toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should handle different environment types", async () => {
|
|
||||||
vi.mocked(prisma.environment.findUnique).mockResolvedValue({
|
|
||||||
...mockEnvironmentData,
|
|
||||||
type: "development",
|
|
||||||
} as never);
|
|
||||||
|
|
||||||
const result = await getEnvironmentStateData(environmentId);
|
|
||||||
|
|
||||||
expect(result.environment.type).toBe("development");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should handle appSetupCompleted false", async () => {
|
|
||||||
vi.mocked(prisma.environment.findUnique).mockResolvedValue({
|
|
||||||
...mockEnvironmentData,
|
|
||||||
appSetupCompleted: false,
|
|
||||||
} as never);
|
|
||||||
|
|
||||||
const result = await getEnvironmentStateData(environmentId);
|
|
||||||
|
|
||||||
expect(result.environment.appSetupCompleted).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should correctly extract organization billing data", async () => {
|
|
||||||
const customBilling = {
|
|
||||||
plan: "enterprise",
|
|
||||||
stripeCustomerId: "cus_123",
|
|
||||||
limits: {
|
|
||||||
monthly: { responses: 10000, miu: 50000 },
|
|
||||||
projects: 100,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
vi.mocked(prisma.environment.findUnique).mockResolvedValue({
|
|
||||||
...mockEnvironmentData,
|
|
||||||
project: {
|
|
||||||
...mockEnvironmentData.project,
|
|
||||||
organization: {
|
|
||||||
id: "org-enterprise",
|
|
||||||
billing: customBilling,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as never);
|
|
||||||
|
|
||||||
const result = await getEnvironmentStateData(environmentId);
|
|
||||||
|
|
||||||
expect(result.organization).toEqual({
|
|
||||||
id: "org-enterprise",
|
|
||||||
billing: customBilling,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -10,7 +10,6 @@ import {
|
|||||||
TJsEnvironmentStateSurvey,
|
TJsEnvironmentStateSurvey,
|
||||||
} from "@formbricks/types/js";
|
} from "@formbricks/types/js";
|
||||||
import { validateInputs } from "@/lib/utils/validate";
|
import { validateInputs } from "@/lib/utils/validate";
|
||||||
import { resolveStorageUrlsInObject } from "@/modules/storage/utils";
|
|
||||||
import { transformPrismaSurvey } from "@/modules/survey/lib/utils";
|
import { transformPrismaSurvey } from "@/modules/survey/lib/utils";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,7 +54,7 @@ export const getEnvironmentStateData = async (environmentId: string): Promise<En
|
|||||||
id: true,
|
id: true,
|
||||||
recontactDays: true,
|
recontactDays: true,
|
||||||
clickOutsideClose: true,
|
clickOutsideClose: true,
|
||||||
overlay: true,
|
darkOverlay: true,
|
||||||
placement: true,
|
placement: true,
|
||||||
inAppSurveyBranding: true,
|
inAppSurveyBranding: true,
|
||||||
styling: true,
|
styling: true,
|
||||||
@@ -175,17 +174,17 @@ export const getEnvironmentStateData = async (environmentId: string): Promise<En
|
|||||||
id: environmentData.project.id,
|
id: environmentData.project.id,
|
||||||
recontactDays: environmentData.project.recontactDays,
|
recontactDays: environmentData.project.recontactDays,
|
||||||
clickOutsideClose: environmentData.project.clickOutsideClose,
|
clickOutsideClose: environmentData.project.clickOutsideClose,
|
||||||
overlay: environmentData.project.overlay,
|
darkOverlay: environmentData.project.darkOverlay,
|
||||||
placement: environmentData.project.placement,
|
placement: environmentData.project.placement,
|
||||||
inAppSurveyBranding: environmentData.project.inAppSurveyBranding,
|
inAppSurveyBranding: environmentData.project.inAppSurveyBranding,
|
||||||
styling: resolveStorageUrlsInObject(environmentData.project.styling),
|
styling: environmentData.project.styling,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
organization: {
|
organization: {
|
||||||
id: environmentData.project.organization.id,
|
id: environmentData.project.organization.id,
|
||||||
billing: environmentData.project.organization.billing,
|
billing: environmentData.project.organization.billing,
|
||||||
},
|
},
|
||||||
surveys: resolveStorageUrlsInObject(transformedSurveys),
|
surveys: transformedSurveys,
|
||||||
actionClasses: environmentData.actionClasses as TJsEnvironmentStateActionClass[],
|
actionClasses: environmentData.actionClasses as TJsEnvironmentStateActionClass[],
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ const mockProject: TJsEnvironmentStateProject = {
|
|||||||
inAppSurveyBranding: true,
|
inAppSurveyBranding: true,
|
||||||
placement: "bottomRight",
|
placement: "bottomRight",
|
||||||
clickOutsideClose: true,
|
clickOutsideClose: true,
|
||||||
overlay: "none",
|
darkOverlay: false,
|
||||||
styling: {
|
styling: {
|
||||||
allowStyleOverwrite: false,
|
allowStyleOverwrite: false,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import {
|
||||||
|
GET,
|
||||||
|
OPTIONS,
|
||||||
|
} from "@/modules/ee/contacts/api/v1/client/[environmentId]/identify/contacts/[userId]/route";
|
||||||
|
|
||||||
|
export { GET, OPTIONS };
|
||||||
@@ -1,15 +1,13 @@
|
|||||||
import { NextRequest } from "next/server";
|
import { NextRequest } from "next/server";
|
||||||
import { logger } from "@formbricks/logger";
|
import { logger } from "@formbricks/logger";
|
||||||
import { DatabaseError, InvalidInputError, ResourceNotFoundError } from "@formbricks/types/errors";
|
import { DatabaseError, InvalidInputError, ResourceNotFoundError } from "@formbricks/types/errors";
|
||||||
import { TResponse, TResponseUpdateInput, ZResponseUpdateInput } from "@formbricks/types/responses";
|
import { ZResponseUpdateInput } from "@formbricks/types/responses";
|
||||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
|
||||||
import { responses } from "@/app/lib/api/response";
|
import { responses } from "@/app/lib/api/response";
|
||||||
import { transformErrorToDetails } from "@/app/lib/api/validator";
|
import { transformErrorToDetails } from "@/app/lib/api/validator";
|
||||||
import { withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
|
import { withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
|
||||||
import { sendToPipeline } from "@/app/lib/pipelines";
|
import { sendToPipeline } from "@/app/lib/pipelines";
|
||||||
import { getResponse } from "@/lib/response/service";
|
import { getResponse } from "@/lib/response/service";
|
||||||
import { getSurvey } from "@/lib/survey/service";
|
import { getSurvey } from "@/lib/survey/service";
|
||||||
import { formatValidationErrorsForV1Api, validateResponseData } from "@/modules/api/lib/validation";
|
|
||||||
import { validateOtherOptionLengthForMultipleChoice } from "@/modules/api/v2/lib/element";
|
import { validateOtherOptionLengthForMultipleChoice } from "@/modules/api/v2/lib/element";
|
||||||
import { createQuotaFullObject } from "@/modules/ee/quotas/lib/helpers";
|
import { createQuotaFullObject } from "@/modules/ee/quotas/lib/helpers";
|
||||||
import { validateFileUploads } from "@/modules/storage/utils";
|
import { validateFileUploads } from "@/modules/storage/utils";
|
||||||
@@ -33,35 +31,6 @@ const handleDatabaseError = (error: Error, url: string, endpoint: string, respon
|
|||||||
return responses.internalServerErrorResponse("Unknown error occurred", true);
|
return responses.internalServerErrorResponse("Unknown error occurred", true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateResponse = (
|
|
||||||
response: TResponse,
|
|
||||||
survey: TSurvey,
|
|
||||||
responseUpdateInput: TResponseUpdateInput
|
|
||||||
) => {
|
|
||||||
// Validate response data against validation rules
|
|
||||||
const mergedData = {
|
|
||||||
...response.data,
|
|
||||||
...responseUpdateInput.data,
|
|
||||||
};
|
|
||||||
|
|
||||||
const validationErrors = validateResponseData(
|
|
||||||
survey.blocks,
|
|
||||||
mergedData,
|
|
||||||
responseUpdateInput.language ?? response.language ?? "en",
|
|
||||||
survey.questions
|
|
||||||
);
|
|
||||||
|
|
||||||
if (validationErrors) {
|
|
||||||
return {
|
|
||||||
response: responses.badRequestResponse(
|
|
||||||
"Validation failed",
|
|
||||||
formatValidationErrorsForV1Api(validationErrors),
|
|
||||||
true
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const PUT = withV1ApiWrapper({
|
export const PUT = withV1ApiWrapper({
|
||||||
handler: async ({
|
handler: async ({
|
||||||
req,
|
req,
|
||||||
@@ -144,11 +113,6 @@ export const PUT = withV1ApiWrapper({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const validationResult = validateResponse(response, survey, inputValidation.data);
|
|
||||||
if (validationResult) {
|
|
||||||
return validationResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update response with quota evaluation
|
// update response with quota evaluation
|
||||||
let updatedResponse;
|
let updatedResponse;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -6,14 +6,12 @@ import { ZEnvironmentId } from "@formbricks/types/environment";
|
|||||||
import { InvalidInputError } from "@formbricks/types/errors";
|
import { InvalidInputError } from "@formbricks/types/errors";
|
||||||
import { TResponseWithQuotaFull } from "@formbricks/types/quota";
|
import { TResponseWithQuotaFull } from "@formbricks/types/quota";
|
||||||
import { TResponseInput, ZResponseInput } from "@formbricks/types/responses";
|
import { TResponseInput, ZResponseInput } from "@formbricks/types/responses";
|
||||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
|
||||||
import { responses } from "@/app/lib/api/response";
|
import { responses } from "@/app/lib/api/response";
|
||||||
import { transformErrorToDetails } from "@/app/lib/api/validator";
|
import { transformErrorToDetails } from "@/app/lib/api/validator";
|
||||||
import { withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
|
import { withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
|
||||||
import { sendToPipeline } from "@/app/lib/pipelines";
|
import { sendToPipeline } from "@/app/lib/pipelines";
|
||||||
import { getSurvey } from "@/lib/survey/service";
|
import { getSurvey } from "@/lib/survey/service";
|
||||||
import { getClientIpFromHeaders } from "@/lib/utils/client-ip";
|
import { getClientIpFromHeaders } from "@/lib/utils/client-ip";
|
||||||
import { formatValidationErrorsForV1Api, validateResponseData } from "@/modules/api/lib/validation";
|
|
||||||
import { getIsContactsEnabled } from "@/modules/ee/license-check/lib/utils";
|
import { getIsContactsEnabled } from "@/modules/ee/license-check/lib/utils";
|
||||||
import { createQuotaFullObject } from "@/modules/ee/quotas/lib/helpers";
|
import { createQuotaFullObject } from "@/modules/ee/quotas/lib/helpers";
|
||||||
import { validateFileUploads } from "@/modules/storage/utils";
|
import { validateFileUploads } from "@/modules/storage/utils";
|
||||||
@@ -35,26 +33,6 @@ export const OPTIONS = async (): Promise<Response> => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateResponse = (responseInputData: TResponseInput, survey: TSurvey) => {
|
|
||||||
// Validate response data against validation rules
|
|
||||||
const validationErrors = validateResponseData(
|
|
||||||
survey.blocks,
|
|
||||||
responseInputData.data,
|
|
||||||
responseInputData.language ?? "en",
|
|
||||||
survey.questions
|
|
||||||
);
|
|
||||||
|
|
||||||
if (validationErrors) {
|
|
||||||
return {
|
|
||||||
response: responses.badRequestResponse(
|
|
||||||
"Validation failed",
|
|
||||||
formatValidationErrorsForV1Api(validationErrors),
|
|
||||||
true
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const POST = withV1ApiWrapper({
|
export const POST = withV1ApiWrapper({
|
||||||
handler: async ({ req, props }: { req: NextRequest; props: Context }) => {
|
handler: async ({ req, props }: { req: NextRequest; props: Context }) => {
|
||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
@@ -145,11 +123,6 @@ export const POST = withV1ApiWrapper({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const validationResult = validateResponse(responseInputData, survey);
|
|
||||||
if (validationResult) {
|
|
||||||
return validationResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
let response: TResponseWithQuotaFull;
|
let response: TResponseWithQuotaFull;
|
||||||
try {
|
try {
|
||||||
const meta: TResponseInput["meta"] = {
|
const meta: TResponseInput["meta"] = {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ImageResponse } from "next/og";
|
import { ImageResponse } from "@vercel/og";
|
||||||
import { NextRequest } from "next/server";
|
import { NextRequest } from "next/server";
|
||||||
|
|
||||||
export const GET = async (req: NextRequest) => {
|
export const GET = async (req: NextRequest) => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { NextRequest } from "next/server";
|
import { NextRequest } from "next/server";
|
||||||
import { TIntegrationNotionConfigData, TIntegrationNotionInput } from "@formbricks/types/integration/notion";
|
import { TIntegrationNotionConfigData, TIntegrationNotionInput } from "@formbricks/types/integration/notion";
|
||||||
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 { withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
|
||||||
import {
|
import {
|
||||||
ENCRYPTION_KEY,
|
ENCRYPTION_KEY,
|
||||||
NOTION_OAUTH_CLIENT_ID,
|
NOTION_OAUTH_CLIENT_ID,
|
||||||
@@ -10,17 +10,10 @@ import {
|
|||||||
WEBAPP_URL,
|
WEBAPP_URL,
|
||||||
} from "@/lib/constants";
|
} from "@/lib/constants";
|
||||||
import { symmetricEncrypt } from "@/lib/crypto";
|
import { symmetricEncrypt } from "@/lib/crypto";
|
||||||
import { hasUserEnvironmentAccess } from "@/lib/environment/auth";
|
|
||||||
import { createOrUpdateIntegration, getIntegrationByType } from "@/lib/integration/service";
|
import { createOrUpdateIntegration, getIntegrationByType } from "@/lib/integration/service";
|
||||||
|
|
||||||
export const GET = withV1ApiWrapper({
|
export const GET = withV1ApiWrapper({
|
||||||
handler: async ({
|
handler: async ({ req }: { req: NextRequest }) => {
|
||||||
req,
|
|
||||||
authentication,
|
|
||||||
}: {
|
|
||||||
req: NextRequest;
|
|
||||||
authentication: NonNullable<TSessionAuthentication>;
|
|
||||||
}) => {
|
|
||||||
const url = req.url;
|
const url = req.url;
|
||||||
const queryParams = new URLSearchParams(url.split("?")[1]); // Split the URL and get the query parameters
|
const queryParams = new URLSearchParams(url.split("?")[1]); // Split the URL and get the query parameters
|
||||||
const environmentId = queryParams.get("state"); // Get the value of the 'state' parameter
|
const environmentId = queryParams.get("state"); // Get the value of the 'state' parameter
|
||||||
@@ -33,13 +26,6 @@ export const GET = withV1ApiWrapper({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const canUserAccessEnvironment = await hasUserEnvironmentAccess(authentication.user.id, environmentId);
|
|
||||||
if (!canUserAccessEnvironment) {
|
|
||||||
return {
|
|
||||||
response: responses.unauthorizedResponse(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (code && typeof code !== "string") {
|
if (code && typeof code !== "string") {
|
||||||
return {
|
return {
|
||||||
response: responses.badRequestResponse("`code` must be a string"),
|
response: responses.badRequestResponse("`code` must be a string"),
|
||||||
|
|||||||
@@ -5,19 +5,12 @@ import {
|
|||||||
TIntegrationSlackCredential,
|
TIntegrationSlackCredential,
|
||||||
} 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 { withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
|
||||||
import { SLACK_CLIENT_ID, SLACK_CLIENT_SECRET, WEBAPP_URL } from "@/lib/constants";
|
import { SLACK_CLIENT_ID, SLACK_CLIENT_SECRET, WEBAPP_URL } from "@/lib/constants";
|
||||||
import { hasUserEnvironmentAccess } from "@/lib/environment/auth";
|
|
||||||
import { createOrUpdateIntegration, getIntegrationByType } from "@/lib/integration/service";
|
import { createOrUpdateIntegration, getIntegrationByType } from "@/lib/integration/service";
|
||||||
|
|
||||||
export const GET = withV1ApiWrapper({
|
export const GET = withV1ApiWrapper({
|
||||||
handler: async ({
|
handler: async ({ req }: { req: NextRequest }) => {
|
||||||
req,
|
|
||||||
authentication,
|
|
||||||
}: {
|
|
||||||
req: NextRequest;
|
|
||||||
authentication: NonNullable<TSessionAuthentication>;
|
|
||||||
}) => {
|
|
||||||
const url = req.url;
|
const url = req.url;
|
||||||
const queryParams = new URLSearchParams(url.split("?")[1]); // Split the URL and get the query parameters
|
const queryParams = new URLSearchParams(url.split("?")[1]); // Split the URL and get the query parameters
|
||||||
const environmentId = queryParams.get("state"); // Get the value of the 'state' parameter
|
const environmentId = queryParams.get("state"); // Get the value of the 'state' parameter
|
||||||
@@ -30,13 +23,6 @@ export const GET = withV1ApiWrapper({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const canUserAccessEnvironment = await hasUserEnvironmentAccess(authentication.user.id, environmentId);
|
|
||||||
if (!canUserAccessEnvironment) {
|
|
||||||
return {
|
|
||||||
response: responses.unauthorizedResponse(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (code && typeof code !== "string") {
|
if (code && typeof code !== "string") {
|
||||||
return {
|
return {
|
||||||
response: responses.badRequestResponse("`code` must be a string"),
|
response: responses.badRequestResponse("`code` must be a string"),
|
||||||
|
|||||||
@@ -8,9 +8,12 @@ import { TApiAuditLog, TApiKeyAuthentication, withV1ApiWrapper } from "@/app/lib
|
|||||||
import { sendToPipeline } from "@/app/lib/pipelines";
|
import { sendToPipeline } from "@/app/lib/pipelines";
|
||||||
import { deleteResponse, getResponse } from "@/lib/response/service";
|
import { deleteResponse, getResponse } from "@/lib/response/service";
|
||||||
import { getSurvey } from "@/lib/survey/service";
|
import { getSurvey } from "@/lib/survey/service";
|
||||||
import { formatValidationErrorsForV1Api, validateResponseData } from "@/modules/api/lib/validation";
|
import {
|
||||||
|
formatValidationErrorsForV1Api,
|
||||||
|
validateResponseData,
|
||||||
|
} from "@/modules/api/v2/management/responses/lib/validation";
|
||||||
import { hasPermission } from "@/modules/organization/settings/api-keys/lib/utils";
|
import { hasPermission } from "@/modules/organization/settings/api-keys/lib/utils";
|
||||||
import { resolveStorageUrlsInObject, validateFileUploads } from "@/modules/storage/utils";
|
import { validateFileUploads } from "@/modules/storage/utils";
|
||||||
import { updateResponseWithQuotaEvaluation } from "./lib/response";
|
import { updateResponseWithQuotaEvaluation } from "./lib/response";
|
||||||
|
|
||||||
async function fetchAndAuthorizeResponse(
|
async function fetchAndAuthorizeResponse(
|
||||||
@@ -57,10 +60,7 @@ export const GET = withV1ApiWrapper({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
response: responses.successResponse({
|
response: responses.successResponse(result.response),
|
||||||
...result.response,
|
|
||||||
data: resolveStorageUrlsInObject(result.response.data),
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {
|
return {
|
||||||
@@ -192,7 +192,7 @@ export const PUT = withV1ApiWrapper({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
response: responses.successResponse({ ...updated, data: resolveStorageUrlsInObject(updated.data) }),
|
response: responses.successResponse(updated),
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -7,9 +7,12 @@ import { transformErrorToDetails } from "@/app/lib/api/validator";
|
|||||||
import { TApiAuditLog, TApiKeyAuthentication, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
|
import { TApiAuditLog, TApiKeyAuthentication, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
|
||||||
import { sendToPipeline } from "@/app/lib/pipelines";
|
import { sendToPipeline } from "@/app/lib/pipelines";
|
||||||
import { getSurvey } from "@/lib/survey/service";
|
import { getSurvey } from "@/lib/survey/service";
|
||||||
import { formatValidationErrorsForV1Api, validateResponseData } from "@/modules/api/lib/validation";
|
import {
|
||||||
|
formatValidationErrorsForV1Api,
|
||||||
|
validateResponseData,
|
||||||
|
} from "@/modules/api/v2/management/responses/lib/validation";
|
||||||
import { hasPermission } from "@/modules/organization/settings/api-keys/lib/utils";
|
import { hasPermission } from "@/modules/organization/settings/api-keys/lib/utils";
|
||||||
import { resolveStorageUrlsInObject, validateFileUploads } from "@/modules/storage/utils";
|
import { validateFileUploads } from "@/modules/storage/utils";
|
||||||
import {
|
import {
|
||||||
createResponseWithQuotaEvaluation,
|
createResponseWithQuotaEvaluation,
|
||||||
getResponses,
|
getResponses,
|
||||||
@@ -54,9 +57,7 @@ export const GET = withV1ApiWrapper({
|
|||||||
allResponses.push(...environmentResponses);
|
allResponses.push(...environmentResponses);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
response: responses.successResponse(
|
response: responses.successResponse(allResponses),
|
||||||
allResponses.map((r) => ({ ...r, data: resolveStorageUrlsInObject(r.data) }))
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof DatabaseError) {
|
if (error instanceof DatabaseError) {
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import { TApiAuditLog, TApiKeyAuthentication, withV1ApiWrapper } from "@/app/lib
|
|||||||
import { getOrganizationByEnvironmentId } from "@/lib/organization/service";
|
import { getOrganizationByEnvironmentId } from "@/lib/organization/service";
|
||||||
import { getSurvey, updateSurvey } from "@/lib/survey/service";
|
import { getSurvey, updateSurvey } from "@/lib/survey/service";
|
||||||
import { hasPermission } from "@/modules/organization/settings/api-keys/lib/utils";
|
import { hasPermission } from "@/modules/organization/settings/api-keys/lib/utils";
|
||||||
import { resolveStorageUrlsInObject } from "@/modules/storage/utils";
|
|
||||||
|
|
||||||
const fetchAndAuthorizeSurvey = async (
|
const fetchAndAuthorizeSurvey = async (
|
||||||
surveyId: string,
|
surveyId: string,
|
||||||
@@ -59,18 +58,16 @@ export const GET = withV1ApiWrapper({
|
|||||||
|
|
||||||
if (shouldTransformToQuestions) {
|
if (shouldTransformToQuestions) {
|
||||||
return {
|
return {
|
||||||
response: responses.successResponse(
|
response: responses.successResponse({
|
||||||
resolveStorageUrlsInObject({
|
...result.survey,
|
||||||
...result.survey,
|
questions: transformBlocksToQuestions(result.survey.blocks, result.survey.endings),
|
||||||
questions: transformBlocksToQuestions(result.survey.blocks, result.survey.endings),
|
blocks: [],
|
||||||
blocks: [],
|
}),
|
||||||
})
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
response: responses.successResponse(resolveStorageUrlsInObject(result.survey)),
|
response: responses.successResponse(result.survey),
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {
|
return {
|
||||||
@@ -205,12 +202,12 @@ export const PUT = withV1ApiWrapper({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
response: responses.successResponse(resolveStorageUrlsInObject(surveyWithQuestions)),
|
response: responses.successResponse(surveyWithQuestions),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
response: responses.successResponse(resolveStorageUrlsInObject(updatedSurvey)),
|
response: responses.successResponse(updatedSurvey),
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import { TApiAuditLog, TApiKeyAuthentication, withV1ApiWrapper } from "@/app/lib
|
|||||||
import { getOrganizationByEnvironmentId } from "@/lib/organization/service";
|
import { getOrganizationByEnvironmentId } from "@/lib/organization/service";
|
||||||
import { createSurvey } from "@/lib/survey/service";
|
import { createSurvey } from "@/lib/survey/service";
|
||||||
import { hasPermission } from "@/modules/organization/settings/api-keys/lib/utils";
|
import { hasPermission } from "@/modules/organization/settings/api-keys/lib/utils";
|
||||||
import { resolveStorageUrlsInObject } from "@/modules/storage/utils";
|
|
||||||
import { getSurveys } from "./lib/surveys";
|
import { getSurveys } from "./lib/surveys";
|
||||||
|
|
||||||
export const GET = withV1ApiWrapper({
|
export const GET = withV1ApiWrapper({
|
||||||
@@ -56,7 +55,7 @@ export const GET = withV1ApiWrapper({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
response: responses.successResponse(resolveStorageUrlsInObject(surveysWithQuestions)),
|
response: responses.successResponse(surveysWithQuestions),
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof DatabaseError) {
|
if (error instanceof DatabaseError) {
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import {
|
||||||
|
OPTIONS,
|
||||||
|
PUT,
|
||||||
|
} from "@/modules/ee/contacts/api/v1/client/[environmentId]/contacts/[userId]/attributes/route";
|
||||||
|
|
||||||
|
export { OPTIONS, PUT };
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import {
|
||||||
|
GET,
|
||||||
|
OPTIONS,
|
||||||
|
} from "@/modules/ee/contacts/api/v1/client/[environmentId]/identify/contacts/[userId]/route";
|
||||||
|
|
||||||
|
export { GET, OPTIONS };
|
||||||
@@ -11,7 +11,6 @@ import { sendToPipeline } from "@/app/lib/pipelines";
|
|||||||
import { getSurvey } from "@/lib/survey/service";
|
import { getSurvey } from "@/lib/survey/service";
|
||||||
import { getElementsFromBlocks } from "@/lib/survey/utils";
|
import { getElementsFromBlocks } from "@/lib/survey/utils";
|
||||||
import { getClientIpFromHeaders } from "@/lib/utils/client-ip";
|
import { getClientIpFromHeaders } from "@/lib/utils/client-ip";
|
||||||
import { formatValidationErrorsForV1Api, validateResponseData } from "@/modules/api/lib/validation";
|
|
||||||
import { validateOtherOptionLengthForMultipleChoice } from "@/modules/api/v2/lib/element";
|
import { validateOtherOptionLengthForMultipleChoice } from "@/modules/api/v2/lib/element";
|
||||||
import { getIsContactsEnabled } from "@/modules/ee/license-check/lib/utils";
|
import { getIsContactsEnabled } from "@/modules/ee/license-check/lib/utils";
|
||||||
import { createQuotaFullObject } from "@/modules/ee/quotas/lib/helpers";
|
import { createQuotaFullObject } from "@/modules/ee/quotas/lib/helpers";
|
||||||
@@ -107,22 +106,6 @@ export const POST = async (request: Request, context: Context): Promise<Response
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate response data against validation rules
|
|
||||||
const validationErrors = validateResponseData(
|
|
||||||
survey.blocks,
|
|
||||||
responseInputData.data,
|
|
||||||
responseInputData.language ?? "en",
|
|
||||||
survey.questions
|
|
||||||
);
|
|
||||||
|
|
||||||
if (validationErrors) {
|
|
||||||
return responses.badRequestResponse(
|
|
||||||
"Validation failed",
|
|
||||||
formatValidationErrorsForV1Api(validationErrors),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let response: TResponseWithQuotaFull;
|
let response: TResponseWithQuotaFull;
|
||||||
try {
|
try {
|
||||||
const meta: TResponseInputV2["meta"] = {
|
const meta: TResponseInputV2["meta"] = {
|
||||||
|
|||||||
@@ -3,9 +3,8 @@
|
|||||||
// Error components must be Client components
|
// Error components must be Client components
|
||||||
import * as Sentry from "@sentry/nextjs";
|
import * as Sentry from "@sentry/nextjs";
|
||||||
import { TFunction } from "i18next";
|
import { TFunction } from "i18next";
|
||||||
import { useEffect } from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { type ClientErrorType, getClientErrorData, isExpectedError } from "@formbricks/types/errors";
|
import { type ClientErrorType, getClientErrorData } from "@formbricks/types/errors";
|
||||||
import { Button } from "@/modules/ui/components/button";
|
import { Button } from "@/modules/ui/components/button";
|
||||||
import { ErrorComponent } from "@/modules/ui/components/error-component";
|
import { ErrorComponent } from "@/modules/ui/components/error-component";
|
||||||
|
|
||||||
@@ -31,13 +30,11 @@ const ErrorBoundary = ({ error, reset }: { error: Error; reset: () => void }) =>
|
|||||||
const errorData = getClientErrorData(error);
|
const errorData = getClientErrorData(error);
|
||||||
const { title, description } = getErrorMessages(errorData.type, t);
|
const { title, description } = getErrorMessages(errorData.type, t);
|
||||||
|
|
||||||
useEffect(() => {
|
if (process.env.NODE_ENV === "development") {
|
||||||
if (process.env.NODE_ENV === "development") {
|
console.error(error.message);
|
||||||
console.error(error.message);
|
} else {
|
||||||
} else if (!isExpectedError(error)) {
|
Sentry.captureException(error);
|
||||||
Sentry.captureException(error);
|
}
|
||||||
}
|
|
||||||
}, [error]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full w-full flex-col items-center justify-center">
|
<div className="flex h-full w-full flex-col items-center justify-center">
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ vi.mock("@/app/middleware/endpoint-validator", async () => {
|
|||||||
isClientSideApiRoute: vi.fn().mockReturnValue({ isClientSideApi: false, isRateLimited: true }),
|
isClientSideApiRoute: vi.fn().mockReturnValue({ isClientSideApi: false, isRateLimited: true }),
|
||||||
isManagementApiRoute: vi.fn().mockReturnValue({ isManagementApi: false, authenticationMethod: "apiKey" }),
|
isManagementApiRoute: vi.fn().mockReturnValue({ isManagementApi: false, authenticationMethod: "apiKey" }),
|
||||||
isIntegrationRoute: vi.fn().mockReturnValue(false),
|
isIntegrationRoute: vi.fn().mockReturnValue(false),
|
||||||
|
isSyncWithUserIdentificationEndpoint: vi.fn().mockReturnValue(null),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -81,6 +82,7 @@ vi.mock("@/modules/core/rate-limit/rate-limit-configs", () => ({
|
|||||||
api: {
|
api: {
|
||||||
client: { windowMs: 60000, max: 100 },
|
client: { windowMs: 60000, max: 100 },
|
||||||
v1: { windowMs: 60000, max: 1000 },
|
v1: { windowMs: 60000, max: 1000 },
|
||||||
|
syncUserIdentification: { windowMs: 60000, max: 50 },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
@@ -461,6 +463,45 @@ describe("withV1ApiWrapper", () => {
|
|||||||
expect(handler).not.toHaveBeenCalled();
|
expect(handler).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("handles sync user identification rate limiting", async () => {
|
||||||
|
const { applyRateLimit, applyIPRateLimit } = await import("@/modules/core/rate-limit/helpers");
|
||||||
|
const {
|
||||||
|
isClientSideApiRoute,
|
||||||
|
isManagementApiRoute,
|
||||||
|
isIntegrationRoute,
|
||||||
|
isSyncWithUserIdentificationEndpoint,
|
||||||
|
} = await import("@/app/middleware/endpoint-validator");
|
||||||
|
const { authenticateRequest } = await import("@/app/api/v1/auth");
|
||||||
|
|
||||||
|
vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: true, isRateLimited: true });
|
||||||
|
vi.mocked(isManagementApiRoute).mockReturnValue({
|
||||||
|
isManagementApi: false,
|
||||||
|
authenticationMethod: AuthenticationMethod.None,
|
||||||
|
});
|
||||||
|
vi.mocked(isIntegrationRoute).mockReturnValue(false);
|
||||||
|
vi.mocked(isSyncWithUserIdentificationEndpoint).mockReturnValue({
|
||||||
|
userId: "user-123",
|
||||||
|
environmentId: "env-123",
|
||||||
|
});
|
||||||
|
vi.mocked(authenticateRequest).mockResolvedValue(null);
|
||||||
|
vi.mocked(applyIPRateLimit).mockResolvedValue(undefined);
|
||||||
|
const rateLimitError = new Error("Sync rate limit exceeded");
|
||||||
|
rateLimitError.message = "Sync rate limit exceeded";
|
||||||
|
vi.mocked(applyRateLimit).mockRejectedValue(rateLimitError);
|
||||||
|
|
||||||
|
const handler = vi.fn();
|
||||||
|
const req = createMockRequest({ url: "/api/v1/client/env-123/app/sync/user-123" });
|
||||||
|
const { withV1ApiWrapper } = await import("./with-api-logging");
|
||||||
|
const wrapped = withV1ApiWrapper({ handler });
|
||||||
|
const res = await wrapped(req, undefined);
|
||||||
|
|
||||||
|
expect(res.status).toBe(429);
|
||||||
|
expect(applyRateLimit).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ windowMs: 60000, max: 50 }),
|
||||||
|
"user-123"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
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 } = (await import(
|
const { queueAuditEvent: mockedQueueAuditEvent } = (await import(
|
||||||
"@/modules/ee/audit-logs/lib/handler"
|
"@/modules/ee/audit-logs/lib/handler"
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
isClientSideApiRoute,
|
isClientSideApiRoute,
|
||||||
isIntegrationRoute,
|
isIntegrationRoute,
|
||||||
isManagementApiRoute,
|
isManagementApiRoute,
|
||||||
|
isSyncWithUserIdentificationEndpoint,
|
||||||
} from "@/app/middleware/endpoint-validator";
|
} from "@/app/middleware/endpoint-validator";
|
||||||
import { AUDIT_LOG_ENABLED, IS_PRODUCTION, SENTRY_DSN } from "@/lib/constants";
|
import { AUDIT_LOG_ENABLED, IS_PRODUCTION, SENTRY_DSN } from "@/lib/constants";
|
||||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||||
@@ -47,16 +48,23 @@ enum ApiV1RouteTypeEnum {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply client-side API rate limiting (IP-based)
|
* Apply client-side API rate limiting (IP-based or sync-specific)
|
||||||
*/
|
*/
|
||||||
const applyClientRateLimit = async (customRateLimitConfig?: TRateLimitConfig): Promise<void> => {
|
const applyClientRateLimit = async (url: string, customRateLimitConfig?: TRateLimitConfig): Promise<void> => {
|
||||||
await applyIPRateLimit(customRateLimitConfig ?? rateLimitConfigs.api.client);
|
const syncEndpoint = isSyncWithUserIdentificationEndpoint(url);
|
||||||
|
if (syncEndpoint) {
|
||||||
|
const syncRateLimitConfig = rateLimitConfigs.api.syncUserIdentification;
|
||||||
|
await applyRateLimit(syncRateLimitConfig, syncEndpoint.userId);
|
||||||
|
} else {
|
||||||
|
await applyIPRateLimit(customRateLimitConfig ?? rateLimitConfigs.api.client);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle rate limiting based on authentication and API type
|
* Handle rate limiting based on authentication and API type
|
||||||
*/
|
*/
|
||||||
const handleRateLimiting = async (
|
const handleRateLimiting = async (
|
||||||
|
url: string,
|
||||||
authentication: TApiV1Authentication,
|
authentication: TApiV1Authentication,
|
||||||
routeType: ApiV1RouteTypeEnum,
|
routeType: ApiV1RouteTypeEnum,
|
||||||
customRateLimitConfig?: TRateLimitConfig
|
customRateLimitConfig?: TRateLimitConfig
|
||||||
@@ -76,7 +84,7 @@ const handleRateLimiting = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (routeType === ApiV1RouteTypeEnum.Client) {
|
if (routeType === ApiV1RouteTypeEnum.Client) {
|
||||||
await applyClientRateLimit(customRateLimitConfig);
|
await applyClientRateLimit(url, customRateLimitConfig);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return responses.tooManyRequestsResponse(error.message);
|
return responses.tooManyRequestsResponse(error.message);
|
||||||
@@ -247,6 +255,7 @@ const getRouteType = (
|
|||||||
* Features:
|
* Features:
|
||||||
* - Performs authentication once and passes result to handler
|
* - Performs authentication once and passes result to handler
|
||||||
* - Applies API key-based rate limiting with differentiated limits for client vs management APIs
|
* - Applies API key-based rate limiting with differentiated limits for client vs management APIs
|
||||||
|
* - Includes additional sync user identification rate limiting for client-side sync endpoints
|
||||||
* - Sets userId and organizationId in audit log automatically when audit logging is enabled
|
* - Sets userId and organizationId in audit log automatically when audit logging is enabled
|
||||||
* - System and Sentry logs are always called for non-success responses
|
* - System and Sentry logs are always called for non-success responses
|
||||||
* - Uses function overloads to provide type safety without requiring type guards
|
* - Uses function overloads to provide type safety without requiring type guards
|
||||||
@@ -319,7 +328,12 @@ export const withV1ApiWrapper: {
|
|||||||
|
|
||||||
// === Rate Limiting ===
|
// === Rate Limiting ===
|
||||||
if (isRateLimited) {
|
if (isRateLimited) {
|
||||||
const rateLimitResponse = await handleRateLimiting(authentication, routeType, customRateLimitConfig);
|
const rateLimitResponse = await handleRateLimiting(
|
||||||
|
req.nextUrl.pathname,
|
||||||
|
authentication,
|
||||||
|
routeType,
|
||||||
|
customRateLimitConfig
|
||||||
|
);
|
||||||
if (rateLimitResponse) return rateLimitResponse;
|
if (rateLimitResponse) return rateLimitResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4848,14 +4848,12 @@ export const previewSurvey = (projectName: string, t: TFunction): TSurvey => {
|
|||||||
t("templates.preview_survey_question_2_choice_2_label"),
|
t("templates.preview_survey_question_2_choice_2_label"),
|
||||||
],
|
],
|
||||||
headline: t("templates.preview_survey_question_2_headline"),
|
headline: t("templates.preview_survey_question_2_headline"),
|
||||||
subheader: t("templates.preview_survey_question_2_subheader"),
|
|
||||||
required: true,
|
required: true,
|
||||||
shuffleOption: "none",
|
shuffleOption: "none",
|
||||||
}),
|
}),
|
||||||
isDraft: true,
|
isDraft: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
buttonLabel: createI18nString(t("templates.next"), []),
|
|
||||||
backButtonLabel: createI18nString(t("templates.preview_survey_question_2_back_button_label"), []),
|
backButtonLabel: createI18nString(t("templates.preview_survey_question_2_back_button_label"), []),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
isManagementApiRoute,
|
isManagementApiRoute,
|
||||||
isPublicDomainRoute,
|
isPublicDomainRoute,
|
||||||
isRouteAllowedForDomain,
|
isRouteAllowedForDomain,
|
||||||
|
isSyncWithUserIdentificationEndpoint,
|
||||||
} from "./endpoint-validator";
|
} from "./endpoint-validator";
|
||||||
|
|
||||||
describe("endpoint-validator", () => {
|
describe("endpoint-validator", () => {
|
||||||
@@ -257,7 +258,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);
|
||||||
});
|
});
|
||||||
@@ -270,6 +270,58 @@ describe("endpoint-validator", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("isSyncWithUserIdentificationEndpoint", () => {
|
||||||
|
test("should return environmentId and userId for valid sync URLs", () => {
|
||||||
|
const result1 = isSyncWithUserIdentificationEndpoint("/api/v1/client/env123/app/sync/user456");
|
||||||
|
expect(result1).toEqual({
|
||||||
|
environmentId: "env123",
|
||||||
|
userId: "user456",
|
||||||
|
});
|
||||||
|
|
||||||
|
const result2 = isSyncWithUserIdentificationEndpoint("/api/v1/client/abc-123/app/sync/xyz-789");
|
||||||
|
expect(result2).toEqual({
|
||||||
|
environmentId: "abc-123",
|
||||||
|
userId: "xyz-789",
|
||||||
|
});
|
||||||
|
|
||||||
|
const result3 = isSyncWithUserIdentificationEndpoint(
|
||||||
|
"/api/v1/client/env_123_test/app/sync/user_456_test"
|
||||||
|
);
|
||||||
|
expect(result3).toEqual({
|
||||||
|
environmentId: "env_123_test",
|
||||||
|
userId: "user_456_test",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should handle optional trailing slash", () => {
|
||||||
|
// Test both with and without trailing slash
|
||||||
|
const result1 = isSyncWithUserIdentificationEndpoint("/api/v1/client/env123/app/sync/user456");
|
||||||
|
expect(result1).toEqual({
|
||||||
|
environmentId: "env123",
|
||||||
|
userId: "user456",
|
||||||
|
});
|
||||||
|
|
||||||
|
const result2 = isSyncWithUserIdentificationEndpoint("/api/v1/client/env123/app/sync/user456/");
|
||||||
|
expect(result2).toEqual({
|
||||||
|
environmentId: "env123",
|
||||||
|
userId: "user456",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should return false for invalid sync URLs", () => {
|
||||||
|
expect(isSyncWithUserIdentificationEndpoint("/api/v1/client/env123/app/sync")).toBe(false);
|
||||||
|
expect(isSyncWithUserIdentificationEndpoint("/api/v1/client/env123/something")).toBe(false);
|
||||||
|
expect(isSyncWithUserIdentificationEndpoint("/api/something")).toBe(false);
|
||||||
|
expect(isSyncWithUserIdentificationEndpoint("/api/v1/client/env123/app/other/user456")).toBe(false);
|
||||||
|
expect(isSyncWithUserIdentificationEndpoint("/api/v2/client/env123/app/sync/user456")).toBe(false); // only v1 supported
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should handle empty or malformed IDs", () => {
|
||||||
|
expect(isSyncWithUserIdentificationEndpoint("/api/v1/client//app/sync/user456")).toBe(false);
|
||||||
|
expect(isSyncWithUserIdentificationEndpoint("/api/v1/client/env123/app/sync/")).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("isPublicDomainRoute", () => {
|
describe("isPublicDomainRoute", () => {
|
||||||
test("should return true for health endpoint", () => {
|
test("should return true for health endpoint", () => {
|
||||||
expect(isPublicDomainRoute("/health")).toBe(true);
|
expect(isPublicDomainRoute("/health")).toBe(true);
|
||||||
@@ -313,19 +365,6 @@ describe("endpoint-validator", () => {
|
|||||||
expect(isPublicDomainRoute("/c")).toBe(false);
|
expect(isPublicDomainRoute("/c")).toBe(false);
|
||||||
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);
|
||||||
@@ -389,8 +428,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 +443,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 +479,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 +493,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 +503,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 +511,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 +531,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 +582,12 @@ 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);
|
expect(
|
||||||
|
isSyncWithUserIdentificationEndpoint("/api/v1/client/env-123_test/app/sync/user-456_test")
|
||||||
|
).toEqual({
|
||||||
|
environmentId: "env-123_test",
|
||||||
|
userId: "user-456_test",
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -564,7 +595,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 +602,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 +615,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,
|
||||||
@@ -601,6 +628,15 @@ describe("endpoint-validator", () => {
|
|||||||
const longSurveyId = "a".repeat(1000);
|
const longSurveyId = "a".repeat(1000);
|
||||||
const longPath = `s/${longSurveyId}`;
|
const longPath = `s/${longSurveyId}`;
|
||||||
expect(isPublicDomainRoute(`/${longPath}`)).toBe(true);
|
expect(isPublicDomainRoute(`/${longPath}`)).toBe(true);
|
||||||
|
|
||||||
|
const longEnvironmentId = "env" + "a".repeat(1000);
|
||||||
|
const longUserId = "user" + "b".repeat(1000);
|
||||||
|
expect(
|
||||||
|
isSyncWithUserIdentificationEndpoint(`/api/v1/client/${longEnvironmentId}/app/sync/${longUserId}`)
|
||||||
|
).toEqual({
|
||||||
|
environmentId: longEnvironmentId,
|
||||||
|
userId: longUserId,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should handle empty and minimal inputs", () => {
|
test("should handle empty and minimal inputs", () => {
|
||||||
@@ -615,6 +651,7 @@ describe("endpoint-validator", () => {
|
|||||||
});
|
});
|
||||||
expect(isIntegrationRoute("")).toBe(false);
|
expect(isIntegrationRoute("")).toBe(false);
|
||||||
expect(isAuthProtectedRoute("")).toBe(false);
|
expect(isAuthProtectedRoute("")).toBe(false);
|
||||||
|
expect(isSyncWithUserIdentificationEndpoint("")).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -623,7 +660,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,
|
||||||
|
|||||||
@@ -43,6 +43,14 @@ export const isAuthProtectedRoute = (url: string): boolean => {
|
|||||||
return protectedRoutes.some((route) => url.startsWith(route));
|
return protectedRoutes.some((route) => url.startsWith(route));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isSyncWithUserIdentificationEndpoint = (
|
||||||
|
url: string
|
||||||
|
): { environmentId: string; userId: string } | false => {
|
||||||
|
const regex = /\/api\/v1\/client\/(?<environmentId>[^/]+)\/app\/sync\/(?<userId>[^/]+)/;
|
||||||
|
const match = url.match(regex);
|
||||||
|
return match ? { environmentId: match.groups!.environmentId, userId: match.groups!.userId } : false;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the route should be accessible on the public domain (PUBLIC_URL)
|
* Check if the route should be accessible on the public domain (PUBLIC_URL)
|
||||||
* Uses whitelist approach - only explicitly allowed routes are accessible
|
* Uses whitelist approach - only explicitly allowed routes are accessible
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { authorizePrivateDownload } from "@/app/storage/[environmentId]/[accessT
|
|||||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||||
import { applyRateLimit } from "@/modules/core/rate-limit/helpers";
|
import { applyRateLimit } from "@/modules/core/rate-limit/helpers";
|
||||||
import { rateLimitConfigs } from "@/modules/core/rate-limit/rate-limit-configs";
|
import { rateLimitConfigs } from "@/modules/core/rate-limit/rate-limit-configs";
|
||||||
import { deleteFile, getFileStreamForDownload } from "@/modules/storage/service";
|
import { deleteFile, getSignedUrlForDownload } from "@/modules/storage/service";
|
||||||
import { getErrorResponseFromStorageError } from "@/modules/storage/utils";
|
import { getErrorResponseFromStorageError } from "@/modules/storage/utils";
|
||||||
import { logFileDeletion } from "./lib/audit-logs";
|
import { logFileDeletion } from "./lib/audit-logs";
|
||||||
|
|
||||||
@@ -39,25 +39,21 @@ export const GET = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stream the file directly
|
const signedUrlResult = await getSignedUrlForDownload(fileName, environmentId, accessType);
|
||||||
const streamResult = await getFileStreamForDownload(fileName, environmentId, accessType);
|
|
||||||
|
|
||||||
if (!streamResult.ok) {
|
if (!signedUrlResult.ok) {
|
||||||
const errorResponse = getErrorResponseFromStorageError(streamResult.error, { fileName });
|
const errorResponse = getErrorResponseFromStorageError(signedUrlResult.error, { fileName });
|
||||||
return errorResponse;
|
return errorResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { body, contentType, contentLength } = streamResult.data;
|
return new Response(null, {
|
||||||
|
status: 302,
|
||||||
return new Response(body, {
|
|
||||||
status: 200,
|
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": contentType,
|
Location: signedUrlResult.data,
|
||||||
...(contentLength > 0 && { "Content-Length": String(contentLength) }),
|
|
||||||
"Cache-Control":
|
"Cache-Control":
|
||||||
accessType === "private"
|
accessType === "private"
|
||||||
? "no-store, no-cache, must-revalidate"
|
? "no-store, no-cache, must-revalidate"
|
||||||
: "public, max-age=31536000, immutable",
|
: "public, max-age=300, s-maxage=300, stale-while-revalidate=300",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
+236
-389
File diff suppressed because it is too large
Load Diff
@@ -1,205 +1,59 @@
|
|||||||
// OpenTelemetry instrumentation for Next.js - loaded via instrumentation.ts hook
|
// instrumentation-node.ts
|
||||||
// Pattern based on: ee/src/opentelemetry.ts (license server)
|
|
||||||
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
|
|
||||||
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
|
|
||||||
import { PrometheusExporter } from "@opentelemetry/exporter-prometheus";
|
import { PrometheusExporter } from "@opentelemetry/exporter-prometheus";
|
||||||
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
import { HostMetrics } from "@opentelemetry/host-metrics";
|
||||||
import { resourceFromAttributes } from "@opentelemetry/resources";
|
import { registerInstrumentations } from "@opentelemetry/instrumentation";
|
||||||
import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
|
import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
|
||||||
import { NodeSDK } from "@opentelemetry/sdk-node";
|
import { RuntimeNodeInstrumentation } from "@opentelemetry/instrumentation-runtime-node";
|
||||||
import {
|
import {
|
||||||
AlwaysOffSampler,
|
detectResources,
|
||||||
AlwaysOnSampler,
|
envDetector,
|
||||||
BatchSpanProcessor,
|
hostDetector,
|
||||||
ParentBasedSampler,
|
processDetector,
|
||||||
type Sampler,
|
resourceFromAttributes,
|
||||||
TraceIdRatioBasedSampler,
|
} from "@opentelemetry/resources";
|
||||||
} from "@opentelemetry/sdk-trace-base";
|
import { MeterProvider } from "@opentelemetry/sdk-metrics";
|
||||||
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from "@opentelemetry/semantic-conventions";
|
|
||||||
import { PrismaInstrumentation } from "@prisma/instrumentation";
|
|
||||||
import { logger } from "@formbricks/logger";
|
import { logger } from "@formbricks/logger";
|
||||||
|
import { env } from "@/lib/env";
|
||||||
|
|
||||||
// --- Configuration from environment ---
|
const exporter = new PrometheusExporter({
|
||||||
const serviceName = process.env.OTEL_SERVICE_NAME || "formbricks";
|
port: env.PROMETHEUS_EXPORTER_PORT ? parseInt(env.PROMETHEUS_EXPORTER_PORT) : 9464,
|
||||||
const serviceVersion = process.env.npm_package_version || "0.0.0";
|
endpoint: "/metrics",
|
||||||
const environment = process.env.ENVIRONMENT || process.env.NODE_ENV || "development";
|
host: "0.0.0.0", // Listen on all network interfaces
|
||||||
const otlpEndpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
|
|
||||||
const prometheusEnabled = process.env.PROMETHEUS_ENABLED === "1";
|
|
||||||
const prometheusPort = process.env.PROMETHEUS_EXPORTER_PORT
|
|
||||||
? Number.parseInt(process.env.PROMETHEUS_EXPORTER_PORT)
|
|
||||||
: 9464;
|
|
||||||
|
|
||||||
// --- Configure OTLP exporters (conditional on endpoint being set) ---
|
|
||||||
let traceExporter: OTLPTraceExporter | undefined;
|
|
||||||
let otlpMetricExporter: OTLPMetricExporter | undefined;
|
|
||||||
|
|
||||||
if (otlpEndpoint) {
|
|
||||||
try {
|
|
||||||
// OTLPTraceExporter reads OTEL_EXPORTER_OTLP_ENDPOINT from env
|
|
||||||
// and appends /v1/traces for HTTP transport
|
|
||||||
// Uses OTEL_EXPORTER_OTLP_HEADERS from env natively (W3C OTel format: key=value,key2=value2)
|
|
||||||
traceExporter = new OTLPTraceExporter();
|
|
||||||
|
|
||||||
// OTLPMetricExporter reads OTEL_EXPORTER_OTLP_ENDPOINT from env
|
|
||||||
// and appends /v1/metrics for HTTP transport
|
|
||||||
// Uses OTEL_EXPORTER_OTLP_HEADERS from env natively
|
|
||||||
otlpMetricExporter = new OTLPMetricExporter();
|
|
||||||
} catch (error) {
|
|
||||||
logger.error(error, "Failed to create OTLP exporters. Telemetry will not be exported.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Configure Prometheus exporter (pull-based metrics for ServiceMonitor) ---
|
|
||||||
let prometheusExporter: PrometheusExporter | undefined;
|
|
||||||
if (prometheusEnabled) {
|
|
||||||
prometheusExporter = new PrometheusExporter({
|
|
||||||
port: prometheusPort,
|
|
||||||
endpoint: "/metrics",
|
|
||||||
host: "0.0.0.0",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Build metric readers array ---
|
|
||||||
const metricReaders: (PeriodicExportingMetricReader | PrometheusExporter)[] = [];
|
|
||||||
|
|
||||||
if (otlpMetricExporter) {
|
|
||||||
metricReaders.push(
|
|
||||||
new PeriodicExportingMetricReader({
|
|
||||||
exporter: otlpMetricExporter,
|
|
||||||
exportIntervalMillis: 60000, // Export every 60 seconds
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prometheusExporter) {
|
|
||||||
metricReaders.push(prometheusExporter);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Resource attributes ---
|
|
||||||
const resourceAttributes: Record<string, string> = {
|
|
||||||
[ATTR_SERVICE_NAME]: serviceName,
|
|
||||||
[ATTR_SERVICE_VERSION]: serviceVersion,
|
|
||||||
"deployment.environment": environment,
|
|
||||||
};
|
|
||||||
|
|
||||||
// --- Configure sampler ---
|
|
||||||
const samplerType = process.env.OTEL_TRACES_SAMPLER || "always_on";
|
|
||||||
const parsedSamplerArg = process.env.OTEL_TRACES_SAMPLER_ARG
|
|
||||||
? Number.parseFloat(process.env.OTEL_TRACES_SAMPLER_ARG)
|
|
||||||
: undefined;
|
|
||||||
const samplerArg =
|
|
||||||
parsedSamplerArg !== undefined && !Number.isNaN(parsedSamplerArg) ? parsedSamplerArg : undefined;
|
|
||||||
|
|
||||||
let sampler: Sampler;
|
|
||||||
switch (samplerType) {
|
|
||||||
case "always_on":
|
|
||||||
sampler = new AlwaysOnSampler();
|
|
||||||
break;
|
|
||||||
case "always_off":
|
|
||||||
sampler = new AlwaysOffSampler();
|
|
||||||
break;
|
|
||||||
case "traceidratio":
|
|
||||||
sampler = new TraceIdRatioBasedSampler(samplerArg ?? 1);
|
|
||||||
break;
|
|
||||||
case "parentbased_traceidratio":
|
|
||||||
sampler = new ParentBasedSampler({
|
|
||||||
root: new TraceIdRatioBasedSampler(samplerArg ?? 1),
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "parentbased_always_on":
|
|
||||||
sampler = new ParentBasedSampler({
|
|
||||||
root: new AlwaysOnSampler(),
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "parentbased_always_off":
|
|
||||||
sampler = new ParentBasedSampler({
|
|
||||||
root: new AlwaysOffSampler(),
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
logger.warn(`Unknown sampler type: ${samplerType}. Using always_on.`);
|
|
||||||
sampler = new AlwaysOnSampler();
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Initialize NodeSDK ---
|
|
||||||
const sdk = new NodeSDK({
|
|
||||||
sampler,
|
|
||||||
resource: resourceFromAttributes(resourceAttributes),
|
|
||||||
// When no OTLP endpoint is configured (e.g. Prometheus-only setups), pass an empty
|
|
||||||
// spanProcessors array to prevent the SDK from falling back to its default OTLP exporter
|
|
||||||
// which would attempt connections to localhost:4318 and cause noisy errors.
|
|
||||||
spanProcessors: traceExporter
|
|
||||||
? [
|
|
||||||
new BatchSpanProcessor(traceExporter, {
|
|
||||||
maxQueueSize: 2048,
|
|
||||||
maxExportBatchSize: 512,
|
|
||||||
scheduledDelayMillis: 5000,
|
|
||||||
exportTimeoutMillis: 30000,
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
: [],
|
|
||||||
metricReaders: metricReaders.length > 0 ? metricReaders : undefined,
|
|
||||||
instrumentations: [
|
|
||||||
getNodeAutoInstrumentations({
|
|
||||||
// Disable noisy/unnecessary instrumentations
|
|
||||||
"@opentelemetry/instrumentation-fs": {
|
|
||||||
enabled: false,
|
|
||||||
},
|
|
||||||
"@opentelemetry/instrumentation-dns": {
|
|
||||||
enabled: false,
|
|
||||||
},
|
|
||||||
"@opentelemetry/instrumentation-net": {
|
|
||||||
enabled: false,
|
|
||||||
},
|
|
||||||
// Disable pg instrumentation - PrismaInstrumentation handles DB tracing
|
|
||||||
"@opentelemetry/instrumentation-pg": {
|
|
||||||
enabled: false,
|
|
||||||
},
|
|
||||||
"@opentelemetry/instrumentation-http": {
|
|
||||||
// Ignore health/metrics endpoints to reduce noise
|
|
||||||
ignoreIncomingRequestHook: (req) => {
|
|
||||||
const url = req.url || "";
|
|
||||||
return url === "/health" || url.startsWith("/metrics") || url === "/api/v2/health";
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Enable runtime metrics for Node.js process monitoring
|
|
||||||
"@opentelemetry/instrumentation-runtime-node": {
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
// Prisma instrumentation for database query tracing
|
|
||||||
new PrismaInstrumentation(),
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Start the SDK
|
const detectedResources = detectResources({
|
||||||
sdk.start();
|
detectors: [envDetector, processDetector, hostDetector],
|
||||||
|
});
|
||||||
|
|
||||||
// --- Log initialization status ---
|
const customResources = resourceFromAttributes({});
|
||||||
const enabledFeatures: string[] = [];
|
|
||||||
if (traceExporter) enabledFeatures.push("traces");
|
|
||||||
if (otlpMetricExporter) enabledFeatures.push("otlp-metrics");
|
|
||||||
if (prometheusExporter) enabledFeatures.push("prometheus-metrics");
|
|
||||||
|
|
||||||
const samplerArgStr = process.env.OTEL_TRACES_SAMPLER_ARG || "";
|
const resources = detectedResources.merge(customResources);
|
||||||
const samplerArgMsg = samplerArgStr ? `, samplerArg=${samplerArgStr}` : "";
|
|
||||||
|
|
||||||
if (enabledFeatures.length > 0) {
|
const meterProvider = new MeterProvider({
|
||||||
logger.info(
|
readers: [exporter],
|
||||||
`OpenTelemetry initialized: service=${serviceName}, version=${serviceVersion}, environment=${environment}, exporters=${enabledFeatures.join("+")}, sampler=${samplerType}${samplerArgMsg}`
|
resource: resources,
|
||||||
);
|
});
|
||||||
} else {
|
|
||||||
logger.info(
|
|
||||||
`OpenTelemetry initialized (no exporters): service=${serviceName}, version=${serviceVersion}, environment=${environment}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Graceful shutdown ---
|
const hostMetrics = new HostMetrics({
|
||||||
// Run before other SIGTERM listeners (logger flush, etc.) so spans are drained first.
|
name: `otel-metrics`,
|
||||||
process.prependListener("SIGTERM", async () => {
|
meterProvider,
|
||||||
|
});
|
||||||
|
|
||||||
|
registerInstrumentations({
|
||||||
|
meterProvider,
|
||||||
|
instrumentations: [new HttpInstrumentation(), new RuntimeNodeInstrumentation()],
|
||||||
|
});
|
||||||
|
|
||||||
|
hostMetrics.start();
|
||||||
|
|
||||||
|
process.on("SIGTERM", async () => {
|
||||||
try {
|
try {
|
||||||
await sdk.shutdown();
|
// Stop collecting metrics or flush them if needed
|
||||||
|
await meterProvider.shutdown();
|
||||||
|
// Possibly close other instrumentation resources
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.error(e, "Error during OpenTelemetry shutdown");
|
logger.error(e, "Error during graceful shutdown");
|
||||||
|
} finally {
|
||||||
|
process.exit(0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,13 +5,10 @@ export const onRequestError = Sentry.captureRequestError;
|
|||||||
|
|
||||||
export const register = async () => {
|
export const register = async () => {
|
||||||
if (process.env.NEXT_RUNTIME === "nodejs") {
|
if (process.env.NEXT_RUNTIME === "nodejs") {
|
||||||
// Load OpenTelemetry instrumentation when Prometheus metrics or OTLP export is enabled
|
if (PROMETHEUS_ENABLED) {
|
||||||
if (PROMETHEUS_ENABLED || process.env.OTEL_EXPORTER_OTLP_ENDPOINT) {
|
|
||||||
await import("./instrumentation-node");
|
await import("./instrumentation-node");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Sentry init loads after OTEL to avoid TracerProvider conflicts
|
|
||||||
// Sentry tracing is disabled (tracesSampleRate: 0) -- SigNoz handles distributed tracing
|
|
||||||
if (process.env.NEXT_RUNTIME === "nodejs" && IS_PRODUCTION && SENTRY_DSN) {
|
if (process.env.NEXT_RUNTIME === "nodejs" && IS_PRODUCTION && SENTRY_DSN) {
|
||||||
await import("./sentry.server.config");
|
await import("./sentry.server.config");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.number().int().min(1).optional()],
|
|
||||||
[offset, z.number().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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -55,6 +55,7 @@ export const env = createEnv({
|
|||||||
OIDC_DISPLAY_NAME: z.string().optional(),
|
OIDC_DISPLAY_NAME: z.string().optional(),
|
||||||
OIDC_ISSUER: z.string().optional(),
|
OIDC_ISSUER: z.string().optional(),
|
||||||
OIDC_SIGNING_ALGORITHM: z.string().optional(),
|
OIDC_SIGNING_ALGORITHM: z.string().optional(),
|
||||||
|
OPENTELEMETRY_LISTENER_URL: z.string().optional(),
|
||||||
REDIS_URL:
|
REDIS_URL:
|
||||||
process.env.NODE_ENV === "test"
|
process.env.NODE_ENV === "test"
|
||||||
? z.string().optional()
|
? z.string().optional()
|
||||||
@@ -173,6 +174,7 @@ export const env = createEnv({
|
|||||||
NEXTAUTH_URL: process.env.NEXTAUTH_URL,
|
NEXTAUTH_URL: process.env.NEXTAUTH_URL,
|
||||||
NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET,
|
NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET,
|
||||||
SENTRY_DSN: process.env.SENTRY_DSN,
|
SENTRY_DSN: process.env.SENTRY_DSN,
|
||||||
|
OPENTELEMETRY_LISTENER_URL: process.env.OPENTELEMETRY_LISTENER_URL,
|
||||||
NOTION_OAUTH_CLIENT_ID: process.env.NOTION_OAUTH_CLIENT_ID,
|
NOTION_OAUTH_CLIENT_ID: process.env.NOTION_OAUTH_CLIENT_ID,
|
||||||
NOTION_OAUTH_CLIENT_SECRET: process.env.NOTION_OAUTH_CLIENT_SECRET,
|
NOTION_OAUTH_CLIENT_SECRET: process.env.NOTION_OAUTH_CLIENT_SECRET,
|
||||||
OIDC_CLIENT_ID: process.env.OIDC_CLIENT_ID,
|
OIDC_CLIENT_ID: process.env.OIDC_CLIENT_ID,
|
||||||
|
|||||||
@@ -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";
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ describe("Project Service", () => {
|
|||||||
},
|
},
|
||||||
placement: WidgetPlacement.bottomRight,
|
placement: WidgetPlacement.bottomRight,
|
||||||
clickOutsideClose: true,
|
clickOutsideClose: true,
|
||||||
overlay: "none",
|
darkOverlay: false,
|
||||||
environments: [],
|
environments: [],
|
||||||
styling: {
|
styling: {
|
||||||
allowStyleOverwrite: true,
|
allowStyleOverwrite: true,
|
||||||
@@ -106,7 +106,7 @@ describe("Project Service", () => {
|
|||||||
},
|
},
|
||||||
placement: WidgetPlacement.bottomRight,
|
placement: WidgetPlacement.bottomRight,
|
||||||
clickOutsideClose: true,
|
clickOutsideClose: true,
|
||||||
overlay: "none",
|
darkOverlay: false,
|
||||||
environments: [],
|
environments: [],
|
||||||
styling: {
|
styling: {
|
||||||
allowStyleOverwrite: true,
|
allowStyleOverwrite: true,
|
||||||
@@ -171,7 +171,7 @@ describe("Project Service", () => {
|
|||||||
},
|
},
|
||||||
placement: WidgetPlacement.bottomRight,
|
placement: WidgetPlacement.bottomRight,
|
||||||
clickOutsideClose: true,
|
clickOutsideClose: true,
|
||||||
overlay: "none",
|
darkOverlay: false,
|
||||||
environments: [],
|
environments: [],
|
||||||
styling: {
|
styling: {
|
||||||
allowStyleOverwrite: true,
|
allowStyleOverwrite: true,
|
||||||
@@ -196,7 +196,7 @@ describe("Project Service", () => {
|
|||||||
},
|
},
|
||||||
placement: WidgetPlacement.bottomRight,
|
placement: WidgetPlacement.bottomRight,
|
||||||
clickOutsideClose: true,
|
clickOutsideClose: true,
|
||||||
overlay: "none",
|
darkOverlay: false,
|
||||||
environments: [],
|
environments: [],
|
||||||
styling: {
|
styling: {
|
||||||
allowStyleOverwrite: true,
|
allowStyleOverwrite: true,
|
||||||
@@ -250,7 +250,7 @@ describe("Project Service", () => {
|
|||||||
},
|
},
|
||||||
placement: WidgetPlacement.bottomRight,
|
placement: WidgetPlacement.bottomRight,
|
||||||
clickOutsideClose: true,
|
clickOutsideClose: true,
|
||||||
overlay: "none",
|
darkOverlay: false,
|
||||||
environments: [],
|
environments: [],
|
||||||
styling: {
|
styling: {
|
||||||
allowStyleOverwrite: true,
|
allowStyleOverwrite: true,
|
||||||
@@ -324,7 +324,7 @@ describe("Project Service", () => {
|
|||||||
},
|
},
|
||||||
placement: WidgetPlacement.bottomRight,
|
placement: WidgetPlacement.bottomRight,
|
||||||
clickOutsideClose: true,
|
clickOutsideClose: true,
|
||||||
overlay: "none",
|
darkOverlay: false,
|
||||||
environments: [],
|
environments: [],
|
||||||
styling: {
|
styling: {
|
||||||
allowStyleOverwrite: true,
|
allowStyleOverwrite: true,
|
||||||
@@ -378,7 +378,7 @@ describe("Project Service", () => {
|
|||||||
},
|
},
|
||||||
placement: WidgetPlacement.bottomRight,
|
placement: WidgetPlacement.bottomRight,
|
||||||
clickOutsideClose: true,
|
clickOutsideClose: true,
|
||||||
overlay: "none",
|
darkOverlay: false,
|
||||||
environments: [],
|
environments: [],
|
||||||
styling: {
|
styling: {
|
||||||
allowStyleOverwrite: true,
|
allowStyleOverwrite: true,
|
||||||
@@ -403,7 +403,7 @@ describe("Project Service", () => {
|
|||||||
},
|
},
|
||||||
placement: WidgetPlacement.bottomRight,
|
placement: WidgetPlacement.bottomRight,
|
||||||
clickOutsideClose: true,
|
clickOutsideClose: true,
|
||||||
overlay: "none",
|
darkOverlay: false,
|
||||||
environments: [],
|
environments: [],
|
||||||
styling: {
|
styling: {
|
||||||
allowStyleOverwrite: true,
|
allowStyleOverwrite: true,
|
||||||
@@ -448,7 +448,7 @@ describe("Project Service", () => {
|
|||||||
},
|
},
|
||||||
placement: WidgetPlacement.bottomRight,
|
placement: WidgetPlacement.bottomRight,
|
||||||
clickOutsideClose: true,
|
clickOutsideClose: true,
|
||||||
overlay: "none",
|
darkOverlay: false,
|
||||||
environments: [],
|
environments: [],
|
||||||
styling: {
|
styling: {
|
||||||
allowStyleOverwrite: true,
|
allowStyleOverwrite: true,
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const selectProject = {
|
|||||||
config: true,
|
config: true,
|
||||||
placement: true,
|
placement: true,
|
||||||
clickOutsideClose: true,
|
clickOutsideClose: true,
|
||||||
overlay: true,
|
darkOverlay: true,
|
||||||
environments: true,
|
environments: true,
|
||||||
styling: true,
|
styling: true,
|
||||||
logo: true,
|
logo: true,
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import { getElementsFromBlocks } from "@/lib/survey/utils";
|
|||||||
import { getIsQuotasEnabled } from "@/modules/ee/license-check/lib/utils";
|
import { getIsQuotasEnabled } from "@/modules/ee/license-check/lib/utils";
|
||||||
import { reduceQuotaLimits } from "@/modules/ee/quotas/lib/quotas";
|
import { reduceQuotaLimits } from "@/modules/ee/quotas/lib/quotas";
|
||||||
import { deleteFile } from "@/modules/storage/service";
|
import { deleteFile } from "@/modules/storage/service";
|
||||||
import { resolveStorageUrlsInObject } from "@/modules/storage/utils";
|
|
||||||
import { getOrganizationIdFromEnvironmentId } from "@/modules/survey/lib/organization";
|
import { getOrganizationIdFromEnvironmentId } from "@/modules/survey/lib/organization";
|
||||||
import { getOrganizationBilling } from "@/modules/survey/lib/survey";
|
import { getOrganizationBilling } from "@/modules/survey/lib/survey";
|
||||||
import { ITEMS_PER_PAGE } from "../constants";
|
import { ITEMS_PER_PAGE } from "../constants";
|
||||||
@@ -409,10 +408,9 @@ export const getResponseDownloadFile = async (
|
|||||||
if (survey.isVerifyEmailEnabled) {
|
if (survey.isVerifyEmailEnabled) {
|
||||||
headers.push("Verified Email");
|
headers.push("Verified Email");
|
||||||
}
|
}
|
||||||
const resolvedResponses = responses.map((r) => ({ ...r, data: resolveStorageUrlsInObject(r.data) }));
|
|
||||||
const jsonData = getResponsesJson(
|
const jsonData = getResponsesJson(
|
||||||
survey,
|
survey,
|
||||||
resolvedResponses,
|
responses,
|
||||||
elements,
|
elements,
|
||||||
userAttributes,
|
userAttributes,
|
||||||
hiddenFields,
|
hiddenFields,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// https://github.com/airbnb/javascript/#naming--uppercase
|
// https://github.com/airbnb/javascript/#naming--uppercase
|
||||||
import { TProjectStyling } from "@formbricks/types/project";
|
import { TProjectStyling } from "@formbricks/types/project";
|
||||||
import { isLight, mixColor } from "@/lib/utils/colors";
|
|
||||||
|
|
||||||
export const COLOR_DEFAULTS = {
|
export const COLOR_DEFAULTS = {
|
||||||
brandColor: "#64748b",
|
brandColor: "#64748b",
|
||||||
@@ -12,210 +11,32 @@ export const COLOR_DEFAULTS = {
|
|||||||
highlightBorderColor: "#64748b",
|
highlightBorderColor: "#64748b",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const DEFAULT_BRAND_COLOR = "#64748b";
|
export const defaultStyling: TProjectStyling = {
|
||||||
|
|
||||||
/**
|
|
||||||
* Derives a complete set of suggested color values from a single brand color.
|
|
||||||
*
|
|
||||||
* Used by the project-level "Suggest Colors" button **and** to build
|
|
||||||
* `STYLE_DEFAULTS` so that a fresh install always has colours that are
|
|
||||||
* visually cohesive with the default brand.
|
|
||||||
*
|
|
||||||
* The returned object is a flat map of form-field paths to values so it
|
|
||||||
* can be spread directly into form defaults or applied via `form.setValue`.
|
|
||||||
*/
|
|
||||||
export const getSuggestedColors = (brandColor: string = DEFAULT_BRAND_COLOR) => {
|
|
||||||
// Question / dark text: brand darkened with black (visible brand tint)
|
|
||||||
const questionColor = mixColor(brandColor, "#000000", 0.35);
|
|
||||||
// Input / option background: white with noticeable brand tint
|
|
||||||
const inputBg = mixColor(brandColor, "#ffffff", 0.92);
|
|
||||||
// Input border: visible brand-tinted border
|
|
||||||
const inputBorder = mixColor(brandColor, "#ffffff", 0.6);
|
|
||||||
// Card tones
|
|
||||||
const cardBg = mixColor(brandColor, "#ffffff", 0.97);
|
|
||||||
const cardBorder = mixColor(brandColor, "#ffffff", 0.8);
|
|
||||||
// Page background
|
|
||||||
const pageBg = mixColor(brandColor, "#ffffff", 0.855);
|
|
||||||
|
|
||||||
return {
|
|
||||||
// General
|
|
||||||
"brandColor.light": brandColor,
|
|
||||||
"questionColor.light": questionColor,
|
|
||||||
|
|
||||||
// Headlines & Descriptions — use questionColor to match the legacy behaviour
|
|
||||||
// where all text elements derived their color from questionColor.
|
|
||||||
"elementHeadlineColor.light": questionColor,
|
|
||||||
"elementDescriptionColor.light": questionColor,
|
|
||||||
"elementUpperLabelColor.light": questionColor,
|
|
||||||
|
|
||||||
// Buttons — use the brand color so the button matches the user's intent.
|
|
||||||
"buttonBgColor.light": brandColor,
|
|
||||||
"buttonTextColor.light": isLight(brandColor) ? "#0f172a" : "#ffffff",
|
|
||||||
|
|
||||||
// Inputs
|
|
||||||
"inputColor.light": inputBg,
|
|
||||||
"inputBorderColor.light": inputBorder,
|
|
||||||
"inputTextColor.light": questionColor,
|
|
||||||
|
|
||||||
// Options (Radio / Checkbox)
|
|
||||||
"optionBgColor.light": inputBg,
|
|
||||||
"optionLabelColor.light": questionColor,
|
|
||||||
|
|
||||||
// Card
|
|
||||||
"cardBackgroundColor.light": cardBg,
|
|
||||||
"cardBorderColor.light": cardBorder,
|
|
||||||
|
|
||||||
// Highlight / Focus
|
|
||||||
"highlightBorderColor.light": mixColor(brandColor, "#ffffff", 0.25),
|
|
||||||
|
|
||||||
// Progress Bar — indicator uses the brand color; track is a lighter tint.
|
|
||||||
"progressIndicatorBgColor.light": brandColor,
|
|
||||||
"progressTrackBgColor.light": mixColor(brandColor, "#ffffff", 0.8),
|
|
||||||
|
|
||||||
// Background
|
|
||||||
background: { bg: pageBg, bgType: "color" as const, brightness: 100 },
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Pre-compute colors derived from the default brand color.
|
|
||||||
const _colors = getSuggestedColors(DEFAULT_BRAND_COLOR);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Single source of truth for every styling default.
|
|
||||||
*
|
|
||||||
* Color values are derived from the default brand color (#64748b) via
|
|
||||||
* `getSuggestedColors()`. Non-color values (dimensions, weights, sizes)
|
|
||||||
* are hardcoded here and must be kept in sync with globals.css.
|
|
||||||
*
|
|
||||||
* Used everywhere: form defaults, preview rendering, email templates,
|
|
||||||
* and as the reset target for "Restore defaults".
|
|
||||||
*/
|
|
||||||
export const STYLE_DEFAULTS: TProjectStyling = {
|
|
||||||
allowStyleOverwrite: true,
|
allowStyleOverwrite: true,
|
||||||
brandColor: { light: _colors["brandColor.light"] },
|
brandColor: {
|
||||||
questionColor: { light: _colors["questionColor.light"] },
|
light: COLOR_DEFAULTS.brandColor,
|
||||||
inputColor: { light: _colors["inputColor.light"] },
|
},
|
||||||
inputBorderColor: { light: _colors["inputBorderColor.light"] },
|
questionColor: {
|
||||||
cardBackgroundColor: { light: _colors["cardBackgroundColor.light"] },
|
light: COLOR_DEFAULTS.questionColor,
|
||||||
cardBorderColor: { light: _colors["cardBorderColor.light"] },
|
},
|
||||||
|
inputColor: {
|
||||||
|
light: COLOR_DEFAULTS.inputColor,
|
||||||
|
},
|
||||||
|
inputBorderColor: {
|
||||||
|
light: COLOR_DEFAULTS.inputBorderColor,
|
||||||
|
},
|
||||||
|
cardBackgroundColor: {
|
||||||
|
light: COLOR_DEFAULTS.cardBackgroundColor,
|
||||||
|
},
|
||||||
|
cardBorderColor: {
|
||||||
|
light: COLOR_DEFAULTS.cardBorderColor,
|
||||||
|
},
|
||||||
isLogoHidden: false,
|
isLogoHidden: false,
|
||||||
highlightBorderColor: { light: _colors["highlightBorderColor.light"] },
|
highlightBorderColor: undefined,
|
||||||
isDarkModeEnabled: false,
|
isDarkModeEnabled: false,
|
||||||
roundness: 8,
|
roundness: 8,
|
||||||
cardArrangement: { linkSurveys: "simple", appSurveys: "simple" },
|
cardArrangement: {
|
||||||
|
linkSurveys: "straight",
|
||||||
// Headlines & Descriptions
|
appSurveys: "straight",
|
||||||
elementHeadlineColor: { light: _colors["elementHeadlineColor.light"] },
|
},
|
||||||
elementHeadlineFontSize: 16,
|
|
||||||
elementHeadlineFontWeight: 600,
|
|
||||||
elementDescriptionColor: { light: _colors["elementDescriptionColor.light"] },
|
|
||||||
elementDescriptionFontSize: 14,
|
|
||||||
elementDescriptionFontWeight: 400,
|
|
||||||
elementUpperLabelColor: { light: _colors["elementUpperLabelColor.light"] },
|
|
||||||
elementUpperLabelFontSize: 12,
|
|
||||||
elementUpperLabelFontWeight: 400,
|
|
||||||
|
|
||||||
// Inputs
|
|
||||||
inputTextColor: { light: _colors["inputTextColor.light"] },
|
|
||||||
inputBorderRadius: 8,
|
|
||||||
inputHeight: 20,
|
|
||||||
inputFontSize: 14,
|
|
||||||
inputPaddingX: 8,
|
|
||||||
inputPaddingY: 8,
|
|
||||||
inputPlaceholderOpacity: 0.5,
|
|
||||||
inputShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)",
|
|
||||||
|
|
||||||
// Buttons
|
|
||||||
buttonBgColor: { light: _colors["buttonBgColor.light"] },
|
|
||||||
buttonTextColor: { light: _colors["buttonTextColor.light"] },
|
|
||||||
buttonBorderRadius: 8,
|
|
||||||
buttonHeight: "auto",
|
|
||||||
buttonFontSize: 16,
|
|
||||||
buttonFontWeight: 500,
|
|
||||||
buttonPaddingX: 12,
|
|
||||||
buttonPaddingY: 12,
|
|
||||||
|
|
||||||
// Options
|
|
||||||
optionBgColor: { light: _colors["optionBgColor.light"] },
|
|
||||||
optionLabelColor: { light: _colors["optionLabelColor.light"] },
|
|
||||||
optionBorderRadius: 8,
|
|
||||||
optionPaddingX: 16,
|
|
||||||
optionPaddingY: 16,
|
|
||||||
optionFontSize: 14,
|
|
||||||
|
|
||||||
// Progress Bar
|
|
||||||
progressTrackHeight: 8,
|
|
||||||
progressTrackBgColor: { light: _colors["progressTrackBgColor.light"] },
|
|
||||||
progressIndicatorBgColor: { light: _colors["progressIndicatorBgColor.light"] },
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fills in new v4.7 color fields from legacy v4.6 fields when they are missing.
|
|
||||||
*
|
|
||||||
* v4.6 stored: brandColor, questionColor, inputColor, inputBorderColor.
|
|
||||||
* v4.7 adds: elementHeadlineColor, buttonBgColor, optionBgColor, etc.
|
|
||||||
*
|
|
||||||
* When loading v4.6 data the new fields are absent. Without this helper the
|
|
||||||
* form would fall back to STYLE_DEFAULTS (derived from the *default* brand
|
|
||||||
* colour), causing a visible mismatch. This function derives the new fields
|
|
||||||
* from the actually-saved legacy fields so the preview and form stay coherent.
|
|
||||||
*
|
|
||||||
* Only sets a field when the legacy source exists AND the new field is absent.
|
|
||||||
*/
|
|
||||||
export const deriveNewFieldsFromLegacy = (saved: Record<string, unknown>): Record<string, unknown> => {
|
|
||||||
const light = (key: string): string | undefined =>
|
|
||||||
(saved[key] as { light?: string } | null | undefined)?.light;
|
|
||||||
|
|
||||||
const q = light("questionColor");
|
|
||||||
const b = light("brandColor");
|
|
||||||
const i = light("inputColor");
|
|
||||||
|
|
||||||
return {
|
|
||||||
...(q && !saved.elementHeadlineColor && { elementHeadlineColor: { light: q } }),
|
|
||||||
...(q && !saved.elementDescriptionColor && { elementDescriptionColor: { light: q } }),
|
|
||||||
...(q && !saved.elementUpperLabelColor && { elementUpperLabelColor: { light: q } }),
|
|
||||||
...(q && !saved.inputTextColor && { inputTextColor: { light: q } }),
|
|
||||||
...(q && !saved.optionLabelColor && { optionLabelColor: { light: q } }),
|
|
||||||
...(b && !saved.buttonBgColor && { buttonBgColor: { light: b } }),
|
|
||||||
...(b && !saved.buttonTextColor && { buttonTextColor: { light: isLight(b) ? "#0f172a" : "#ffffff" } }),
|
|
||||||
...(i && !saved.optionBgColor && { optionBgColor: { light: i } }),
|
|
||||||
...(b && !saved.progressIndicatorBgColor && { progressIndicatorBgColor: { light: b } }),
|
|
||||||
...(b &&
|
|
||||||
!saved.progressTrackBgColor && { progressTrackBgColor: { light: mixColor(b, "#ffffff", 0.8) } }),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds a complete TProjectStyling object from a single brand color.
|
|
||||||
*
|
|
||||||
* Uses STYLE_DEFAULTS for all non-color properties (dimensions, weights, etc.)
|
|
||||||
* and derives every color from the given brand color via getSuggestedColors().
|
|
||||||
*
|
|
||||||
* Useful when only a brand color is known (e.g. onboarding) and a fully
|
|
||||||
* coherent styling object is needed for both preview rendering and persistence.
|
|
||||||
*/
|
|
||||||
export const buildStylingFromBrandColor = (brandColor: string = DEFAULT_BRAND_COLOR): TProjectStyling => {
|
|
||||||
const colors = getSuggestedColors(brandColor);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...STYLE_DEFAULTS,
|
|
||||||
brandColor: { light: colors["brandColor.light"] },
|
|
||||||
questionColor: { light: colors["questionColor.light"] },
|
|
||||||
elementHeadlineColor: { light: colors["elementHeadlineColor.light"] },
|
|
||||||
elementDescriptionColor: { light: colors["elementDescriptionColor.light"] },
|
|
||||||
elementUpperLabelColor: { light: colors["elementUpperLabelColor.light"] },
|
|
||||||
buttonBgColor: { light: colors["buttonBgColor.light"] },
|
|
||||||
buttonTextColor: { light: colors["buttonTextColor.light"] },
|
|
||||||
inputColor: { light: colors["inputColor.light"] },
|
|
||||||
inputBorderColor: { light: colors["inputBorderColor.light"] },
|
|
||||||
inputTextColor: { light: colors["inputTextColor.light"] },
|
|
||||||
optionBgColor: { light: colors["optionBgColor.light"] },
|
|
||||||
optionLabelColor: { light: colors["optionLabelColor.light"] },
|
|
||||||
cardBackgroundColor: { light: colors["cardBackgroundColor.light"] },
|
|
||||||
cardBorderColor: { light: colors["cardBorderColor.light"] },
|
|
||||||
highlightBorderColor: { light: colors["highlightBorderColor.light"] },
|
|
||||||
progressIndicatorBgColor: { light: colors["progressIndicatorBgColor.light"] },
|
|
||||||
progressTrackBgColor: { light: colors["progressTrackBgColor.light"] },
|
|
||||||
background: colors.background,
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export const mockProject: TProject = {
|
|||||||
inAppSurveyBranding: false,
|
inAppSurveyBranding: false,
|
||||||
placement: "bottomRight",
|
placement: "bottomRight",
|
||||||
clickOutsideClose: false,
|
clickOutsideClose: false,
|
||||||
overlay: "none",
|
darkOverlay: false,
|
||||||
environments: [],
|
environments: [],
|
||||||
languages: [],
|
languages: [],
|
||||||
config: {
|
config: {
|
||||||
@@ -168,7 +168,6 @@ export const mockContactAttributeKey: TContactAttributeKey = {
|
|||||||
type: "custom",
|
type: "custom",
|
||||||
description: "mock action class",
|
description: "mock action class",
|
||||||
isUnique: false,
|
isUnique: false,
|
||||||
dataType: "string",
|
|
||||||
...commonMockProperties,
|
...commonMockProperties,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -141,68 +141,5 @@ describe("Time Utilities", () => {
|
|||||||
expect(convertDatesInObject("string")).toBe("string");
|
expect(convertDatesInObject("string")).toBe("string");
|
||||||
expect(convertDatesInObject(123)).toBe(123);
|
expect(convertDatesInObject(123)).toBe(123);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should not convert dates in ignored keys when keysToIgnore is provided", () => {
|
|
||||||
const keysToIgnore = new Set(["contactAttributes", "variables", "data", "meta"]);
|
|
||||||
const input = {
|
|
||||||
createdAt: "2024-03-20T15:30:00",
|
|
||||||
contactAttributes: {
|
|
||||||
createdAt: "2024-03-20T16:30:00",
|
|
||||||
email: "test@example.com",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = convertDatesInObject(input, keysToIgnore);
|
|
||||||
expect(result.createdAt).toBeInstanceOf(Date);
|
|
||||||
expect(result.contactAttributes.createdAt).toBe("2024-03-20T16:30:00");
|
|
||||||
expect(result.contactAttributes.email).toBe("test@example.com");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should not convert dates in variables when keysToIgnore is provided", () => {
|
|
||||||
const keysToIgnore = new Set(["contactAttributes", "variables", "data", "meta"]);
|
|
||||||
const input = {
|
|
||||||
updatedAt: "2024-03-20T15:30:00",
|
|
||||||
variables: {
|
|
||||||
createdAt: "2024-03-20T16:30:00",
|
|
||||||
userId: "123",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = convertDatesInObject(input, keysToIgnore);
|
|
||||||
expect(result.updatedAt).toBeInstanceOf(Date);
|
|
||||||
expect(result.variables.createdAt).toBe("2024-03-20T16:30:00");
|
|
||||||
expect(result.variables.userId).toBe("123");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should not convert dates in data or meta when keysToIgnore is provided", () => {
|
|
||||||
const keysToIgnore = new Set(["contactAttributes", "variables", "data", "meta"]);
|
|
||||||
const input = {
|
|
||||||
createdAt: "2024-03-20T15:30:00",
|
|
||||||
data: {
|
|
||||||
createdAt: "2024-03-20T16:30:00",
|
|
||||||
},
|
|
||||||
meta: {
|
|
||||||
updatedAt: "2024-03-20T17:30:00",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = convertDatesInObject(input, keysToIgnore);
|
|
||||||
expect(result.createdAt).toBeInstanceOf(Date);
|
|
||||||
expect(result.data.createdAt).toBe("2024-03-20T16:30:00");
|
|
||||||
expect(result.meta.updatedAt).toBe("2024-03-20T17:30:00");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should recurse into all keys when keysToIgnore is not provided", () => {
|
|
||||||
const input = {
|
|
||||||
createdAt: "2024-03-20T15:30:00",
|
|
||||||
contactAttributes: {
|
|
||||||
createdAt: "2024-03-20T16:30:00",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = convertDatesInObject(input);
|
|
||||||
expect(result.createdAt).toBeInstanceOf(Date);
|
|
||||||
expect(result.contactAttributes.createdAt).toBeInstanceOf(Date);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -151,20 +151,16 @@ export const getTodaysDateTimeFormatted = (seperator: string) => {
|
|||||||
return [formattedDate, formattedTime].join(seperator);
|
return [formattedDate, formattedTime].join(seperator);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const convertDatesInObject = <T>(obj: T, keysToIgnore?: Set<string>): T => {
|
export const convertDatesInObject = <T>(obj: T): T => {
|
||||||
if (obj === null || typeof obj !== "object") {
|
if (obj === null || typeof obj !== "object") {
|
||||||
return obj; // Return if obj is not an object
|
return obj; // Return if obj is not an object
|
||||||
}
|
}
|
||||||
if (Array.isArray(obj)) {
|
if (Array.isArray(obj)) {
|
||||||
// Handle arrays by mapping each element through the function
|
// Handle arrays by mapping each element through the function
|
||||||
return obj.map((item) => convertDatesInObject(item, keysToIgnore)) as unknown as T;
|
return obj.map((item) => convertDatesInObject(item)) as unknown as T;
|
||||||
}
|
}
|
||||||
const newObj: Record<string, unknown> = {};
|
const newObj: any = {};
|
||||||
for (const key in obj) {
|
for (const key in obj) {
|
||||||
if (keysToIgnore?.has(key)) {
|
|
||||||
newObj[key] = obj[key];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (
|
if (
|
||||||
(key === "createdAt" || key === "updatedAt") &&
|
(key === "createdAt" || key === "updatedAt") &&
|
||||||
typeof obj[key] === "string" &&
|
typeof obj[key] === "string" &&
|
||||||
@@ -172,10 +168,10 @@ export const convertDatesInObject = <T>(obj: T, keysToIgnore?: Set<string>): T =
|
|||||||
) {
|
) {
|
||||||
newObj[key] = new Date(obj[key] as unknown as string);
|
newObj[key] = new Date(obj[key] as unknown as string);
|
||||||
} else if (typeof obj[key] === "object" && obj[key] !== null) {
|
} else if (typeof obj[key] === "object" && obj[key] !== null) {
|
||||||
newObj[key] = convertDatesInObject(obj[key], keysToIgnore);
|
newObj[key] = convertDatesInObject(obj[key]);
|
||||||
} else {
|
} else {
|
||||||
newObj[key] = obj[key];
|
newObj[key] = obj[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return newObj as T;
|
return newObj;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,261 +0,0 @@
|
|||||||
import * as Sentry from "@sentry/nextjs";
|
|
||||||
import { getServerSession } from "next-auth";
|
|
||||||
import { DEFAULT_SERVER_ERROR_MESSAGE } from "next-safe-action";
|
|
||||||
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
|
||||||
import {
|
|
||||||
AuthenticationError,
|
|
||||||
AuthorizationError,
|
|
||||||
EXPECTED_ERROR_NAMES,
|
|
||||||
InvalidInputError,
|
|
||||||
OperationNotAllowedError,
|
|
||||||
ResourceNotFoundError,
|
|
||||||
TooManyRequestsError,
|
|
||||||
UnknownError,
|
|
||||||
ValidationError,
|
|
||||||
isExpectedError,
|
|
||||||
} from "@formbricks/types/errors";
|
|
||||||
|
|
||||||
// Mock Sentry
|
|
||||||
vi.mock("@sentry/nextjs", () => ({
|
|
||||||
captureException: vi.fn(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Mock logger — use plain functions for chained calls so vi.resetAllMocks() doesn't break them
|
|
||||||
vi.mock("@formbricks/logger", () => ({
|
|
||||||
logger: {
|
|
||||||
withContext: () => ({ error: vi.fn() }),
|
|
||||||
warn: vi.fn(),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Mock next-auth
|
|
||||||
vi.mock("next-auth", () => ({
|
|
||||||
getServerSession: vi.fn(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Mock authOptions
|
|
||||||
vi.mock("@/modules/auth/lib/authOptions", () => ({
|
|
||||||
authOptions: {},
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Mock user service
|
|
||||||
vi.mock("@/lib/user/service", () => ({
|
|
||||||
getUser: vi.fn(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Mock client IP
|
|
||||||
vi.mock("@/lib/utils/client-ip", () => ({
|
|
||||||
getClientIpFromHeaders: vi.fn(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Mock constants
|
|
||||||
vi.mock("@/lib/constants", () => ({
|
|
||||||
AUDIT_LOG_ENABLED: false,
|
|
||||||
AUDIT_LOG_GET_USER_IP: false,
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Mock audit log types
|
|
||||||
vi.mock("@/modules/ee/audit-logs/types/audit-log", () => ({
|
|
||||||
UNKNOWN_DATA: "unknown",
|
|
||||||
}));
|
|
||||||
|
|
||||||
// ── shared helper tests (pure logic, no action client needed) ──────────
|
|
||||||
|
|
||||||
describe("isExpectedError (shared helper)", () => {
|
|
||||||
test("EXPECTED_ERROR_NAMES contains exactly the right error names", () => {
|
|
||||||
const expected = [
|
|
||||||
"ResourceNotFoundError",
|
|
||||||
"AuthorizationError",
|
|
||||||
"InvalidInputError",
|
|
||||||
"ValidationError",
|
|
||||||
"AuthenticationError",
|
|
||||||
"OperationNotAllowedError",
|
|
||||||
"TooManyRequestsError",
|
|
||||||
];
|
|
||||||
|
|
||||||
expect(EXPECTED_ERROR_NAMES.size).toBe(expected.length);
|
|
||||||
for (const name of expected) {
|
|
||||||
expect(EXPECTED_ERROR_NAMES.has(name)).toBe(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test.each([
|
|
||||||
{ ErrorClass: AuthorizationError, args: ["Not authorized"] },
|
|
||||||
{ ErrorClass: AuthenticationError, args: ["Not authenticated"] },
|
|
||||||
{ ErrorClass: TooManyRequestsError, args: ["Rate limit exceeded"] },
|
|
||||||
{ ErrorClass: ResourceNotFoundError, args: ["Survey", "123"] },
|
|
||||||
{ ErrorClass: InvalidInputError, args: ["Invalid input"] },
|
|
||||||
{ ErrorClass: ValidationError, args: ["Invalid data"] },
|
|
||||||
{ ErrorClass: OperationNotAllowedError, args: ["Not allowed"] },
|
|
||||||
])("returns true for $ErrorClass.name", ({ ErrorClass, args }) => {
|
|
||||||
const error = new (ErrorClass as any)(...args);
|
|
||||||
expect(isExpectedError(error)).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("returns true for serialised errors that only have a matching name", () => {
|
|
||||||
const serialisedError = new Error("Auth failed");
|
|
||||||
serialisedError.name = "AuthorizationError";
|
|
||||||
expect(isExpectedError(serialisedError)).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test.each([
|
|
||||||
{ error: new Error("Something broke"), label: "Error" },
|
|
||||||
{ error: new TypeError("Cannot read properties"), label: "TypeError" },
|
|
||||||
{ error: new RangeError("Maximum call stack"), label: "RangeError" },
|
|
||||||
{ error: new UnknownError("Unknown"), label: "UnknownError" },
|
|
||||||
])("returns false for $label", ({ error }) => {
|
|
||||||
expect(isExpectedError(error)).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// ── integration tests against the real actionClient / authenticatedActionClient ──
|
|
||||||
|
|
||||||
describe("actionClient handleServerError", () => {
|
|
||||||
// Lazily import so mocks are in place first
|
|
||||||
let actionClient: (typeof import("./index"))["actionClient"];
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
vi.clearAllMocks();
|
|
||||||
const mod = await import("./index");
|
|
||||||
actionClient = mod.actionClient;
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
vi.clearAllMocks();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Helper: create and execute an action that throws the given error
|
|
||||||
const executeThrowingAction = async (error: Error) => {
|
|
||||||
const action = actionClient.action(async () => {
|
|
||||||
throw error;
|
|
||||||
});
|
|
||||||
return action();
|
|
||||||
};
|
|
||||||
|
|
||||||
describe("expected errors should NOT be reported to Sentry", () => {
|
|
||||||
test("AuthorizationError returns its message and is not sent to Sentry", async () => {
|
|
||||||
const result = await executeThrowingAction(new AuthorizationError("Not authorized"));
|
|
||||||
expect(result?.serverError).toBe("Not authorized");
|
|
||||||
expect(Sentry.captureException).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("AuthenticationError returns its message and is not sent to Sentry", async () => {
|
|
||||||
const result = await executeThrowingAction(new AuthenticationError("Not authenticated"));
|
|
||||||
expect(result?.serverError).toBe("Not authenticated");
|
|
||||||
expect(Sentry.captureException).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("TooManyRequestsError returns its message and is not sent to Sentry", async () => {
|
|
||||||
const result = await executeThrowingAction(new TooManyRequestsError("Rate limit exceeded"));
|
|
||||||
expect(result?.serverError).toBe("Rate limit exceeded");
|
|
||||||
expect(Sentry.captureException).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("ResourceNotFoundError returns its message and is not sent to Sentry", async () => {
|
|
||||||
const result = await executeThrowingAction(new ResourceNotFoundError("Survey", "123"));
|
|
||||||
expect(result?.serverError).toContain("Survey");
|
|
||||||
expect(Sentry.captureException).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("InvalidInputError returns its message and is not sent to Sentry", async () => {
|
|
||||||
const result = await executeThrowingAction(new InvalidInputError("Invalid input"));
|
|
||||||
expect(result?.serverError).toBe("Invalid input");
|
|
||||||
expect(Sentry.captureException).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("ValidationError returns its message and is not sent to Sentry", async () => {
|
|
||||||
const result = await executeThrowingAction(new ValidationError("Invalid data"));
|
|
||||||
expect(result?.serverError).toBe("Invalid data");
|
|
||||||
expect(Sentry.captureException).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("OperationNotAllowedError returns its message and is not sent to Sentry", async () => {
|
|
||||||
const result = await executeThrowingAction(new OperationNotAllowedError("Not allowed"));
|
|
||||||
expect(result?.serverError).toBe("Not allowed");
|
|
||||||
expect(Sentry.captureException).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("unexpected errors SHOULD be reported to Sentry", () => {
|
|
||||||
test("generic Error is sent to Sentry and returns default message", async () => {
|
|
||||||
const error = new Error("Something broke");
|
|
||||||
const result = await executeThrowingAction(error);
|
|
||||||
expect(result?.serverError).toBe(DEFAULT_SERVER_ERROR_MESSAGE);
|
|
||||||
expect(Sentry.captureException).toHaveBeenCalledWith(
|
|
||||||
error,
|
|
||||||
expect.objectContaining({ extra: expect.any(Object) })
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("TypeError is sent to Sentry and returns default message", async () => {
|
|
||||||
const error = new TypeError("Cannot read properties of undefined");
|
|
||||||
const result = await executeThrowingAction(error);
|
|
||||||
expect(result?.serverError).toBe(DEFAULT_SERVER_ERROR_MESSAGE);
|
|
||||||
expect(Sentry.captureException).toHaveBeenCalledWith(
|
|
||||||
error,
|
|
||||||
expect.objectContaining({ extra: expect.any(Object) })
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("UnknownError is sent to Sentry (not an expected business-logic error)", async () => {
|
|
||||||
const error = new UnknownError("Unknown error");
|
|
||||||
const result = await executeThrowingAction(error);
|
|
||||||
expect(result?.serverError).toBe(DEFAULT_SERVER_ERROR_MESSAGE);
|
|
||||||
expect(Sentry.captureException).toHaveBeenCalledWith(
|
|
||||||
error,
|
|
||||||
expect.objectContaining({ extra: expect.any(Object) })
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("authenticatedActionClient", () => {
|
|
||||||
let authenticatedActionClient: (typeof import("./index"))["authenticatedActionClient"];
|
|
||||||
let getUser: (typeof import("@/lib/user/service"))["getUser"];
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
vi.clearAllMocks();
|
|
||||||
const mod = await import("./index");
|
|
||||||
authenticatedActionClient = mod.authenticatedActionClient;
|
|
||||||
const userService = await import("@/lib/user/service");
|
|
||||||
getUser = userService.getUser;
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
vi.clearAllMocks();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("throws AuthenticationError when there is no session", async () => {
|
|
||||||
vi.mocked(getServerSession).mockResolvedValue(null);
|
|
||||||
|
|
||||||
const action = authenticatedActionClient.action(async () => "ok");
|
|
||||||
const result = await action();
|
|
||||||
|
|
||||||
// handleServerError catches AuthenticationError and returns its message
|
|
||||||
expect(result?.serverError).toBe("Not authenticated");
|
|
||||||
expect(Sentry.captureException).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("throws AuthorizationError when user is not found", async () => {
|
|
||||||
vi.mocked(getServerSession).mockResolvedValue({ user: { id: "user-1" } });
|
|
||||||
vi.mocked(getUser).mockResolvedValue(null as any);
|
|
||||||
|
|
||||||
const action = authenticatedActionClient.action(async () => "ok");
|
|
||||||
const result = await action();
|
|
||||||
|
|
||||||
expect(result?.serverError).toBe("User not found");
|
|
||||||
expect(Sentry.captureException).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
test("executes action successfully when session and user exist", async () => {
|
|
||||||
vi.mocked(getServerSession).mockResolvedValue({ user: { id: "user-1" } });
|
|
||||||
vi.mocked(getUser).mockResolvedValue({ id: "user-1", name: "Test" } as any);
|
|
||||||
|
|
||||||
const action = authenticatedActionClient.action(async () => "success");
|
|
||||||
const result = await action();
|
|
||||||
|
|
||||||
expect(result?.data).toBe("success");
|
|
||||||
expect(result?.serverError).toBeUndefined();
|
|
||||||
expect(Sentry.captureException).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -3,7 +3,15 @@ import { getServerSession } from "next-auth";
|
|||||||
import { DEFAULT_SERVER_ERROR_MESSAGE, createSafeActionClient } from "next-safe-action";
|
import { DEFAULT_SERVER_ERROR_MESSAGE, createSafeActionClient } from "next-safe-action";
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
import { logger } from "@formbricks/logger";
|
import { logger } from "@formbricks/logger";
|
||||||
import { AuthenticationError, AuthorizationError, isExpectedError } from "@formbricks/types/errors";
|
import {
|
||||||
|
AuthenticationError,
|
||||||
|
AuthorizationError,
|
||||||
|
InvalidInputError,
|
||||||
|
OperationNotAllowedError,
|
||||||
|
ResourceNotFoundError,
|
||||||
|
TooManyRequestsError,
|
||||||
|
UnknownError,
|
||||||
|
} from "@formbricks/types/errors";
|
||||||
import { AUDIT_LOG_ENABLED, AUDIT_LOG_GET_USER_IP } from "@/lib/constants";
|
import { AUDIT_LOG_ENABLED, AUDIT_LOG_GET_USER_IP } from "@/lib/constants";
|
||||||
import { getUser } from "@/lib/user/service";
|
import { getUser } from "@/lib/user/service";
|
||||||
import { getClientIpFromHeaders } from "@/lib/utils/client-ip";
|
import { getClientIpFromHeaders } from "@/lib/utils/client-ip";
|
||||||
@@ -14,18 +22,24 @@ import { ActionClientCtx } from "./types/context";
|
|||||||
export const actionClient = createSafeActionClient({
|
export const actionClient = createSafeActionClient({
|
||||||
handleServerError(e, utils) {
|
handleServerError(e, utils) {
|
||||||
const eventId = (utils.ctx as Record<string, any>)?.auditLoggingCtx?.eventId ?? undefined; // keep explicit fallback
|
const eventId = (utils.ctx as Record<string, any>)?.auditLoggingCtx?.eventId ?? undefined; // keep explicit fallback
|
||||||
|
|
||||||
if (isExpectedError(e)) {
|
|
||||||
return e.message;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only capture unexpected errors to Sentry
|
|
||||||
Sentry.captureException(e, {
|
Sentry.captureException(e, {
|
||||||
extra: {
|
extra: {
|
||||||
eventId,
|
eventId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
e instanceof ResourceNotFoundError ||
|
||||||
|
e instanceof AuthorizationError ||
|
||||||
|
e instanceof InvalidInputError ||
|
||||||
|
e instanceof UnknownError ||
|
||||||
|
e instanceof AuthenticationError ||
|
||||||
|
e instanceof OperationNotAllowedError ||
|
||||||
|
e instanceof TooManyRequestsError
|
||||||
|
) {
|
||||||
|
return e.message;
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line no-console -- This error needs to be logged for debugging server-side errors
|
// eslint-disable-next-line no-console -- This error needs to be logged for debugging server-side errors
|
||||||
logger.withContext({ eventId }).error(e, "SERVER ERROR");
|
logger.withContext({ eventId }).error(e, "SERVER ERROR");
|
||||||
return DEFAULT_SERVER_ERROR_MESSAGE;
|
return DEFAULT_SERVER_ERROR_MESSAGE;
|
||||||
|
|||||||
@@ -11,16 +11,3 @@ export const isSafeIdentifier = (value: string): boolean => {
|
|||||||
// Can only contain lowercase letters, numbers, and underscores
|
// Can only contain lowercase letters, numbers, and underscores
|
||||||
return /^[a-z0-9_]+$/.test(value);
|
return /^[a-z0-9_]+$/.test(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a snake_case string to Title Case for display as a label.
|
|
||||||
* Example: "job_description" -> "Job Description"
|
|
||||||
* "api_key" -> "Api Key"
|
|
||||||
* "signup_date" -> "Signup Date"
|
|
||||||
*/
|
|
||||||
export const formatSnakeCaseToTitleCase = (key: string): string => {
|
|
||||||
return key
|
|
||||||
.split("_")
|
|
||||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
||||||
.join(" ");
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -12,18 +12,11 @@ export function validateInputs<T extends ValidationPair<any>[]>(
|
|||||||
for (const [value, schema] of pairs) {
|
for (const [value, schema] of pairs) {
|
||||||
const inputValidation = schema.safeParse(value);
|
const inputValidation = schema.safeParse(value);
|
||||||
if (!inputValidation.success) {
|
if (!inputValidation.success) {
|
||||||
const zodDetails = inputValidation.error.issues
|
|
||||||
.map((issue) => {
|
|
||||||
const path = issue?.path?.join(".") ?? "";
|
|
||||||
return `${path}${issue.message}`;
|
|
||||||
})
|
|
||||||
.join("; ");
|
|
||||||
|
|
||||||
logger.error(
|
logger.error(
|
||||||
inputValidation.error,
|
inputValidation.error,
|
||||||
`Validation failed for ${JSON.stringify(value).substring(0, 100)} and ${JSON.stringify(schema)}`
|
`Validation failed for ${JSON.stringify(value).substring(0, 100)} and ${JSON.stringify(schema)}`
|
||||||
);
|
);
|
||||||
throw new ValidationError(`Validation failed: ${zodDetails}`);
|
throw new ValidationError("Validation failed");
|
||||||
}
|
}
|
||||||
parsedData.push(inputValidation.data);
|
parsedData.push(inputValidation.data);
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-177
@@ -188,7 +188,6 @@
|
|||||||
"customer_success": "Kundenerfolg",
|
"customer_success": "Kundenerfolg",
|
||||||
"dark_overlay": "Dunkle Überlagerung",
|
"dark_overlay": "Dunkle Überlagerung",
|
||||||
"date": "Datum",
|
"date": "Datum",
|
||||||
"days": "Tage",
|
|
||||||
"default": "Standard",
|
"default": "Standard",
|
||||||
"delete": "Löschen",
|
"delete": "Löschen",
|
||||||
"description": "Beschreibung",
|
"description": "Beschreibung",
|
||||||
@@ -218,7 +217,6 @@
|
|||||||
"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",
|
||||||
@@ -277,7 +275,6 @@
|
|||||||
"mobile_overlay_app_works_best_on_desktop": "Formbricks funktioniert am besten auf einem größeren Bildschirm. Um Umfragen zu verwalten oder zu erstellen, wechsle zu einem anderen Gerät.",
|
"mobile_overlay_app_works_best_on_desktop": "Formbricks funktioniert am besten auf einem größeren Bildschirm. Um Umfragen zu verwalten oder zu erstellen, wechsle zu einem anderen Gerät.",
|
||||||
"mobile_overlay_surveys_look_good": "Keine Sorge – deine Umfragen sehen auf jedem Gerät und jeder Bildschirmgröße großartig aus!",
|
"mobile_overlay_surveys_look_good": "Keine Sorge – deine Umfragen sehen auf jedem Gerät und jeder Bildschirmgröße großartig aus!",
|
||||||
"mobile_overlay_title": "Oops, Bildschirm zu klein erkannt!",
|
"mobile_overlay_title": "Oops, Bildschirm zu klein erkannt!",
|
||||||
"months": "Monate",
|
|
||||||
"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",
|
||||||
@@ -288,7 +285,6 @@
|
|||||||
"no_background_image_found": "Kein Hintergrundbild gefunden.",
|
"no_background_image_found": "Kein Hintergrundbild gefunden.",
|
||||||
"no_code": "No Code",
|
"no_code": "No Code",
|
||||||
"no_files_uploaded": "Keine Dateien hochgeladen",
|
"no_files_uploaded": "Keine Dateien hochgeladen",
|
||||||
"no_overlay": "Kein Overlay",
|
|
||||||
"no_quotas_found": "Keine Kontingente gefunden",
|
"no_quotas_found": "Keine Kontingente gefunden",
|
||||||
"no_result_found": "Kein Ergebnis gefunden",
|
"no_result_found": "Kein Ergebnis gefunden",
|
||||||
"no_results": "Keine Ergebnisse",
|
"no_results": "Keine Ergebnisse",
|
||||||
@@ -315,7 +311,6 @@
|
|||||||
"organization_teams_not_found": "Organisations-Teams nicht gefunden",
|
"organization_teams_not_found": "Organisations-Teams nicht gefunden",
|
||||||
"other": "Andere",
|
"other": "Andere",
|
||||||
"others": "Andere",
|
"others": "Andere",
|
||||||
"overlay_color": "Overlay-Farbe",
|
|
||||||
"overview": "Überblick",
|
"overview": "Überblick",
|
||||||
"password": "Passwort",
|
"password": "Passwort",
|
||||||
"paused": "Pausiert",
|
"paused": "Pausiert",
|
||||||
@@ -355,7 +350,6 @@
|
|||||||
"request_trial_license": "Testlizenz anfordern",
|
"request_trial_license": "Testlizenz anfordern",
|
||||||
"reset_to_default": "Auf Standard zurücksetzen",
|
"reset_to_default": "Auf Standard zurücksetzen",
|
||||||
"response": "Antwort",
|
"response": "Antwort",
|
||||||
"response_id": "Antwort-ID",
|
|
||||||
"responses": "Antworten",
|
"responses": "Antworten",
|
||||||
"restart": "Neustart",
|
"restart": "Neustart",
|
||||||
"role": "Rolle",
|
"role": "Rolle",
|
||||||
@@ -396,7 +390,6 @@
|
|||||||
"status": "Status",
|
"status": "Status",
|
||||||
"step_by_step_manual": "Schritt-für-Schritt-Anleitung",
|
"step_by_step_manual": "Schritt-für-Schritt-Anleitung",
|
||||||
"storage_not_configured": "Dateispeicher nicht eingerichtet, Uploads werden wahrscheinlich fehlschlagen",
|
"storage_not_configured": "Dateispeicher nicht eingerichtet, Uploads werden wahrscheinlich fehlschlagen",
|
||||||
"string": "Text",
|
|
||||||
"styling": "Styling",
|
"styling": "Styling",
|
||||||
"submit": "Abschicken",
|
"submit": "Abschicken",
|
||||||
"summary": "Zusammenfassung",
|
"summary": "Zusammenfassung",
|
||||||
@@ -429,7 +422,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",
|
||||||
@@ -453,7 +445,6 @@
|
|||||||
"website_and_app_connection": "Website & App Verbindung",
|
"website_and_app_connection": "Website & App Verbindung",
|
||||||
"website_app_survey": "Website- & App-Umfrage",
|
"website_app_survey": "Website- & App-Umfrage",
|
||||||
"website_survey": "Website-Umfrage",
|
"website_survey": "Website-Umfrage",
|
||||||
"weeks": "Wochen",
|
|
||||||
"welcome_card": "Willkommenskarte",
|
"welcome_card": "Willkommenskarte",
|
||||||
"workspace_configuration": "Projektkonfiguration",
|
"workspace_configuration": "Projektkonfiguration",
|
||||||
"workspace_created_successfully": "Projekt erfolgreich erstellt",
|
"workspace_created_successfully": "Projekt erfolgreich erstellt",
|
||||||
@@ -464,7 +455,6 @@
|
|||||||
"workspace_not_found": "Projekt nicht gefunden",
|
"workspace_not_found": "Projekt nicht gefunden",
|
||||||
"workspace_permission_not_found": "Projektberechtigung nicht gefunden",
|
"workspace_permission_not_found": "Projektberechtigung nicht gefunden",
|
||||||
"workspaces": "Projekte",
|
"workspaces": "Projekte",
|
||||||
"years": "Jahre",
|
|
||||||
"you": "Du",
|
"you": "Du",
|
||||||
"you_are_downgraded_to_the_community_edition": "Du wurdest auf die Community Edition herabgestuft.",
|
"you_are_downgraded_to_the_community_edition": "Du wurdest auf die Community Edition herabgestuft.",
|
||||||
"you_are_not_authorized_to_perform_this_action": "Du bist nicht berechtigt, diese Aktion durchzuführen.",
|
"you_are_not_authorized_to_perform_this_action": "Du bist nicht berechtigt, diese Aktion durchzuführen.",
|
||||||
@@ -625,7 +615,7 @@
|
|||||||
"attribute_created_successfully": "Attribut erfolgreich erstellt",
|
"attribute_created_successfully": "Attribut erfolgreich erstellt",
|
||||||
"attribute_description": "Beschreibung",
|
"attribute_description": "Beschreibung",
|
||||||
"attribute_description_placeholder": "Kurze Beschreibung",
|
"attribute_description_placeholder": "Kurze Beschreibung",
|
||||||
"attribute_key": "Schlüssel",
|
"attribute_key": "Attribut",
|
||||||
"attribute_key_cannot_be_changed": "Schlüssel kann nach der Erstellung nicht geändert werden",
|
"attribute_key_cannot_be_changed": "Schlüssel kann nach der Erstellung nicht geändert werden",
|
||||||
"attribute_key_hint": "Nur Kleinbuchstaben, Zahlen und Unterstriche. Muss mit einem Buchstaben beginnen.",
|
"attribute_key_hint": "Nur Kleinbuchstaben, Zahlen und Unterstriche. Muss mit einem Buchstaben beginnen.",
|
||||||
"attribute_key_placeholder": "z. B. geburtsdatum",
|
"attribute_key_placeholder": "z. B. geburtsdatum",
|
||||||
@@ -636,45 +626,28 @@
|
|||||||
"attribute_updated_successfully": "Attribut erfolgreich aktualisiert",
|
"attribute_updated_successfully": "Attribut erfolgreich aktualisiert",
|
||||||
"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_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_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_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",
|
|
||||||
"data_type": "Datentyp",
|
|
||||||
"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",
|
|
||||||
"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",
|
||||||
"edit_attribute_values_description": "Ändern Sie die Werte für bestimmte Attribute dieses Kontakts.",
|
"edit_attribute_values_description": "Ändern Sie die Werte für bestimmte Attribute dieses Kontakts.",
|
||||||
"edit_attributes": "Attribute bearbeiten",
|
|
||||||
"edit_attributes_success": "Kontaktattribute erfolgreich aktualisiert",
|
"edit_attributes_success": "Kontaktattribute erfolgreich aktualisiert",
|
||||||
"generate_personal_link": "Persönlichen Link generieren",
|
"generate_personal_link": "Persönlichen Link generieren",
|
||||||
"generate_personal_link_description": "Wähle eine veröffentlichte Umfrage aus, um einen personalisierten Link für diesen Kontakt zu generieren.",
|
"generate_personal_link_description": "Wähle eine veröffentlichte Umfrage aus, um einen personalisierten Link für diesen Kontakt zu generieren.",
|
||||||
"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_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",
|
||||||
"not_provided": "Nicht angegeben",
|
"not_provided": "Nicht angegeben",
|
||||||
"number_value_required": "Zahlenwert ist erforderlich. Verwende die Löschen-Schaltfläche, um dieses Attribut zu entfernen.",
|
|
||||||
"personal_link_generated": "Persönlicher Link erfolgreich generiert",
|
"personal_link_generated": "Persönlicher Link erfolgreich generiert",
|
||||||
"personal_link_generated_but_clipboard_failed": "Persönlicher Link wurde generiert, konnte aber nicht in die Zwischenablage kopiert werden: {url}",
|
"personal_link_generated_but_clipboard_failed": "Persönlicher Link wurde generiert, konnte aber nicht in die Zwischenablage kopiert werden: {url}",
|
||||||
"personal_survey_link": "Link zur persönlichen Umfrage",
|
"personal_survey_link": "Link zur persönlichen Umfrage",
|
||||||
@@ -683,24 +656,13 @@
|
|||||||
"search_contact": "Kontakt suchen",
|
"search_contact": "Kontakt suchen",
|
||||||
"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",
|
|
||||||
"survey_viewed": "Umfrage angesehen",
|
|
||||||
"survey_viewed_at": "Angesehen am",
|
|
||||||
"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}\" 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_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_invalid_file_type": "Bitte lade eine CSV-Datei hoch",
|
|
||||||
"upload_contacts_error_no_valid_contacts": "Die hochgeladene CSV-Datei enthält keine gültigen Kontakte. Bitte schaue dir die Beispiel-CSV-Datei für das richtige Format an.",
|
|
||||||
"upload_contacts_modal_attribute_header": "Formbricks-Attribut",
|
|
||||||
"upload_contacts_modal_attributes_description": "Ordne die Spalten in deiner CSV den Attributen in Formbricks zu.",
|
"upload_contacts_modal_attributes_description": "Ordne die Spalten in deiner CSV den Attributen in Formbricks zu.",
|
||||||
"upload_contacts_modal_attributes_new": "Neues Attribut",
|
"upload_contacts_modal_attributes_new": "Neues Attribut",
|
||||||
"upload_contacts_modal_attributes_search_or_add": "Attribut suchen oder hinzufügen",
|
"upload_contacts_modal_attributes_search_or_add": "Attribut suchen oder hinzufügen",
|
||||||
|
"upload_contacts_modal_attributes_should_be_mapped_to": "sollte zugeordnet werden zu",
|
||||||
"upload_contacts_modal_attributes_title": "Attribute",
|
"upload_contacts_modal_attributes_title": "Attribute",
|
||||||
"upload_contacts_modal_csv_column_header": "CSV-Spalte",
|
|
||||||
"upload_contacts_modal_description": "Lade eine CSV hoch, um Kontakte mit Attributen schnell zu importieren",
|
"upload_contacts_modal_description": "Lade eine CSV hoch, um Kontakte mit Attributen schnell zu importieren",
|
||||||
"upload_contacts_modal_download_example_csv": "Beispiel-CSV herunterladen",
|
"upload_contacts_modal_download_example_csv": "Beispiel-CSV herunterladen",
|
||||||
"upload_contacts_modal_duplicates_description": "Wie sollen wir vorgehen, wenn ein Kontakt bereits existiert?",
|
"upload_contacts_modal_duplicates_description": "Wie sollen wir vorgehen, wenn ein Kontakt bereits existiert?",
|
||||||
@@ -757,12 +719,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",
|
||||||
@@ -886,40 +843,6 @@
|
|||||||
"no_attributes_yet": "Noch keine Attribute",
|
"no_attributes_yet": "Noch keine Attribute",
|
||||||
"no_filters_yet": "Es gibt noch keine Filter",
|
"no_filters_yet": "Es gibt noch keine Filter",
|
||||||
"no_segments_yet": "Du hast momentan keine gespeicherten Segmente.",
|
"no_segments_yet": "Du hast momentan keine gespeicherten Segmente.",
|
||||||
"operator_contains": "enthält",
|
|
||||||
"operator_does_not_contain": "enthält nicht",
|
|
||||||
"operator_ends_with": "endet mit",
|
|
||||||
"operator_is_after": "ist nach",
|
|
||||||
"operator_is_before": "ist vor",
|
|
||||||
"operator_is_between": "ist zwischen",
|
|
||||||
"operator_is_newer_than": "ist neuer als",
|
|
||||||
"operator_is_not_set": "ist nicht festgelegt",
|
|
||||||
"operator_is_older_than": "ist älter als",
|
|
||||||
"operator_is_same_day": "ist am selben Tag",
|
|
||||||
"operator_is_set": "ist festgelegt",
|
|
||||||
"operator_starts_with": "fängt an mit",
|
|
||||||
"operator_title_contains": "Enthält",
|
|
||||||
"operator_title_does_not_contain": "Enthält nicht",
|
|
||||||
"operator_title_ends_with": "Endet mit",
|
|
||||||
"operator_title_equals": "Gleich",
|
|
||||||
"operator_title_greater_equal": "Größer als oder gleich",
|
|
||||||
"operator_title_greater_than": "Größer als",
|
|
||||||
"operator_title_is_after": "Ist nach",
|
|
||||||
"operator_title_is_before": "Ist vor",
|
|
||||||
"operator_title_is_between": "Ist zwischen",
|
|
||||||
"operator_title_is_newer_than": "Ist neuer als",
|
|
||||||
"operator_title_is_not_set": "Ist nicht festgelegt",
|
|
||||||
"operator_title_is_older_than": "Ist älter als",
|
|
||||||
"operator_title_is_same_day": "Ist am selben Tag",
|
|
||||||
"operator_title_is_set": "Ist festgelegt",
|
|
||||||
"operator_title_less_equal": "Kleiner oder gleich",
|
|
||||||
"operator_title_less_than": "Kleiner als",
|
|
||||||
"operator_title_not_equals": "Ist nicht gleich",
|
|
||||||
"operator_title_starts_with": "Fängt an mit",
|
|
||||||
"operator_title_user_is_in": "Nutzer ist in",
|
|
||||||
"operator_title_user_is_not_in": "Nutzer ist nicht in",
|
|
||||||
"operator_user_is_in": "Nutzer ist in",
|
|
||||||
"operator_user_is_not_in": "Nutzer ist nicht in",
|
|
||||||
"person_and_attributes": "Person & Attribute",
|
"person_and_attributes": "Person & Attribute",
|
||||||
"phone": "Handy",
|
"phone": "Handy",
|
||||||
"please_remove_the_segment_from_these_surveys_in_order_to_delete_it": "Bitte entferne das Segment aus diesen Umfragen, um es zu löschen.",
|
"please_remove_the_segment_from_these_surveys_in_order_to_delete_it": "Bitte entferne das Segment aus diesen Umfragen, um es zu löschen.",
|
||||||
@@ -944,7 +867,6 @@
|
|||||||
"user_targeting_is_currently_only_available_when": "Benutzerzielgruppen sind derzeit nur verfügbar, wenn",
|
"user_targeting_is_currently_only_available_when": "Benutzerzielgruppen sind derzeit nur verfügbar, wenn",
|
||||||
"value_cannot_be_empty": "Wert darf nicht leer sein.",
|
"value_cannot_be_empty": "Wert darf nicht leer sein.",
|
||||||
"value_must_be_a_number": "Wert muss eine Zahl sein.",
|
"value_must_be_a_number": "Wert muss eine Zahl sein.",
|
||||||
"value_must_be_positive": "Wert muss eine positive Zahl sein.",
|
|
||||||
"view_filters": "Filter anzeigen",
|
"view_filters": "Filter anzeigen",
|
||||||
"where": "Wo",
|
"where": "Wo",
|
||||||
"with_the_formbricks_sdk": "mit dem Formbricks SDK"
|
"with_the_formbricks_sdk": "mit dem Formbricks SDK"
|
||||||
@@ -1031,32 +953,19 @@
|
|||||||
"enterprise_features": "Unternehmensfunktionen",
|
"enterprise_features": "Unternehmensfunktionen",
|
||||||
"get_an_enterprise_license_to_get_access_to_all_features": "Hol dir eine Enterprise-Lizenz, um Zugriff auf alle Funktionen zu erhalten.",
|
"get_an_enterprise_license_to_get_access_to_all_features": "Hol dir eine Enterprise-Lizenz, um Zugriff auf alle Funktionen zu erhalten.",
|
||||||
"keep_full_control_over_your_data_privacy_and_security": "Behalte die volle Kontrolle über deine Daten, Privatsphäre und Sicherheit.",
|
"keep_full_control_over_your_data_privacy_and_security": "Behalte die volle Kontrolle über deine Daten, Privatsphäre und Sicherheit.",
|
||||||
"license_invalid_description": "Der Lizenzschlüssel in deiner ENTERPRISE_LICENSE_KEY-Umgebungsvariable ist nicht gültig. Bitte überprüfe auf Tippfehler oder fordere einen neuen Schlüssel an.",
|
|
||||||
"license_status": "Lizenzstatus",
|
|
||||||
"license_status_active": "Aktiv",
|
|
||||||
"license_status_description": "Status deiner Enterprise-Lizenz.",
|
|
||||||
"license_status_expired": "Abgelaufen",
|
|
||||||
"license_status_invalid": "Ungültige Lizenz",
|
|
||||||
"license_status_unreachable": "Nicht erreichbar",
|
|
||||||
"license_unreachable_grace_period": "Der Lizenzserver ist nicht erreichbar. Deine Enterprise-Funktionen bleiben während einer 3-tägigen Kulanzfrist bis zum {gracePeriodEnd} aktiv.",
|
|
||||||
"no_call_needed_no_strings_attached_request_a_free_30_day_trial_license_to_test_all_features_by_filling_out_this_form": "Ganz unkompliziert: Fordere eine kostenlose 30-Tage-Testlizenz an, um alle Funktionen zu testen, indem Du dieses Formular ausfüllst:",
|
"no_call_needed_no_strings_attached_request_a_free_30_day_trial_license_to_test_all_features_by_filling_out_this_form": "Ganz unkompliziert: Fordere eine kostenlose 30-Tage-Testlizenz an, um alle Funktionen zu testen, indem Du dieses Formular ausfüllst:",
|
||||||
"no_credit_card_no_sales_call_just_test_it": "Keine Kreditkarte. Kein Verkaufsgespräch. Einfach testen :)",
|
"no_credit_card_no_sales_call_just_test_it": "Keine Kreditkarte. Kein Verkaufsgespräch. Einfach testen :)",
|
||||||
"on_request": "Auf Anfrage",
|
"on_request": "Auf Anfrage",
|
||||||
"organization_roles": "Organisationsrollen (Admin, Editor, Entwickler, etc.)",
|
"organization_roles": "Organisationsrollen (Admin, Editor, Entwickler, etc.)",
|
||||||
"questions_please_reach_out_to": "Fragen? Bitte melde Dich bei",
|
"questions_please_reach_out_to": "Fragen? Bitte melde Dich bei",
|
||||||
"recheck_license": "Lizenz erneut prüfen",
|
|
||||||
"recheck_license_failed": "Lizenzprüfung fehlgeschlagen. Der Lizenzserver ist möglicherweise nicht erreichbar.",
|
|
||||||
"recheck_license_invalid": "Der Lizenzschlüssel ist ungültig. Bitte überprüfe deinen ENTERPRISE_LICENSE_KEY.",
|
|
||||||
"recheck_license_success": "Lizenzprüfung erfolgreich",
|
|
||||||
"recheck_license_unreachable": "Lizenzserver ist nicht erreichbar. Bitte versuche es später erneut.",
|
|
||||||
"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",
|
||||||
"soc2_hipaa_iso_27001_compliance_check": "SOC2-, HIPAA- und ISO 27001-Konformitätsprüfung",
|
"soc2_hipaa_iso_27001_compliance_check": "SOC2-, HIPAA- und ISO 27001-Konformitätsprüfung",
|
||||||
"sso": "SSO (Google, Microsoft, OpenID Connect)",
|
"sso": "SSO (Google, Microsoft, OpenID Connect)",
|
||||||
"teams": "Teams & Zugriffskontrolle (Lesen, Lesen & Schreiben, Verwalten)",
|
"teams": "Teams & Zugriffskontrolle (Lesen, Lesen & Schreiben, Verwalten)",
|
||||||
"unlock_the_full_power_of_formbricks_free_for_30_days": "Schalte die volle Power von Formbricks frei. 30 Tage kostenlos."
|
"unlock_the_full_power_of_formbricks_free_for_30_days": "Schalte die volle Power von Formbricks frei. 30 Tage kostenlos.",
|
||||||
|
"your_enterprise_license_is_active_all_features_unlocked": "Deine Unternehmenslizenz ist aktiv. Alle Funktionen freigeschaltet."
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"bulk_invite_warning_description": "Bitte beachte, dass im Free-Plan alle Organisationsmitglieder automatisch die Rolle \"Owner\" zugewiesen bekommen, unabhängig von der im CSV-File angegebenen Rolle.",
|
"bulk_invite_warning_description": "Bitte beachte, dass im Free-Plan alle Organisationsmitglieder automatisch die Rolle \"Owner\" zugewiesen bekommen, unabhängig von der im CSV-File angegebenen Rolle.",
|
||||||
@@ -1080,7 +989,7 @@
|
|||||||
"from_your_organization": "von deiner 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}",
|
"invited_on": "Eingeladen am {date}",
|
||||||
"invites_failed": "Einladungen fehlgeschlagen",
|
"invites_failed": "Einladungen fehlgeschlagen",
|
||||||
"leave_organization": "Organisation verlassen",
|
"leave_organization": "Organisation verlassen",
|
||||||
"leave_organization_description": "Du wirst diese Organisation verlassen und den Zugriff auf alle Umfragen und Antworten verlieren. Du kannst nur wieder beitreten, wenn Du erneut eingeladen wirst.",
|
"leave_organization_description": "Du wirst diese Organisation verlassen und den Zugriff auf alle Umfragen und Antworten verlieren. Du kannst nur wieder beitreten, wenn Du erneut eingeladen wirst.",
|
||||||
@@ -1193,6 +1102,8 @@
|
|||||||
"please_fill_all_workspace_fields": "Bitte füllen Sie alle Felder aus, um einen neuen Workspace hinzuzufügen.",
|
"please_fill_all_workspace_fields": "Bitte füllen Sie alle Felder aus, um einen neuen Workspace hinzuzufügen.",
|
||||||
"read": "Lesen",
|
"read": "Lesen",
|
||||||
"read_write": "Lesen & Schreiben",
|
"read_write": "Lesen & Schreiben",
|
||||||
|
"select_member": "Mitglied auswählen",
|
||||||
|
"select_workspace": "Workspace auswählen",
|
||||||
"team_admin": "Team-Admin",
|
"team_admin": "Team-Admin",
|
||||||
"team_created_successfully": "Team erfolgreich erstellt.",
|
"team_created_successfully": "Team erfolgreich erstellt.",
|
||||||
"team_deleted_successfully": "Team erfolgreich gelöscht.",
|
"team_deleted_successfully": "Team erfolgreich gelöscht.",
|
||||||
@@ -1242,6 +1153,7 @@
|
|||||||
"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": "Füge deiner Umfragekarte einen äußeren Rahmen hinzu.",
|
||||||
"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",
|
||||||
@@ -1280,7 +1192,6 @@
|
|||||||
"block_duplicated": "Block dupliziert.",
|
"block_duplicated": "Block dupliziert.",
|
||||||
"bold": "Fett",
|
"bold": "Fett",
|
||||||
"brand_color": "Markenfarbe",
|
"brand_color": "Markenfarbe",
|
||||||
"brand_color_description": "Wird auf Buttons, Links und Hervorhebungen angewendet.",
|
|
||||||
"brightness": "Helligkeit",
|
"brightness": "Helligkeit",
|
||||||
"bulk_edit": "Massenbearbeitung",
|
"bulk_edit": "Massenbearbeitung",
|
||||||
"bulk_edit_description": "Bearbeiten Sie alle Optionen unten, eine pro Zeile. Leere Zeilen werden übersprungen und Duplikate entfernt.",
|
"bulk_edit_description": "Bearbeiten Sie alle Optionen unten, eine pro Zeile. Leere Zeilen werden übersprungen und Duplikate entfernt.",
|
||||||
@@ -1298,9 +1209,7 @@
|
|||||||
"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",
|
||||||
"card_background_color_description": "Füllt den Bereich der Umfragekarte.",
|
|
||||||
"card_border_color": "Farbe des Kartenrandes",
|
"card_border_color": "Farbe des Kartenrandes",
|
||||||
"card_border_color_description": "Umrandet die Umfragekarte.",
|
|
||||||
"card_styling": "Kartengestaltung",
|
"card_styling": "Kartengestaltung",
|
||||||
"casual": "Lässig",
|
"casual": "Lässig",
|
||||||
"caution_edit_duplicate": "Duplizieren & bearbeiten",
|
"caution_edit_duplicate": "Duplizieren & bearbeiten",
|
||||||
@@ -1311,12 +1220,20 @@
|
|||||||
"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.",
|
||||||
"caution_recommendation": "Dies kann im Umfrageübersicht zu Dateninkonsistenzen führen. Wir empfehlen stattdessen, die Umfrage zu duplizieren.",
|
"caution_recommendation": "Dies kann im Umfrageübersicht zu Dateninkonsistenzen führen. Wir empfehlen stattdessen, die Umfrage zu duplizieren.",
|
||||||
"caution_text": "Änderungen werden zu Inkonsistenzen führen",
|
"caution_text": "Änderungen werden zu Inkonsistenzen führen",
|
||||||
|
"centered_modal_overlay_color": "Zentrierte modale Überlagerungsfarbe",
|
||||||
"change_anyway": "Trotzdem ändern",
|
"change_anyway": "Trotzdem ändern",
|
||||||
"change_background": "Hintergrund ändern",
|
"change_background": "Hintergrund ändern",
|
||||||
"change_question_type": "Fragetyp ändern",
|
"change_question_type": "Fragetyp ändern",
|
||||||
"change_survey_type": "Die Änderung des Umfragetypen kann vorhandenen Zugriff beeinträchtigen",
|
"change_survey_type": "Die Änderung des Umfragetypen kann vorhandenen Zugriff beeinträchtigen",
|
||||||
|
"change_the_background_color_of_the_card": "Hintergrundfarbe der Karte ändern.",
|
||||||
|
"change_the_background_color_of_the_input_fields": "Hintergrundfarbe der Eingabefelder ändern.",
|
||||||
"change_the_background_to_a_color_image_or_animation": "Hintergrund zu einer Farbe, einem Bild oder einer Animation ändern.",
|
"change_the_background_to_a_color_image_or_animation": "Hintergrund zu einer Farbe, einem Bild oder einer Animation ändern.",
|
||||||
|
"change_the_border_color_of_the_card": "Randfarbe der Karte ändern.",
|
||||||
|
"change_the_border_color_of_the_input_fields": "Randfarbe der Eingabefelder ändern.",
|
||||||
|
"change_the_border_radius_of_the_card_and_the_inputs": "Radius der Ränder der Karte und der Eingabefelder ändern.",
|
||||||
|
"change_the_brand_color_of_the_survey": "Markenfarbe der Umfrage ändern.",
|
||||||
"change_the_placement_of_this_survey": "Platzierung dieser Umfrage ändern.",
|
"change_the_placement_of_this_survey": "Platzierung dieser Umfrage ändern.",
|
||||||
|
"change_the_question_color_of_the_survey": "Fragefarbe der Umfrage ändern.",
|
||||||
"changes_saved": "Änderungen gespeichert.",
|
"changes_saved": "Änderungen gespeichert.",
|
||||||
"changing_survey_type_will_remove_existing_distribution_channels": "\"Das Ändern des Umfragetypen beeinflusst, wie er geteilt werden kann. Wenn Teilnehmer bereits Zugriffslinks für den aktuellen Typ haben, könnten sie das Zugriffsrecht nach dem Wechsel verlieren.\"",
|
"changing_survey_type_will_remove_existing_distribution_channels": "\"Das Ändern des Umfragetypen beeinflusst, wie er geteilt werden kann. Wenn Teilnehmer bereits Zugriffslinks für den aktuellen Typ haben, könnten sie das Zugriffsrecht nach dem Wechsel verlieren.\"",
|
||||||
"checkbox_label": "Checkbox-Beschriftung",
|
"checkbox_label": "Checkbox-Beschriftung",
|
||||||
@@ -1456,6 +1373,7 @@
|
|||||||
"hide_progress_bar": "Fortschrittsbalken ausblenden",
|
"hide_progress_bar": "Fortschrittsbalken ausblenden",
|
||||||
"hide_question_settings": "Frageeinstellungen ausblenden",
|
"hide_question_settings": "Frageeinstellungen ausblenden",
|
||||||
"hostname": "Hostname",
|
"hostname": "Hostname",
|
||||||
|
"how_funky_do_you_want_your_cards_in_survey_type_derived_surveys": "Wie funky sollen deine Karten in {surveyTypeDerived} Umfragen sein",
|
||||||
"if_you_need_more_please": "Wenn Sie mehr benötigen, bitte",
|
"if_you_need_more_please": "Wenn Sie mehr benötigen, bitte",
|
||||||
"if_you_really_want_that_answer_ask_until_you_get_it": "Weiterhin anzeigen, wenn ausgelöst, bis eine Antwort abgegeben wird.",
|
"if_you_really_want_that_answer_ask_until_you_get_it": "Weiterhin anzeigen, wenn ausgelöst, bis eine Antwort abgegeben wird.",
|
||||||
"ignore_global_waiting_time": "Abkühlphase ignorieren",
|
"ignore_global_waiting_time": "Abkühlphase ignorieren",
|
||||||
@@ -1466,9 +1384,7 @@
|
|||||||
"initial_value": "Anfangswert",
|
"initial_value": "Anfangswert",
|
||||||
"inner_text": "Innerer Text",
|
"inner_text": "Innerer Text",
|
||||||
"input_border_color": "Randfarbe des Eingabefelds",
|
"input_border_color": "Randfarbe des Eingabefelds",
|
||||||
"input_border_color_description": "Umrandet Texteingaben und Textbereiche.",
|
|
||||||
"input_color": "Farbe des Eingabefelds",
|
"input_color": "Farbe des Eingabefelds",
|
||||||
"input_color_description": "Füllt das Innere von Texteingaben.",
|
|
||||||
"insert_link": "Link einfügen",
|
"insert_link": "Link einfügen",
|
||||||
"invalid_targeting": "Ungültiges Targeting: Bitte überprüfe deine Zielgruppenfilter",
|
"invalid_targeting": "Ungültiges Targeting: Bitte überprüfe deine Zielgruppenfilter",
|
||||||
"invalid_video_url_warning": "Bitte gib eine gültige YouTube-, Vimeo- oder Loom-URL ein. Andere Video-Plattformen werden derzeit nicht unterstützt.",
|
"invalid_video_url_warning": "Bitte gib eine gültige YouTube-, Vimeo- oder Loom-URL ein. Andere Video-Plattformen werden derzeit nicht unterstützt.",
|
||||||
@@ -1552,12 +1468,13 @@
|
|||||||
"protect_survey_with_pin_description": "Nur Benutzer, die die PIN haben, können auf die Umfrage zugreifen.",
|
"protect_survey_with_pin_description": "Nur Benutzer, die die PIN haben, können auf die Umfrage zugreifen.",
|
||||||
"publish": "Veröffentlichen",
|
"publish": "Veröffentlichen",
|
||||||
"question": "Frage",
|
"question": "Frage",
|
||||||
|
"question_color": "Fragefarbe",
|
||||||
"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_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",
|
||||||
"question_used_in_recall": "Diese Frage wird in Frage {questionIndex} abgerufen.",
|
"question_used_in_recall": "Diese Frage wird in Frage {questionIndex} abgerufen.",
|
||||||
"question_used_in_recall_ending_card": "Diese Frage wird in der Abschlusskarte abgerufen.",
|
"question_used_in_recall_ending_card": "Diese Frage wird in der Abschlusskarte abgerufen.",
|
||||||
"quotas": {
|
"quotas": {
|
||||||
@@ -1613,7 +1530,6 @@
|
|||||||
"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 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",
|
||||||
@@ -1655,6 +1571,7 @@
|
|||||||
"styling_set_to_theme_styles": "Styling auf Themenstile eingestellt",
|
"styling_set_to_theme_styles": "Styling auf Themenstile eingestellt",
|
||||||
"subheading": "Zwischenüberschrift",
|
"subheading": "Zwischenüberschrift",
|
||||||
"subtract": "Subtrahieren -",
|
"subtract": "Subtrahieren -",
|
||||||
|
"suggest_colors": "Farben vorschlagen",
|
||||||
"survey_completed_heading": "Umfrage abgeschlossen",
|
"survey_completed_heading": "Umfrage abgeschlossen",
|
||||||
"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",
|
||||||
@@ -1731,7 +1648,7 @@
|
|||||||
"validation_rules": "Validierungsregeln",
|
"validation_rules": "Validierungsregeln",
|
||||||
"validation_rules_description": "Nur Antworten akzeptieren, die die folgenden Kriterien erfüllen",
|
"validation_rules_description": "Nur Antworten akzeptieren, die die folgenden Kriterien erfüllen",
|
||||||
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} wird in der Logik der Frage {questionIndex} verwendet. Bitte entferne es zuerst aus der Logik.",
|
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} wird in der Logik der Frage {questionIndex} verwendet. Bitte entferne es zuerst aus der Logik.",
|
||||||
"variable_is_used_in_quota_please_remove_it_from_quota_first": "Variable “{variableName}” wird in der “{quotaName}” Quote verwendet",
|
"variable_is_used_in_quota_please_remove_it_from_quota_first": "Variable \"{variableName}\" wird in der \"{quotaName}\" Quote verwendet",
|
||||||
"variable_name_conflicts_with_hidden_field": "Der Variablenname steht im Konflikt mit einer vorhandenen Hidden-Field-ID.",
|
"variable_name_conflicts_with_hidden_field": "Der Variablenname steht im Konflikt mit einer vorhandenen Hidden-Field-ID.",
|
||||||
"variable_name_is_already_taken_please_choose_another": "Variablenname ist bereits vergeben, bitte wähle einen anderen.",
|
"variable_name_is_already_taken_please_choose_another": "Variablenname ist bereits vergeben, bitte wähle einen anderen.",
|
||||||
"variable_name_must_start_with_a_letter": "Variablenname muss mit einem Buchstaben beginnen.",
|
"variable_name_must_start_with_a_letter": "Variablenname muss mit einem Buchstaben beginnen.",
|
||||||
@@ -1957,7 +1874,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",
|
||||||
@@ -2000,7 +1916,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",
|
||||||
@@ -2140,71 +2055,9 @@
|
|||||||
"look": {
|
"look": {
|
||||||
"add_background_color": "Hintergrundfarbe hinzufügen",
|
"add_background_color": "Hintergrundfarbe hinzufügen",
|
||||||
"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_button_bg": "Button-Hintergrund",
|
|
||||||
"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_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_height_description": "Steuert die Button-Höhe.",
|
|
||||||
"advanced_styling_field_button_padding_x_description": "Fügt links und rechts Abstand hinzu.",
|
|
||||||
"advanced_styling_field_button_padding_y_description": "Fügt oben und unten Abstand hinzu.",
|
|
||||||
"advanced_styling_field_button_text": "Button-Text",
|
|
||||||
"advanced_styling_field_button_text_description": "Färbt die Beschriftung innerhalb von Buttons.",
|
|
||||||
"advanced_styling_field_description_color": "Beschreibungsfarbe",
|
|
||||||
"advanced_styling_field_description_color_description": "Färbt den Text unterhalb jeder Überschrift.",
|
|
||||||
"advanced_styling_field_description_size": "Schriftgröße der Beschreibung",
|
|
||||||
"advanced_styling_field_description_size_description": "Skaliert den Beschreibungstext.",
|
|
||||||
"advanced_styling_field_description_weight": "Schriftstärke der Beschreibung",
|
|
||||||
"advanced_styling_field_description_weight_description": "Macht den Beschreibungstext heller oder fetter.",
|
|
||||||
"advanced_styling_field_font_size": "Schriftgröße",
|
|
||||||
"advanced_styling_field_font_weight": "Schriftstärke",
|
|
||||||
"advanced_styling_field_headline_color": "Überschriftsfarbe",
|
|
||||||
"advanced_styling_field_headline_color_description": "Färbt den Hauptfragetext.",
|
|
||||||
"advanced_styling_field_headline_size": "Schriftgröße der Überschrift",
|
|
||||||
"advanced_styling_field_headline_size_description": "Skaliert den Überschriftentext.",
|
|
||||||
"advanced_styling_field_headline_weight": "Schriftstärke der Überschrift",
|
|
||||||
"advanced_styling_field_headline_weight_description": "Macht den Überschriftentext heller oder fetter.",
|
|
||||||
"advanced_styling_field_height": "Mindesthöhe",
|
|
||||||
"advanced_styling_field_indicator_bg": "Indikator-Hintergrund",
|
|
||||||
"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_font_size_description": "Skaliert den eingegebenen Text in Eingabefeldern.",
|
|
||||||
"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_y_description": "Fügt oben und unten Abstand hinzu.",
|
|
||||||
"advanced_styling_field_input_placeholder_opacity_description": "Blendet den Platzhaltertext aus.",
|
|
||||||
"advanced_styling_field_input_shadow_description": "Fügt einen Schlagschatten um Eingabefelder hinzu.",
|
|
||||||
"advanced_styling_field_input_text": "Eingabetext",
|
|
||||||
"advanced_styling_field_input_text_description": "Färbt den eingegebenen Text in Eingabefeldern.",
|
|
||||||
"advanced_styling_field_option_bg": "Hintergrund",
|
|
||||||
"advanced_styling_field_option_bg_description": "Füllt die Optionselemente.",
|
|
||||||
"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_label": "Label-Farbe",
|
|
||||||
"advanced_styling_field_option_label_description": "Färbt den Text der Optionsbeschriftung.",
|
|
||||||
"advanced_styling_field_option_padding_x_description": "Fügt links und rechts Abstand hinzu.",
|
|
||||||
"advanced_styling_field_option_padding_y_description": "Fügt oben und unten Abstand hinzu.",
|
|
||||||
"advanced_styling_field_padding_x": "Innenabstand X",
|
|
||||||
"advanced_styling_field_padding_y": "Innenabstand Y",
|
|
||||||
"advanced_styling_field_placeholder_opacity": "Platzhalter-Deckkraft",
|
|
||||||
"advanced_styling_field_shadow": "Schatten",
|
|
||||||
"advanced_styling_field_track_bg": "Track-Hintergrund",
|
|
||||||
"advanced_styling_field_track_bg_description": "Färbt den nicht ausgefüllten Teil des Balkens.",
|
|
||||||
"advanced_styling_field_track_height": "Track-Höhe",
|
|
||||||
"advanced_styling_field_track_height_description": "Steuert die Dicke des Fortschrittsbalkens.",
|
|
||||||
"advanced_styling_field_upper_label_color": "Farbe des oberen Labels",
|
|
||||||
"advanced_styling_field_upper_label_color_description": "Färbt die kleine Beschriftung über Eingabefeldern.",
|
|
||||||
"advanced_styling_field_upper_label_size": "Schriftgröße des oberen Labels",
|
|
||||||
"advanced_styling_field_upper_label_size_description": "Skaliert die kleine Beschriftung über Eingabefeldern.",
|
|
||||||
"advanced_styling_field_upper_label_weight": "Schriftstärke des oberen Labels",
|
|
||||||
"advanced_styling_field_upper_label_weight_description": "Macht die Beschriftung leichter oder fetter.",
|
|
||||||
"advanced_styling_section_buttons": "Buttons",
|
|
||||||
"advanced_styling_section_headlines": "Überschriften & Beschreibungen",
|
|
||||||
"advanced_styling_section_inputs": "Eingabefelder",
|
|
||||||
"advanced_styling_section_options": "Optionen (Radio/Checkbox)",
|
|
||||||
"app_survey_placement": "Platzierung der App-Umfrage",
|
"app_survey_placement": "Platzierung der App-Umfrage",
|
||||||
"app_survey_placement_settings_description": "Ändere, wo Umfragen in deiner Web-App oder Website angezeigt werden.",
|
"app_survey_placement_settings_description": "Ändere, wo Umfragen in deiner Web-App oder Website angezeigt werden.",
|
||||||
|
"centered_modal_overlay_color": "Zentrierte modale Überlagerungsfarbe",
|
||||||
"email_customization": "E-Mail-Anpassung",
|
"email_customization": "E-Mail-Anpassung",
|
||||||
"email_customization_description": "Ändere das Aussehen und die Gestaltung von E-Mails, die Formbricks in deinem Namen versendet.",
|
"email_customization_description": "Ändere das Aussehen und die Gestaltung von E-Mails, die Formbricks in deinem Namen versendet.",
|
||||||
"enable_custom_styling": "Benutzerdefiniertes Styling aktivieren",
|
"enable_custom_styling": "Benutzerdefiniertes Styling aktivieren",
|
||||||
@@ -2215,9 +2068,6 @@
|
|||||||
"formbricks_branding_hidden": "Formbricks-Branding ist ausgeblendet.",
|
"formbricks_branding_hidden": "Formbricks-Branding ist ausgeblendet.",
|
||||||
"formbricks_branding_settings_description": "Wir freuen uns über deine Unterstützung, haben aber Verständnis, wenn du es ausschaltest.",
|
"formbricks_branding_settings_description": "Wir freuen uns über deine Unterstützung, haben aber Verständnis, wenn du es ausschaltest.",
|
||||||
"formbricks_branding_shown": "Formbricks-Branding wird angezeigt.",
|
"formbricks_branding_shown": "Formbricks-Branding wird angezeigt.",
|
||||||
"generate_theme_btn": "Generieren",
|
|
||||||
"generate_theme_confirmation": "Möchtest du ein passendes Farbschema basierend auf deiner Markenfarbe generieren? Dies überschreibt deine aktuellen Farbeinstellungen.",
|
|
||||||
"generate_theme_header": "Farbschema generieren?",
|
|
||||||
"logo_removed_successfully": "Logo erfolgreich entfernt",
|
"logo_removed_successfully": "Logo erfolgreich entfernt",
|
||||||
"logo_settings_description": "Lade dein Firmenlogo hoch, um Umfragen und Link-Vorschauen zu branden.",
|
"logo_settings_description": "Lade dein Firmenlogo hoch, um Umfragen und Link-Vorschauen zu branden.",
|
||||||
"logo_updated_successfully": "Logo erfolgreich aktualisiert",
|
"logo_updated_successfully": "Logo erfolgreich aktualisiert",
|
||||||
@@ -2232,8 +2082,6 @@
|
|||||||
"show_formbricks_branding_in": "Formbricks-Branding in {type}-Umfragen anzeigen",
|
"show_formbricks_branding_in": "Formbricks-Branding in {type}-Umfragen anzeigen",
|
||||||
"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",
|
|
||||||
"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."
|
||||||
},
|
},
|
||||||
@@ -2997,7 +2845,6 @@
|
|||||||
"preview_survey_question_2_choice_1_label": "Ja, halte mich auf dem Laufenden.",
|
"preview_survey_question_2_choice_1_label": "Ja, halte mich auf dem Laufenden.",
|
||||||
"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_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",
|
||||||
|
|||||||
+254
-407
File diff suppressed because it is too large
Load Diff
+24
-177
@@ -188,7 +188,6 @@
|
|||||||
"customer_success": "Éxito del cliente",
|
"customer_success": "Éxito del cliente",
|
||||||
"dark_overlay": "Superposición oscura",
|
"dark_overlay": "Superposición oscura",
|
||||||
"date": "Fecha",
|
"date": "Fecha",
|
||||||
"days": "días",
|
|
||||||
"default": "Predeterminado",
|
"default": "Predeterminado",
|
||||||
"delete": "Eliminar",
|
"delete": "Eliminar",
|
||||||
"description": "Descripción",
|
"description": "Descripción",
|
||||||
@@ -218,7 +217,6 @@
|
|||||||
"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",
|
||||||
@@ -277,7 +275,6 @@
|
|||||||
"mobile_overlay_app_works_best_on_desktop": "Formbricks funciona mejor en una pantalla más grande. Para gestionar o crear encuestas, cambia a otro dispositivo.",
|
"mobile_overlay_app_works_best_on_desktop": "Formbricks funciona mejor en una pantalla más grande. Para gestionar o crear encuestas, cambia a otro dispositivo.",
|
||||||
"mobile_overlay_surveys_look_good": "No te preocupes – ¡tus encuestas se ven geniales en todos los dispositivos y tamaños de pantalla!",
|
"mobile_overlay_surveys_look_good": "No te preocupes – ¡tus encuestas se ven geniales en todos los dispositivos y tamaños de pantalla!",
|
||||||
"mobile_overlay_title": "¡Ups, pantalla pequeña detectada!",
|
"mobile_overlay_title": "¡Ups, pantalla pequeña detectada!",
|
||||||
"months": "meses",
|
|
||||||
"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",
|
||||||
@@ -288,7 +285,6 @@
|
|||||||
"no_background_image_found": "No se encontró imagen de fondo.",
|
"no_background_image_found": "No se encontró imagen de fondo.",
|
||||||
"no_code": "Sin código",
|
"no_code": "Sin código",
|
||||||
"no_files_uploaded": "No se subieron archivos",
|
"no_files_uploaded": "No se subieron archivos",
|
||||||
"no_overlay": "Sin superposición",
|
|
||||||
"no_quotas_found": "No se encontraron cuotas",
|
"no_quotas_found": "No se encontraron cuotas",
|
||||||
"no_result_found": "No se encontró resultado",
|
"no_result_found": "No se encontró resultado",
|
||||||
"no_results": "Sin resultados",
|
"no_results": "Sin resultados",
|
||||||
@@ -315,7 +311,6 @@
|
|||||||
"organization_teams_not_found": "Equipos de la organización no encontrados",
|
"organization_teams_not_found": "Equipos de la organización no encontrados",
|
||||||
"other": "Otro",
|
"other": "Otro",
|
||||||
"others": "Otros",
|
"others": "Otros",
|
||||||
"overlay_color": "Color de superposición",
|
|
||||||
"overview": "Resumen",
|
"overview": "Resumen",
|
||||||
"password": "Contraseña",
|
"password": "Contraseña",
|
||||||
"paused": "Pausado",
|
"paused": "Pausado",
|
||||||
@@ -355,7 +350,6 @@
|
|||||||
"request_trial_license": "Solicitar licencia de prueba",
|
"request_trial_license": "Solicitar licencia de prueba",
|
||||||
"reset_to_default": "Restablecer a valores predeterminados",
|
"reset_to_default": "Restablecer a valores predeterminados",
|
||||||
"response": "Respuesta",
|
"response": "Respuesta",
|
||||||
"response_id": "ID de respuesta",
|
|
||||||
"responses": "Respuestas",
|
"responses": "Respuestas",
|
||||||
"restart": "Reiniciar",
|
"restart": "Reiniciar",
|
||||||
"role": "Rol",
|
"role": "Rol",
|
||||||
@@ -396,7 +390,6 @@
|
|||||||
"status": "Estado",
|
"status": "Estado",
|
||||||
"step_by_step_manual": "Manual paso a paso",
|
"step_by_step_manual": "Manual paso a paso",
|
||||||
"storage_not_configured": "Almacenamiento de archivos no configurado, es probable que fallen las subidas",
|
"storage_not_configured": "Almacenamiento de archivos no configurado, es probable que fallen las subidas",
|
||||||
"string": "Texto",
|
|
||||||
"styling": "Estilo",
|
"styling": "Estilo",
|
||||||
"submit": "Enviar",
|
"submit": "Enviar",
|
||||||
"summary": "Resumen",
|
"summary": "Resumen",
|
||||||
@@ -429,7 +422,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",
|
||||||
@@ -453,7 +445,6 @@
|
|||||||
"website_and_app_connection": "Conexión de sitio web y aplicación",
|
"website_and_app_connection": "Conexión de sitio web y aplicación",
|
||||||
"website_app_survey": "Encuesta de sitio web y aplicación",
|
"website_app_survey": "Encuesta de sitio web y aplicación",
|
||||||
"website_survey": "Encuesta de sitio web",
|
"website_survey": "Encuesta de sitio web",
|
||||||
"weeks": "semanas",
|
|
||||||
"welcome_card": "Tarjeta de bienvenida",
|
"welcome_card": "Tarjeta de bienvenida",
|
||||||
"workspace_configuration": "Configuración del proyecto",
|
"workspace_configuration": "Configuración del proyecto",
|
||||||
"workspace_created_successfully": "Proyecto creado correctamente",
|
"workspace_created_successfully": "Proyecto creado correctamente",
|
||||||
@@ -464,7 +455,6 @@
|
|||||||
"workspace_not_found": "Proyecto no encontrado",
|
"workspace_not_found": "Proyecto no encontrado",
|
||||||
"workspace_permission_not_found": "Permiso del proyecto no encontrado",
|
"workspace_permission_not_found": "Permiso del proyecto no encontrado",
|
||||||
"workspaces": "Proyectos",
|
"workspaces": "Proyectos",
|
||||||
"years": "años",
|
|
||||||
"you": "Tú",
|
"you": "Tú",
|
||||||
"you_are_downgraded_to_the_community_edition": "Has sido degradado a la edición Community.",
|
"you_are_downgraded_to_the_community_edition": "Has sido degradado a la edición Community.",
|
||||||
"you_are_not_authorized_to_perform_this_action": "No tienes autorización para realizar esta acción.",
|
"you_are_not_authorized_to_perform_this_action": "No tienes autorización para realizar esta acción.",
|
||||||
@@ -625,7 +615,7 @@
|
|||||||
"attribute_created_successfully": "Atributo creado con éxito",
|
"attribute_created_successfully": "Atributo creado con éxito",
|
||||||
"attribute_description": "Descripción",
|
"attribute_description": "Descripción",
|
||||||
"attribute_description_placeholder": "Descripción breve",
|
"attribute_description_placeholder": "Descripción breve",
|
||||||
"attribute_key": "Clave",
|
"attribute_key": "Atributo",
|
||||||
"attribute_key_cannot_be_changed": "La clave no se puede cambiar después de la creación",
|
"attribute_key_cannot_be_changed": "La clave no se puede cambiar después de la creación",
|
||||||
"attribute_key_hint": "Solo letras minúsculas, números y guiones bajos. Debe empezar con una letra.",
|
"attribute_key_hint": "Solo letras minúsculas, números y guiones bajos. Debe empezar con una letra.",
|
||||||
"attribute_key_placeholder": "p. ej. fecha_de_nacimiento",
|
"attribute_key_placeholder": "p. ej. fecha_de_nacimiento",
|
||||||
@@ -636,45 +626,28 @@
|
|||||||
"attribute_updated_successfully": "Atributo actualizado con éxito",
|
"attribute_updated_successfully": "Atributo actualizado con éxito",
|
||||||
"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_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_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 tipo '{dataType}'",
|
|
||||||
"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",
|
|
||||||
"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_description": "Elige cómo debe almacenarse y filtrarse este atributo",
|
|
||||||
"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",
|
||||||
"edit_attribute_values_description": "Cambia los valores de atributos específicos para este contacto.",
|
"edit_attribute_values_description": "Cambia los valores de atributos específicos para este contacto.",
|
||||||
"edit_attributes": "Editar atributos",
|
|
||||||
"edit_attributes_success": "Atributos del contacto actualizados correctamente",
|
"edit_attributes_success": "Atributos del contacto actualizados correctamente",
|
||||||
"generate_personal_link": "Generar enlace personal",
|
"generate_personal_link": "Generar enlace personal",
|
||||||
"generate_personal_link_description": "Selecciona una encuesta publicada para generar un enlace personalizado para este contacto.",
|
"generate_personal_link_description": "Selecciona una encuesta publicada para generar un enlace personalizado para este contacto.",
|
||||||
"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_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",
|
||||||
"not_provided": "No proporcionado",
|
"not_provided": "No proporcionado",
|
||||||
"number_value_required": "Se requiere un valor numérico. Usa el botón de eliminar para quitar este atributo.",
|
|
||||||
"personal_link_generated": "Enlace personal generado correctamente",
|
"personal_link_generated": "Enlace personal generado correctamente",
|
||||||
"personal_link_generated_but_clipboard_failed": "Enlace personal generado pero falló al copiar al portapapeles: {url}",
|
"personal_link_generated_but_clipboard_failed": "Enlace personal generado pero falló al copiar al portapapeles: {url}",
|
||||||
"personal_survey_link": "Enlace personal de encuesta",
|
"personal_survey_link": "Enlace personal de encuesta",
|
||||||
@@ -683,24 +656,13 @@
|
|||||||
"search_contact": "Buscar contacto",
|
"search_contact": "Buscar contacto",
|
||||||
"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",
|
|
||||||
"survey_viewed": "Encuesta vista",
|
|
||||||
"survey_viewed_at": "Vista el",
|
|
||||||
"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_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_generic": "Se produjo un error al cargar los contactos. Por favor, inténtalo de nuevo más tarde.",
|
|
||||||
"upload_contacts_error_invalid_file_type": "Por favor, carga un archivo CSV",
|
|
||||||
"upload_contacts_error_no_valid_contacts": "El archivo CSV cargado no contiene ningún contacto válido, por favor consulta el archivo CSV de ejemplo para ver el formato correcto.",
|
|
||||||
"upload_contacts_modal_attribute_header": "Atributo de Formbricks",
|
|
||||||
"upload_contacts_modal_attributes_description": "Asigna las columnas de tu CSV a los atributos en Formbricks.",
|
"upload_contacts_modal_attributes_description": "Asigna las columnas de tu CSV a los atributos en Formbricks.",
|
||||||
"upload_contacts_modal_attributes_new": "Nuevo atributo",
|
"upload_contacts_modal_attributes_new": "Nuevo atributo",
|
||||||
"upload_contacts_modal_attributes_search_or_add": "Buscar o añadir atributo",
|
"upload_contacts_modal_attributes_search_or_add": "Buscar o añadir atributo",
|
||||||
|
"upload_contacts_modal_attributes_should_be_mapped_to": "debe asignarse a",
|
||||||
"upload_contacts_modal_attributes_title": "Atributos",
|
"upload_contacts_modal_attributes_title": "Atributos",
|
||||||
"upload_contacts_modal_csv_column_header": "Columna CSV",
|
|
||||||
"upload_contacts_modal_description": "Sube un CSV para importar rápidamente contactos con atributos",
|
"upload_contacts_modal_description": "Sube un CSV para importar rápidamente contactos con atributos",
|
||||||
"upload_contacts_modal_download_example_csv": "Descargar CSV de ejemplo",
|
"upload_contacts_modal_download_example_csv": "Descargar CSV de ejemplo",
|
||||||
"upload_contacts_modal_duplicates_description": "¿Cómo deberíamos manejar si un contacto ya existe en tus contactos?",
|
"upload_contacts_modal_duplicates_description": "¿Cómo deberíamos manejar si un contacto ya existe en tus contactos?",
|
||||||
@@ -757,12 +719,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. 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",
|
||||||
@@ -886,40 +843,6 @@
|
|||||||
"no_attributes_yet": "¡Aún no hay atributos!",
|
"no_attributes_yet": "¡Aún no hay atributos!",
|
||||||
"no_filters_yet": "¡Aún no hay filtros!",
|
"no_filters_yet": "¡Aún no hay filtros!",
|
||||||
"no_segments_yet": "Actualmente no tienes segmentos guardados.",
|
"no_segments_yet": "Actualmente no tienes segmentos guardados.",
|
||||||
"operator_contains": "contiene",
|
|
||||||
"operator_does_not_contain": "no contiene",
|
|
||||||
"operator_ends_with": "termina con",
|
|
||||||
"operator_is_after": "es después de",
|
|
||||||
"operator_is_before": "es antes de",
|
|
||||||
"operator_is_between": "está entre",
|
|
||||||
"operator_is_newer_than": "es más reciente que",
|
|
||||||
"operator_is_not_set": "no está establecido",
|
|
||||||
"operator_is_older_than": "es más antiguo que",
|
|
||||||
"operator_is_same_day": "es el mismo día",
|
|
||||||
"operator_is_set": "está establecido",
|
|
||||||
"operator_starts_with": "comienza con",
|
|
||||||
"operator_title_contains": "Contiene",
|
|
||||||
"operator_title_does_not_contain": "No contiene",
|
|
||||||
"operator_title_ends_with": "Termina con",
|
|
||||||
"operator_title_equals": "Es igual a",
|
|
||||||
"operator_title_greater_equal": "Mayor o igual que",
|
|
||||||
"operator_title_greater_than": "Mayor que",
|
|
||||||
"operator_title_is_after": "Es después de",
|
|
||||||
"operator_title_is_before": "Es antes de",
|
|
||||||
"operator_title_is_between": "Está entre",
|
|
||||||
"operator_title_is_newer_than": "Es más reciente que",
|
|
||||||
"operator_title_is_not_set": "No está establecido",
|
|
||||||
"operator_title_is_older_than": "Es más antiguo que",
|
|
||||||
"operator_title_is_same_day": "Es el mismo día",
|
|
||||||
"operator_title_is_set": "Está establecido",
|
|
||||||
"operator_title_less_equal": "Menor o igual que",
|
|
||||||
"operator_title_less_than": "Menor que",
|
|
||||||
"operator_title_not_equals": "No es igual a",
|
|
||||||
"operator_title_starts_with": "Comienza con",
|
|
||||||
"operator_title_user_is_in": "El usuario está en",
|
|
||||||
"operator_title_user_is_not_in": "El usuario no está en",
|
|
||||||
"operator_user_is_in": "El usuario está en",
|
|
||||||
"operator_user_is_not_in": "El usuario no está en",
|
|
||||||
"person_and_attributes": "Persona y atributos",
|
"person_and_attributes": "Persona y atributos",
|
||||||
"phone": "Teléfono",
|
"phone": "Teléfono",
|
||||||
"please_remove_the_segment_from_these_surveys_in_order_to_delete_it": "Por favor, elimina el segmento de estas encuestas para poder borrarlo.",
|
"please_remove_the_segment_from_these_surveys_in_order_to_delete_it": "Por favor, elimina el segmento de estas encuestas para poder borrarlo.",
|
||||||
@@ -944,7 +867,6 @@
|
|||||||
"user_targeting_is_currently_only_available_when": "La segmentación de usuarios actualmente solo está disponible cuando",
|
"user_targeting_is_currently_only_available_when": "La segmentación de usuarios actualmente solo está disponible cuando",
|
||||||
"value_cannot_be_empty": "El valor no puede estar vacío.",
|
"value_cannot_be_empty": "El valor no puede estar vacío.",
|
||||||
"value_must_be_a_number": "El valor debe ser un número.",
|
"value_must_be_a_number": "El valor debe ser un número.",
|
||||||
"value_must_be_positive": "El valor debe ser un número positivo.",
|
|
||||||
"view_filters": "Ver filtros",
|
"view_filters": "Ver filtros",
|
||||||
"where": "Donde",
|
"where": "Donde",
|
||||||
"with_the_formbricks_sdk": "con el SDK de Formbricks"
|
"with_the_formbricks_sdk": "con el SDK de Formbricks"
|
||||||
@@ -1031,32 +953,19 @@
|
|||||||
"enterprise_features": "Características empresariales",
|
"enterprise_features": "Características empresariales",
|
||||||
"get_an_enterprise_license_to_get_access_to_all_features": "Obtén una licencia empresarial para acceder a todas las características.",
|
"get_an_enterprise_license_to_get_access_to_all_features": "Obtén una licencia empresarial para acceder a todas las características.",
|
||||||
"keep_full_control_over_your_data_privacy_and_security": "Mantén el control total sobre la privacidad y seguridad de tus datos.",
|
"keep_full_control_over_your_data_privacy_and_security": "Mantén el control total sobre la privacidad y seguridad de tus datos.",
|
||||||
"license_invalid_description": "La clave de licencia en tu variable de entorno ENTERPRISE_LICENSE_KEY no es válida. Por favor, comprueba si hay errores tipográficos o solicita una clave nueva.",
|
|
||||||
"license_status": "Estado de la licencia",
|
|
||||||
"license_status_active": "Activa",
|
|
||||||
"license_status_description": "Estado de tu licencia enterprise.",
|
|
||||||
"license_status_expired": "Caducada",
|
|
||||||
"license_status_invalid": "Licencia no válida",
|
|
||||||
"license_status_unreachable": "Inaccesible",
|
|
||||||
"license_unreachable_grace_period": "No se puede acceder al servidor de licencias. Tus funciones empresariales permanecen activas durante un período de gracia de 3 días que finaliza el {gracePeriodEnd}.",
|
|
||||||
"no_call_needed_no_strings_attached_request_a_free_30_day_trial_license_to_test_all_features_by_filling_out_this_form": "Sin necesidad de llamadas, sin compromisos: solicita una licencia de prueba gratuita de 30 días para probar todas las características rellenando este formulario:",
|
"no_call_needed_no_strings_attached_request_a_free_30_day_trial_license_to_test_all_features_by_filling_out_this_form": "Sin necesidad de llamadas, sin compromisos: solicita una licencia de prueba gratuita de 30 días para probar todas las características rellenando este formulario:",
|
||||||
"no_credit_card_no_sales_call_just_test_it": "Sin tarjeta de crédito. Sin llamada de ventas. Solo pruébalo :)",
|
"no_credit_card_no_sales_call_just_test_it": "Sin tarjeta de crédito. Sin llamada de ventas. Solo pruébalo :)",
|
||||||
"on_request": "Bajo petición",
|
"on_request": "Bajo petición",
|
||||||
"organization_roles": "Roles de organización (administrador, editor, desarrollador, etc.)",
|
"organization_roles": "Roles de organización (administrador, editor, desarrollador, etc.)",
|
||||||
"questions_please_reach_out_to": "¿Preguntas? Por favor, contacta con",
|
"questions_please_reach_out_to": "¿Preguntas? Por favor, contacta con",
|
||||||
"recheck_license": "Volver a comprobar licencia",
|
|
||||||
"recheck_license_failed": "Error al comprobar la licencia. Es posible que el servidor de licencias no esté disponible.",
|
|
||||||
"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_unreachable": "El servidor de licencias no está disponible. Inténtalo de nuevo más tarde.",
|
|
||||||
"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",
|
||||||
"soc2_hipaa_iso_27001_compliance_check": "Verificación de cumplimiento SOC2, HIPAA, ISO 27001",
|
"soc2_hipaa_iso_27001_compliance_check": "Verificación de cumplimiento SOC2, HIPAA, ISO 27001",
|
||||||
"sso": "SSO (Google, Microsoft, OpenID Connect)",
|
"sso": "SSO (Google, Microsoft, OpenID Connect)",
|
||||||
"teams": "Equipos y roles de acceso (lectura, lectura y escritura, gestión)",
|
"teams": "Equipos y roles de acceso (lectura, lectura y escritura, gestión)",
|
||||||
"unlock_the_full_power_of_formbricks_free_for_30_days": "Desbloquea todo el potencial de Formbricks. Gratis durante 30 días."
|
"unlock_the_full_power_of_formbricks_free_for_30_days": "Desbloquea todo el potencial de Formbricks. Gratis durante 30 días.",
|
||||||
|
"your_enterprise_license_is_active_all_features_unlocked": "Tu licencia empresarial está activa. Todas las características desbloqueadas."
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"bulk_invite_warning_description": "En el plan gratuito, a todos los miembros de la organización se les asigna siempre el rol de \"Propietario\".",
|
"bulk_invite_warning_description": "En el plan gratuito, a todos los miembros de la organización se les asigna siempre el rol de \"Propietario\".",
|
||||||
@@ -1080,7 +989,7 @@
|
|||||||
"from_your_organization": "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}",
|
"invited_on": "Invitado el {date}",
|
||||||
"invites_failed": "Las invitaciones fallaron",
|
"invites_failed": "Las invitaciones fallaron",
|
||||||
"leave_organization": "Abandonar organización",
|
"leave_organization": "Abandonar organización",
|
||||||
"leave_organization_description": "Abandonarás esta organización y perderás acceso a todas las encuestas y respuestas. Solo podrás volver a unirte si te invitan de nuevo.",
|
"leave_organization_description": "Abandonarás esta organización y perderás acceso a todas las encuestas y respuestas. Solo podrás volver a unirte si te invitan de nuevo.",
|
||||||
@@ -1193,6 +1102,8 @@
|
|||||||
"please_fill_all_workspace_fields": "Por favor, rellena todos los campos para añadir un proyecto nuevo.",
|
"please_fill_all_workspace_fields": "Por favor, rellena todos los campos para añadir un proyecto nuevo.",
|
||||||
"read": "Lectura",
|
"read": "Lectura",
|
||||||
"read_write": "Lectura y escritura",
|
"read_write": "Lectura y escritura",
|
||||||
|
"select_member": "Seleccionar miembro",
|
||||||
|
"select_workspace": "Seleccionar proyecto",
|
||||||
"team_admin": "Administrador de equipo",
|
"team_admin": "Administrador de equipo",
|
||||||
"team_created_successfully": "Equipo creado con éxito.",
|
"team_created_successfully": "Equipo creado con éxito.",
|
||||||
"team_deleted_successfully": "Equipo eliminado correctamente.",
|
"team_deleted_successfully": "Equipo eliminado correctamente.",
|
||||||
@@ -1242,6 +1153,7 @@
|
|||||||
"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": "Añadir un borde exterior a tu tarjeta de encuesta.",
|
||||||
"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",
|
||||||
@@ -1280,7 +1192,6 @@
|
|||||||
"block_duplicated": "Bloque duplicado.",
|
"block_duplicated": "Bloque duplicado.",
|
||||||
"bold": "Negrita",
|
"bold": "Negrita",
|
||||||
"brand_color": "Color de marca",
|
"brand_color": "Color de marca",
|
||||||
"brand_color_description": "Se aplica a botones, enlaces y resaltados.",
|
|
||||||
"brightness": "Brillo",
|
"brightness": "Brillo",
|
||||||
"bulk_edit": "Edición masiva",
|
"bulk_edit": "Edición masiva",
|
||||||
"bulk_edit_description": "Edita todas las opciones a continuación, una por línea. Las líneas vacías se omitirán y los duplicados se eliminarán.",
|
"bulk_edit_description": "Edita todas las opciones a continuación, una por línea. Las líneas vacías se omitirán y los duplicados se eliminarán.",
|
||||||
@@ -1298,9 +1209,7 @@
|
|||||||
"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",
|
||||||
"card_background_color_description": "Rellena el área de la tarjeta de encuesta.",
|
|
||||||
"card_border_color": "Color del borde de la tarjeta",
|
"card_border_color": "Color del borde de la tarjeta",
|
||||||
"card_border_color_description": "Delinea la tarjeta de encuesta.",
|
|
||||||
"card_styling": "Estilo de la tarjeta",
|
"card_styling": "Estilo de la tarjeta",
|
||||||
"casual": "Informal",
|
"casual": "Informal",
|
||||||
"caution_edit_duplicate": "Duplicar y editar",
|
"caution_edit_duplicate": "Duplicar y editar",
|
||||||
@@ -1311,12 +1220,20 @@
|
|||||||
"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.",
|
||||||
"caution_recommendation": "Esto puede causar inconsistencias de datos en el resumen de la encuesta. Recomendamos duplicar la encuesta en su lugar.",
|
"caution_recommendation": "Esto puede causar inconsistencias de datos en el resumen de la encuesta. Recomendamos duplicar la encuesta en su lugar.",
|
||||||
"caution_text": "Los cambios provocarán inconsistencias",
|
"caution_text": "Los cambios provocarán inconsistencias",
|
||||||
|
"centered_modal_overlay_color": "Color de superposición del modal centrado",
|
||||||
"change_anyway": "Cambiar de todos modos",
|
"change_anyway": "Cambiar de todos modos",
|
||||||
"change_background": "Cambiar fondo",
|
"change_background": "Cambiar fondo",
|
||||||
"change_question_type": "Cambiar tipo de pregunta",
|
"change_question_type": "Cambiar tipo de pregunta",
|
||||||
"change_survey_type": "Cambiar el tipo de encuesta afecta al acceso existente",
|
"change_survey_type": "Cambiar el tipo de encuesta afecta al acceso existente",
|
||||||
|
"change_the_background_color_of_the_card": "Cambiar el color de fondo de la tarjeta.",
|
||||||
|
"change_the_background_color_of_the_input_fields": "Cambiar el color de fondo de los campos de entrada.",
|
||||||
"change_the_background_to_a_color_image_or_animation": "Cambiar el fondo a un color, imagen o animación.",
|
"change_the_background_to_a_color_image_or_animation": "Cambiar el fondo a un color, imagen o animación.",
|
||||||
|
"change_the_border_color_of_the_card": "Cambiar el color del borde de la tarjeta.",
|
||||||
|
"change_the_border_color_of_the_input_fields": "Cambiar el color del borde de los campos de entrada.",
|
||||||
|
"change_the_border_radius_of_the_card_and_the_inputs": "Cambiar el radio del borde de la tarjeta y las entradas.",
|
||||||
|
"change_the_brand_color_of_the_survey": "Cambiar el color de marca de la encuesta.",
|
||||||
"change_the_placement_of_this_survey": "Cambiar la ubicación de esta encuesta.",
|
"change_the_placement_of_this_survey": "Cambiar la ubicación de esta encuesta.",
|
||||||
|
"change_the_question_color_of_the_survey": "Cambiar el color de las preguntas de la encuesta.",
|
||||||
"changes_saved": "Cambios guardados.",
|
"changes_saved": "Cambios guardados.",
|
||||||
"changing_survey_type_will_remove_existing_distribution_channels": "Cambiar el tipo de encuesta afectará a cómo se puede compartir. Si los encuestados ya tienen enlaces de acceso para el tipo actual, podrían perder el acceso después del cambio.",
|
"changing_survey_type_will_remove_existing_distribution_channels": "Cambiar el tipo de encuesta afectará a cómo se puede compartir. Si los encuestados ya tienen enlaces de acceso para el tipo actual, podrían perder el acceso después del cambio.",
|
||||||
"checkbox_label": "Etiqueta de casilla de verificación",
|
"checkbox_label": "Etiqueta de casilla de verificación",
|
||||||
@@ -1456,6 +1373,7 @@
|
|||||||
"hide_progress_bar": "Ocultar barra de progreso",
|
"hide_progress_bar": "Ocultar barra de progreso",
|
||||||
"hide_question_settings": "Ocultar ajustes de la pregunta",
|
"hide_question_settings": "Ocultar ajustes de la pregunta",
|
||||||
"hostname": "Nombre de host",
|
"hostname": "Nombre de host",
|
||||||
|
"how_funky_do_you_want_your_cards_in_survey_type_derived_surveys": "¿Cuánto estilo quieres darle a tus tarjetas en las encuestas de tipo {surveyTypeDerived}?",
|
||||||
"if_you_need_more_please": "Si necesitas más, por favor",
|
"if_you_need_more_please": "Si necesitas más, por favor",
|
||||||
"if_you_really_want_that_answer_ask_until_you_get_it": "Seguir mostrando cuando se active hasta que se envíe una respuesta.",
|
"if_you_really_want_that_answer_ask_until_you_get_it": "Seguir mostrando cuando se active hasta que se envíe una respuesta.",
|
||||||
"ignore_global_waiting_time": "Ignorar periodo de espera",
|
"ignore_global_waiting_time": "Ignorar periodo de espera",
|
||||||
@@ -1466,9 +1384,7 @@
|
|||||||
"initial_value": "Valor inicial",
|
"initial_value": "Valor inicial",
|
||||||
"inner_text": "Texto interior",
|
"inner_text": "Texto interior",
|
||||||
"input_border_color": "Color del borde de entrada",
|
"input_border_color": "Color del borde de entrada",
|
||||||
"input_border_color_description": "Delinea los campos de texto y áreas de texto.",
|
|
||||||
"input_color": "Color de entrada",
|
"input_color": "Color de entrada",
|
||||||
"input_color_description": "Rellena el interior de los campos de texto.",
|
|
||||||
"insert_link": "Insertar enlace",
|
"insert_link": "Insertar enlace",
|
||||||
"invalid_targeting": "Segmentación no válida: por favor, comprueba tus filtros de audiencia",
|
"invalid_targeting": "Segmentación no válida: por favor, comprueba tus filtros de audiencia",
|
||||||
"invalid_video_url_warning": "Por favor, introduce una URL válida de YouTube, Vimeo o Loom. Actualmente no admitimos otros proveedores de alojamiento de vídeos.",
|
"invalid_video_url_warning": "Por favor, introduce una URL válida de YouTube, Vimeo o Loom. Actualmente no admitimos otros proveedores de alojamiento de vídeos.",
|
||||||
@@ -1552,12 +1468,13 @@
|
|||||||
"protect_survey_with_pin_description": "Solo los usuarios que tengan el PIN pueden acceder a la encuesta.",
|
"protect_survey_with_pin_description": "Solo los usuarios que tengan el PIN pueden acceder a la encuesta.",
|
||||||
"publish": "Publicar",
|
"publish": "Publicar",
|
||||||
"question": "Pregunta",
|
"question": "Pregunta",
|
||||||
|
"question_color": "Color de la pregunta",
|
||||||
"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_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}\"",
|
||||||
"question_used_in_recall": "Esta pregunta se está recordando en la pregunta {questionIndex}.",
|
"question_used_in_recall": "Esta pregunta se está recordando en la pregunta {questionIndex}.",
|
||||||
"question_used_in_recall_ending_card": "Esta pregunta se está recordando en la Tarjeta Final",
|
"question_used_in_recall_ending_card": "Esta pregunta se está recordando en la Tarjeta Final",
|
||||||
"quotas": {
|
"quotas": {
|
||||||
@@ -1613,7 +1530,6 @@
|
|||||||
"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 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",
|
||||||
@@ -1655,6 +1571,7 @@
|
|||||||
"styling_set_to_theme_styles": "Estilo configurado según los estilos del tema",
|
"styling_set_to_theme_styles": "Estilo configurado según los estilos del tema",
|
||||||
"subheading": "Subtítulo",
|
"subheading": "Subtítulo",
|
||||||
"subtract": "Restar -",
|
"subtract": "Restar -",
|
||||||
|
"suggest_colors": "Sugerir colores",
|
||||||
"survey_completed_heading": "Encuesta completada",
|
"survey_completed_heading": "Encuesta completada",
|
||||||
"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",
|
||||||
@@ -1731,7 +1648,7 @@
|
|||||||
"validation_rules": "Reglas de validación",
|
"validation_rules": "Reglas de validación",
|
||||||
"validation_rules_description": "Solo aceptar respuestas que cumplan los siguientes criterios",
|
"validation_rules_description": "Solo aceptar respuestas que cumplan los siguientes criterios",
|
||||||
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} se usa en la lógica de la pregunta {questionIndex}. Por favor, elimínala primero de la lógica.",
|
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} se usa en la lógica de la pregunta {questionIndex}. Por favor, elimínala primero de la lógica.",
|
||||||
"variable_is_used_in_quota_please_remove_it_from_quota_first": "La variable “{variableName}” se está utilizando en la cuota “{quotaName}”",
|
"variable_is_used_in_quota_please_remove_it_from_quota_first": "La variable \"{variableName}\" se está utilizando en la cuota \"{quotaName}\"",
|
||||||
"variable_name_conflicts_with_hidden_field": "El nombre de la variable entra en conflicto con un ID de campo oculto existente.",
|
"variable_name_conflicts_with_hidden_field": "El nombre de la variable entra en conflicto con un ID de campo oculto existente.",
|
||||||
"variable_name_is_already_taken_please_choose_another": "El nombre de la variable ya está en uso, por favor elige otro.",
|
"variable_name_is_already_taken_please_choose_another": "El nombre de la variable ya está en uso, por favor elige otro.",
|
||||||
"variable_name_must_start_with_a_letter": "El nombre de la variable debe comenzar con una letra.",
|
"variable_name_must_start_with_a_letter": "El nombre de la variable debe comenzar con una letra.",
|
||||||
@@ -1957,7 +1874,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",
|
||||||
@@ -2000,7 +1916,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",
|
||||||
@@ -2140,71 +2055,9 @@
|
|||||||
"look": {
|
"look": {
|
||||||
"add_background_color": "Añadir color de fondo",
|
"add_background_color": "Añadir color de fondo",
|
||||||
"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_button_bg": "Fondo del botón",
|
|
||||||
"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_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_height_description": "Controla la altura del botón.",
|
|
||||||
"advanced_styling_field_button_padding_x_description": "Añade espacio a la izquierda y a la derecha.",
|
|
||||||
"advanced_styling_field_button_padding_y_description": "Añade espacio arriba y abajo.",
|
|
||||||
"advanced_styling_field_button_text": "Texto del botón",
|
|
||||||
"advanced_styling_field_button_text_description": "Colorea la etiqueta dentro de los botones.",
|
|
||||||
"advanced_styling_field_description_color": "Color de la descripción",
|
|
||||||
"advanced_styling_field_description_color_description": "Colorea el texto debajo de cada titular.",
|
|
||||||
"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_weight": "Grosor de fuente de la descripción",
|
|
||||||
"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_weight": "Grosor de fuente",
|
|
||||||
"advanced_styling_field_headline_color": "Color del titular",
|
|
||||||
"advanced_styling_field_headline_color_description": "Colorea el texto principal de la pregunta.",
|
|
||||||
"advanced_styling_field_headline_size": "Tamaño de fuente del titular",
|
|
||||||
"advanced_styling_field_headline_size_description": "Escala el texto del titular.",
|
|
||||||
"advanced_styling_field_headline_weight": "Grosor de fuente del titular",
|
|
||||||
"advanced_styling_field_headline_weight_description": "Hace el texto del titular más ligero o más grueso.",
|
|
||||||
"advanced_styling_field_height": "Altura mínima",
|
|
||||||
"advanced_styling_field_indicator_bg": "Fondo del indicador",
|
|
||||||
"advanced_styling_field_indicator_bg_description": "Colorea la porción rellena de la barra.",
|
|
||||||
"advanced_styling_field_input_border_radius_description": "Redondea las esquinas del campo.",
|
|
||||||
"advanced_styling_field_input_font_size_description": "Escala el texto escrito en los campos.",
|
|
||||||
"advanced_styling_field_input_height_description": "Controla la altura mínima del campo de entrada.",
|
|
||||||
"advanced_styling_field_input_padding_x_description": "Añade espacio a la izquierda y a la derecha.",
|
|
||||||
"advanced_styling_field_input_padding_y_description": "Añade espacio en la parte superior e inferior.",
|
|
||||||
"advanced_styling_field_input_placeholder_opacity_description": "Atenúa el texto de sugerencia del marcador de posición.",
|
|
||||||
"advanced_styling_field_input_shadow_description": "Añade una sombra alrededor de los campos de entrada.",
|
|
||||||
"advanced_styling_field_input_text": "Texto 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_description": "Rellena los elementos de opción.",
|
|
||||||
"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_label": "Color de la etiqueta",
|
|
||||||
"advanced_styling_field_option_label_description": "Colorea el texto de la etiqueta de opción.",
|
|
||||||
"advanced_styling_field_option_padding_x_description": "Añade espacio a la izquierda y a la derecha.",
|
|
||||||
"advanced_styling_field_option_padding_y_description": "Añade espacio en la parte superior e inferior.",
|
|
||||||
"advanced_styling_field_padding_x": "Relleno X",
|
|
||||||
"advanced_styling_field_padding_y": "Relleno Y",
|
|
||||||
"advanced_styling_field_placeholder_opacity": "Opacidad del marcador de posición",
|
|
||||||
"advanced_styling_field_shadow": "Sombra",
|
|
||||||
"advanced_styling_field_track_bg": "Fondo de la pista",
|
|
||||||
"advanced_styling_field_track_bg_description": "Colorea la parte no rellenada de la barra.",
|
|
||||||
"advanced_styling_field_track_height": "Altura de la pista",
|
|
||||||
"advanced_styling_field_track_height_description": "Controla el grosor de la barra de progreso.",
|
|
||||||
"advanced_styling_field_upper_label_color": "Color de la etiqueta del titular",
|
|
||||||
"advanced_styling_field_upper_label_color_description": "Colorea la etiqueta pequeña sobre los campos de entrada.",
|
|
||||||
"advanced_styling_field_upper_label_size": "Tamaño de fuente de la etiqueta del titular",
|
|
||||||
"advanced_styling_field_upper_label_size_description": "Escala la etiqueta pequeña sobre los campos de entrada.",
|
|
||||||
"advanced_styling_field_upper_label_weight": "Grosor de fuente de la etiqueta del titular",
|
|
||||||
"advanced_styling_field_upper_label_weight_description": "Hace que la etiqueta sea más ligera o más gruesa.",
|
|
||||||
"advanced_styling_section_buttons": "Botones",
|
|
||||||
"advanced_styling_section_headlines": "Títulos y descripciones",
|
|
||||||
"advanced_styling_section_inputs": "Campos de entrada",
|
|
||||||
"advanced_styling_section_options": "Opciones (radio/casilla de verificación)",
|
|
||||||
"app_survey_placement": "Ubicación de encuesta de aplicación",
|
"app_survey_placement": "Ubicación de encuesta de aplicación",
|
||||||
"app_survey_placement_settings_description": "Cambia dónde se mostrarán las encuestas en tu aplicación web o sitio web.",
|
"app_survey_placement_settings_description": "Cambia dónde se mostrarán las encuestas en tu aplicación web o sitio web.",
|
||||||
|
"centered_modal_overlay_color": "Color de superposición del modal centrado",
|
||||||
"email_customization": "Personalización de correo electrónico",
|
"email_customization": "Personalización de correo electrónico",
|
||||||
"email_customization_description": "Cambia el aspecto de los correos electrónicos que Formbricks envía en tu nombre.",
|
"email_customization_description": "Cambia el aspecto de los correos electrónicos que Formbricks envía en tu nombre.",
|
||||||
"enable_custom_styling": "Habilitar estilo personalizado",
|
"enable_custom_styling": "Habilitar estilo personalizado",
|
||||||
@@ -2215,9 +2068,6 @@
|
|||||||
"formbricks_branding_hidden": "La marca de Formbricks está oculta.",
|
"formbricks_branding_hidden": "La marca de Formbricks está oculta.",
|
||||||
"formbricks_branding_settings_description": "Nos encanta tu apoyo, pero lo entendemos si lo desactivas.",
|
"formbricks_branding_settings_description": "Nos encanta tu apoyo, pero lo entendemos si lo desactivas.",
|
||||||
"formbricks_branding_shown": "La marca de Formbricks se muestra.",
|
"formbricks_branding_shown": "La marca de Formbricks se muestra.",
|
||||||
"generate_theme_btn": "Generar",
|
|
||||||
"generate_theme_confirmation": "¿Te gustaría generar un tema de colores que combine con el color de tu marca? Esto sobrescribirá tu configuración de colores actual.",
|
|
||||||
"generate_theme_header": "¿Generar tema de colores?",
|
|
||||||
"logo_removed_successfully": "Logotipo eliminado correctamente",
|
"logo_removed_successfully": "Logotipo eliminado correctamente",
|
||||||
"logo_settings_description": "Sube el logotipo de tu empresa para personalizar las encuestas y las vistas previas de enlaces.",
|
"logo_settings_description": "Sube el logotipo de tu empresa para personalizar las encuestas y las vistas previas de enlaces.",
|
||||||
"logo_updated_successfully": "Logotipo actualizado correctamente",
|
"logo_updated_successfully": "Logotipo actualizado correctamente",
|
||||||
@@ -2232,8 +2082,6 @@
|
|||||||
"show_formbricks_branding_in": "Mostrar marca de Formbricks en encuestas de {type}",
|
"show_formbricks_branding_in": "Mostrar marca de Formbricks en encuestas de {type}",
|
||||||
"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",
|
|
||||||
"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."
|
||||||
},
|
},
|
||||||
@@ -2997,7 +2845,6 @@
|
|||||||
"preview_survey_question_2_choice_1_label": "Sí, mantenme informado.",
|
"preview_survey_question_2_choice_1_label": "Sí, mantenme informado.",
|
||||||
"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_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",
|
||||||
|
|||||||
+24
-177
@@ -188,7 +188,6 @@
|
|||||||
"customer_success": "Succès Client",
|
"customer_success": "Succès Client",
|
||||||
"dark_overlay": "Foncée",
|
"dark_overlay": "Foncée",
|
||||||
"date": "Date",
|
"date": "Date",
|
||||||
"days": "jours",
|
|
||||||
"default": "Par défaut",
|
"default": "Par défaut",
|
||||||
"delete": "Supprimer",
|
"delete": "Supprimer",
|
||||||
"description": "Description",
|
"description": "Description",
|
||||||
@@ -218,7 +217,6 @@
|
|||||||
"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",
|
||||||
@@ -277,7 +275,6 @@
|
|||||||
"mobile_overlay_app_works_best_on_desktop": "Formbricks fonctionne mieux sur un écran plus grand. Pour gérer ou créer des sondages, passez à un autre appareil.",
|
"mobile_overlay_app_works_best_on_desktop": "Formbricks fonctionne mieux sur un écran plus grand. Pour gérer ou créer des sondages, passez à un autre appareil.",
|
||||||
"mobile_overlay_surveys_look_good": "Ne t'inquiète pas – tes enquêtes sont superbes sur tous les appareils et tailles d'écran!",
|
"mobile_overlay_surveys_look_good": "Ne t'inquiète pas – tes enquêtes sont superbes sur tous les appareils et tailles d'écran!",
|
||||||
"mobile_overlay_title": "Oups, écran minuscule détecté!",
|
"mobile_overlay_title": "Oups, écran minuscule détecté!",
|
||||||
"months": "mois",
|
|
||||||
"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",
|
||||||
@@ -288,7 +285,6 @@
|
|||||||
"no_background_image_found": "Aucune image de fond trouvée.",
|
"no_background_image_found": "Aucune image de fond trouvée.",
|
||||||
"no_code": "Sans code",
|
"no_code": "Sans code",
|
||||||
"no_files_uploaded": "Aucun fichier n'a été téléchargé.",
|
"no_files_uploaded": "Aucun fichier n'a été téléchargé.",
|
||||||
"no_overlay": "Aucune superposition",
|
|
||||||
"no_quotas_found": "Aucun quota trouvé",
|
"no_quotas_found": "Aucun quota trouvé",
|
||||||
"no_result_found": "Aucun résultat trouvé",
|
"no_result_found": "Aucun résultat trouvé",
|
||||||
"no_results": "Aucun résultat",
|
"no_results": "Aucun résultat",
|
||||||
@@ -315,7 +311,6 @@
|
|||||||
"organization_teams_not_found": "Équipes d'organisation non trouvées",
|
"organization_teams_not_found": "Équipes d'organisation non trouvées",
|
||||||
"other": "Autre",
|
"other": "Autre",
|
||||||
"others": "Autres",
|
"others": "Autres",
|
||||||
"overlay_color": "Couleur de superposition",
|
|
||||||
"overview": "Aperçu",
|
"overview": "Aperçu",
|
||||||
"password": "Mot de passe",
|
"password": "Mot de passe",
|
||||||
"paused": "En pause",
|
"paused": "En pause",
|
||||||
@@ -355,7 +350,6 @@
|
|||||||
"request_trial_license": "Demander une licence d'essai",
|
"request_trial_license": "Demander une licence d'essai",
|
||||||
"reset_to_default": "Réinitialiser par défaut",
|
"reset_to_default": "Réinitialiser par défaut",
|
||||||
"response": "Réponse",
|
"response": "Réponse",
|
||||||
"response_id": "ID de réponse",
|
|
||||||
"responses": "Réponses",
|
"responses": "Réponses",
|
||||||
"restart": "Recommencer",
|
"restart": "Recommencer",
|
||||||
"role": "Rôle",
|
"role": "Rôle",
|
||||||
@@ -396,7 +390,6 @@
|
|||||||
"status": "Statut",
|
"status": "Statut",
|
||||||
"step_by_step_manual": "Manuel étape par étape",
|
"step_by_step_manual": "Manuel étape par étape",
|
||||||
"storage_not_configured": "Stockage de fichiers non configuré, les téléchargements risquent d'échouer",
|
"storage_not_configured": "Stockage de fichiers non configuré, les téléchargements risquent d'échouer",
|
||||||
"string": "Texte",
|
|
||||||
"styling": "Style",
|
"styling": "Style",
|
||||||
"submit": "Soumettre",
|
"submit": "Soumettre",
|
||||||
"summary": "Résumé",
|
"summary": "Résumé",
|
||||||
@@ -429,7 +422,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",
|
||||||
@@ -453,7 +445,6 @@
|
|||||||
"website_and_app_connection": "Connexion de sites Web et d'applications",
|
"website_and_app_connection": "Connexion de sites Web et d'applications",
|
||||||
"website_app_survey": "Sondage sur le site Web et l'application",
|
"website_app_survey": "Sondage sur le site Web et l'application",
|
||||||
"website_survey": "Sondage de site web",
|
"website_survey": "Sondage de site web",
|
||||||
"weeks": "semaines",
|
|
||||||
"welcome_card": "Carte de bienvenue",
|
"welcome_card": "Carte de bienvenue",
|
||||||
"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",
|
||||||
@@ -464,7 +455,6 @@
|
|||||||
"workspace_not_found": "Projet introuvable",
|
"workspace_not_found": "Projet introuvable",
|
||||||
"workspace_permission_not_found": "Permission du projet introuvable",
|
"workspace_permission_not_found": "Permission du projet introuvable",
|
||||||
"workspaces": "Projets",
|
"workspaces": "Projets",
|
||||||
"years": "années",
|
|
||||||
"you": "Vous",
|
"you": "Vous",
|
||||||
"you_are_downgraded_to_the_community_edition": "Vous êtes rétrogradé à l'édition communautaire.",
|
"you_are_downgraded_to_the_community_edition": "Vous êtes rétrogradé à l'édition communautaire.",
|
||||||
"you_are_not_authorized_to_perform_this_action": "Vous n'êtes pas autorisé à effectuer cette action.",
|
"you_are_not_authorized_to_perform_this_action": "Vous n'êtes pas autorisé à effectuer cette action.",
|
||||||
@@ -625,7 +615,7 @@
|
|||||||
"attribute_created_successfully": "Attribut créé avec succès",
|
"attribute_created_successfully": "Attribut créé avec succès",
|
||||||
"attribute_description": "Description",
|
"attribute_description": "Description",
|
||||||
"attribute_description_placeholder": "Brève description",
|
"attribute_description_placeholder": "Brève description",
|
||||||
"attribute_key": "Clé",
|
"attribute_key": "Attribut",
|
||||||
"attribute_key_cannot_be_changed": "La clé ne peut pas être modifiée après la création",
|
"attribute_key_cannot_be_changed": "La clé ne peut pas être modifiée après la création",
|
||||||
"attribute_key_hint": "Uniquement des lettres minuscules, des chiffres et des underscores. Doit commencer par une lettre.",
|
"attribute_key_hint": "Uniquement des lettres minuscules, des chiffres et des underscores. Doit commencer par une lettre.",
|
||||||
"attribute_key_placeholder": "ex. date_de_naissance",
|
"attribute_key_placeholder": "ex. date_de_naissance",
|
||||||
@@ -636,45 +626,28 @@
|
|||||||
"attribute_updated_successfully": "Attribut mis à jour avec succès",
|
"attribute_updated_successfully": "Attribut mis à jour avec succès",
|
||||||
"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_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_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_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",
|
|
||||||
"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_description": "Choisis comment cet attribut doit être stocké et filtré",
|
|
||||||
"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",
|
||||||
"edit_attribute_values_description": "Modifiez les valeurs d'attributs spécifiques pour ce contact.",
|
"edit_attribute_values_description": "Modifiez les valeurs d'attributs spécifiques pour ce contact.",
|
||||||
"edit_attributes": "Modifier les attributs",
|
|
||||||
"edit_attributes_success": "Attributs du contact mis à jour avec succès",
|
"edit_attributes_success": "Attributs du contact mis à jour avec succès",
|
||||||
"generate_personal_link": "Générer un lien personnel",
|
"generate_personal_link": "Générer un lien personnel",
|
||||||
"generate_personal_link_description": "Sélectionnez une enquête publiée pour générer un lien personnalisé pour ce contact.",
|
"generate_personal_link_description": "Sélectionnez une enquête publiée pour générer un lien personnalisé pour ce contact.",
|
||||||
"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_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",
|
||||||
"not_provided": "Non fourni",
|
"not_provided": "Non fourni",
|
||||||
"number_value_required": "La valeur numérique est requise. Utilisez le bouton supprimer pour retirer cet attribut.",
|
|
||||||
"personal_link_generated": "Lien personnel généré avec succès",
|
"personal_link_generated": "Lien personnel généré avec succès",
|
||||||
"personal_link_generated_but_clipboard_failed": "Lien personnel généré mais échec de la copie dans le presse-papiers : {url}",
|
"personal_link_generated_but_clipboard_failed": "Lien personnel généré mais échec de la copie dans le presse-papiers : {url}",
|
||||||
"personal_survey_link": "Lien vers le sondage personnel",
|
"personal_survey_link": "Lien vers le sondage personnel",
|
||||||
@@ -683,24 +656,13 @@
|
|||||||
"search_contact": "Rechercher un contact",
|
"search_contact": "Rechercher un contact",
|
||||||
"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",
|
|
||||||
"survey_viewed": "Enquête consultée",
|
|
||||||
"survey_viewed_at": "Consultée le",
|
|
||||||
"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_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_generic": "Une erreur s'est produite lors de l'importation des contacts. Veuillez réessayer plus tard.",
|
|
||||||
"upload_contacts_error_invalid_file_type": "Veuillez importer un fichier CSV",
|
|
||||||
"upload_contacts_error_no_valid_contacts": "Le fichier CSV importé ne contient aucun contact valide, veuillez consulter l'exemple de fichier CSV pour le format correct.",
|
|
||||||
"upload_contacts_modal_attribute_header": "Attribut Formbricks",
|
|
||||||
"upload_contacts_modal_attributes_description": "Mappez les colonnes de votre CSV aux attributs dans Formbricks.",
|
"upload_contacts_modal_attributes_description": "Mappez les colonnes de votre CSV aux attributs dans Formbricks.",
|
||||||
"upload_contacts_modal_attributes_new": "Nouvel attribut",
|
"upload_contacts_modal_attributes_new": "Nouvel attribut",
|
||||||
"upload_contacts_modal_attributes_search_or_add": "Rechercher ou ajouter un attribut",
|
"upload_contacts_modal_attributes_search_or_add": "Rechercher ou ajouter un attribut",
|
||||||
|
"upload_contacts_modal_attributes_should_be_mapped_to": "devrait être mappé à",
|
||||||
"upload_contacts_modal_attributes_title": "Attributs",
|
"upload_contacts_modal_attributes_title": "Attributs",
|
||||||
"upload_contacts_modal_csv_column_header": "Colonne CSV",
|
|
||||||
"upload_contacts_modal_description": "Téléchargez un fichier CSV pour importer rapidement des contacts avec des attributs.",
|
"upload_contacts_modal_description": "Téléchargez un fichier CSV pour importer rapidement des contacts avec des attributs.",
|
||||||
"upload_contacts_modal_download_example_csv": "Télécharger un exemple de CSV",
|
"upload_contacts_modal_download_example_csv": "Télécharger un exemple de CSV",
|
||||||
"upload_contacts_modal_duplicates_description": "Que faire si un contact existe déjà ?",
|
"upload_contacts_modal_duplicates_description": "Que faire si un contact existe déjà ?",
|
||||||
@@ -757,12 +719,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 la permission 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",
|
||||||
@@ -886,40 +843,6 @@
|
|||||||
"no_attributes_yet": "Aucun attribut pour le moment !",
|
"no_attributes_yet": "Aucun attribut pour le moment !",
|
||||||
"no_filters_yet": "Il n'y a pas encore de filtres !",
|
"no_filters_yet": "Il n'y a pas encore de filtres !",
|
||||||
"no_segments_yet": "Aucun segment n'est actuellement enregistré.",
|
"no_segments_yet": "Aucun segment n'est actuellement enregistré.",
|
||||||
"operator_contains": "contient",
|
|
||||||
"operator_does_not_contain": "ne contient pas",
|
|
||||||
"operator_ends_with": "se termine par",
|
|
||||||
"operator_is_after": "est après",
|
|
||||||
"operator_is_before": "est avant",
|
|
||||||
"operator_is_between": "est entre",
|
|
||||||
"operator_is_newer_than": "est plus récent que",
|
|
||||||
"operator_is_not_set": "n'est pas défini",
|
|
||||||
"operator_is_older_than": "est plus ancien que",
|
|
||||||
"operator_is_same_day": "est le même jour",
|
|
||||||
"operator_is_set": "est défini",
|
|
||||||
"operator_starts_with": "commence par",
|
|
||||||
"operator_title_contains": "Contient",
|
|
||||||
"operator_title_does_not_contain": "Ne contient pas",
|
|
||||||
"operator_title_ends_with": "Se termine par",
|
|
||||||
"operator_title_equals": "Égal",
|
|
||||||
"operator_title_greater_equal": "Supérieur ou égal à",
|
|
||||||
"operator_title_greater_than": "Supérieur à",
|
|
||||||
"operator_title_is_after": "Est après",
|
|
||||||
"operator_title_is_before": "Est avant",
|
|
||||||
"operator_title_is_between": "Est entre",
|
|
||||||
"operator_title_is_newer_than": "Est plus récent que",
|
|
||||||
"operator_title_is_not_set": "N'est pas défini",
|
|
||||||
"operator_title_is_older_than": "Est plus ancien que",
|
|
||||||
"operator_title_is_same_day": "Est le même jour",
|
|
||||||
"operator_title_is_set": "Est défini",
|
|
||||||
"operator_title_less_equal": "Inférieur ou égal à",
|
|
||||||
"operator_title_less_than": "Inférieur à",
|
|
||||||
"operator_title_not_equals": "N'est pas égal à",
|
|
||||||
"operator_title_starts_with": "Commence par",
|
|
||||||
"operator_title_user_is_in": "L'utilisateur est dans",
|
|
||||||
"operator_title_user_is_not_in": "L'utilisateur n'est pas dans",
|
|
||||||
"operator_user_is_in": "L'utilisateur est dans",
|
|
||||||
"operator_user_is_not_in": "L'utilisateur n'est pas dans",
|
|
||||||
"person_and_attributes": "Personne et attributs",
|
"person_and_attributes": "Personne et attributs",
|
||||||
"phone": "Téléphone",
|
"phone": "Téléphone",
|
||||||
"please_remove_the_segment_from_these_surveys_in_order_to_delete_it": "Veuillez supprimer le segment de ces enquêtes afin de le supprimer.",
|
"please_remove_the_segment_from_these_surveys_in_order_to_delete_it": "Veuillez supprimer le segment de ces enquêtes afin de le supprimer.",
|
||||||
@@ -944,7 +867,6 @@
|
|||||||
"user_targeting_is_currently_only_available_when": "La ciblage des utilisateurs est actuellement disponible uniquement lorsque",
|
"user_targeting_is_currently_only_available_when": "La ciblage des utilisateurs est actuellement disponible uniquement lorsque",
|
||||||
"value_cannot_be_empty": "La valeur ne peut pas être vide.",
|
"value_cannot_be_empty": "La valeur ne peut pas être vide.",
|
||||||
"value_must_be_a_number": "La valeur doit être un nombre.",
|
"value_must_be_a_number": "La valeur doit être un nombre.",
|
||||||
"value_must_be_positive": "La valeur doit être un nombre positif.",
|
|
||||||
"view_filters": "Filtres de vue",
|
"view_filters": "Filtres de vue",
|
||||||
"where": "Où",
|
"where": "Où",
|
||||||
"with_the_formbricks_sdk": "avec le SDK Formbricks"
|
"with_the_formbricks_sdk": "avec le SDK Formbricks"
|
||||||
@@ -1031,32 +953,19 @@
|
|||||||
"enterprise_features": "Fonctionnalités d'entreprise",
|
"enterprise_features": "Fonctionnalités d'entreprise",
|
||||||
"get_an_enterprise_license_to_get_access_to_all_features": "Obtenez une licence Entreprise pour accéder à toutes les fonctionnalités.",
|
"get_an_enterprise_license_to_get_access_to_all_features": "Obtenez une licence Entreprise pour accéder à toutes les fonctionnalités.",
|
||||||
"keep_full_control_over_your_data_privacy_and_security": "Gardez un contrôle total sur la confidentialité et la sécurité de vos données.",
|
"keep_full_control_over_your_data_privacy_and_security": "Gardez un contrôle total sur la confidentialité et la sécurité de vos données.",
|
||||||
"license_invalid_description": "La clé de licence dans votre variable d'environnement ENTERPRISE_LICENSE_KEY n'est pas valide. Veuillez vérifier les fautes de frappe ou demander une nouvelle clé.",
|
|
||||||
"license_status": "Statut de la licence",
|
|
||||||
"license_status_active": "Active",
|
|
||||||
"license_status_description": "Statut de votre licence entreprise.",
|
|
||||||
"license_status_expired": "Expirée",
|
|
||||||
"license_status_invalid": "Licence invalide",
|
|
||||||
"license_status_unreachable": "Inaccessible",
|
|
||||||
"license_unreachable_grace_period": "Le serveur de licence est injoignable. Vos fonctionnalités entreprise restent actives pendant une période de grâce de 3 jours se terminant le {gracePeriodEnd}.",
|
|
||||||
"no_call_needed_no_strings_attached_request_a_free_30_day_trial_license_to_test_all_features_by_filling_out_this_form": "Aucun appel nécessaire, aucune obligation : Demandez une licence d'essai gratuite de 30 jours pour tester toutes les fonctionnalités en remplissant ce formulaire :",
|
"no_call_needed_no_strings_attached_request_a_free_30_day_trial_license_to_test_all_features_by_filling_out_this_form": "Aucun appel nécessaire, aucune obligation : Demandez une licence d'essai gratuite de 30 jours pour tester toutes les fonctionnalités en remplissant ce formulaire :",
|
||||||
"no_credit_card_no_sales_call_just_test_it": "Aucune carte de crédit. Aucun appel de vente. Testez-le simplement :)",
|
"no_credit_card_no_sales_call_just_test_it": "Aucune carte de crédit. Aucun appel de vente. Testez-le simplement :)",
|
||||||
"on_request": "Sur demande",
|
"on_request": "Sur demande",
|
||||||
"organization_roles": "Rôles d'organisation (Administrateur, Éditeur, Développeur, etc.)",
|
"organization_roles": "Rôles d'organisation (Administrateur, Éditeur, Développeur, etc.)",
|
||||||
"questions_please_reach_out_to": "Des questions ? Veuillez contacter",
|
"questions_please_reach_out_to": "Des questions ? Veuillez contacter",
|
||||||
"recheck_license": "Revérifier la licence",
|
|
||||||
"recheck_license_failed": "La vérification de la licence a échoué. Le serveur de licences est peut-être inaccessible.",
|
|
||||||
"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_unreachable": "Le serveur de licences est inaccessible. Veuillez réessayer plus tard.",
|
|
||||||
"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",
|
||||||
"soc2_hipaa_iso_27001_compliance_check": "Vérification de conformité SOC2, HIPAA, ISO 27001",
|
"soc2_hipaa_iso_27001_compliance_check": "Vérification de conformité SOC2, HIPAA, ISO 27001",
|
||||||
"sso": "SSO (Google, Microsoft, OpenID Connect)",
|
"sso": "SSO (Google, Microsoft, OpenID Connect)",
|
||||||
"teams": "Équipes et Rôles d'Accès (Lire, Lire et Écrire, Gérer)",
|
"teams": "Équipes et Rôles d'Accès (Lire, Lire et Écrire, Gérer)",
|
||||||
"unlock_the_full_power_of_formbricks_free_for_30_days": "Débloquez tout le potentiel de Formbricks. Gratuit pendant 30 jours."
|
"unlock_the_full_power_of_formbricks_free_for_30_days": "Débloquez tout le potentiel de Formbricks. Gratuit pendant 30 jours.",
|
||||||
|
"your_enterprise_license_is_active_all_features_unlocked": "Votre licence d'entreprise est active. Toutes les fonctionnalités sont déverrouillées."
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"bulk_invite_warning_description": "Dans le plan gratuit, tous les membres de l'organisation se voient toujours attribuer le rôle \"Owner\".",
|
"bulk_invite_warning_description": "Dans le plan gratuit, tous les membres de l'organisation se voient toujours attribuer le rôle \"Owner\".",
|
||||||
@@ -1080,7 +989,7 @@
|
|||||||
"from_your_organization": "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}",
|
"invited_on": "Invité le {date}",
|
||||||
"invites_failed": "Invitations échouées",
|
"invites_failed": "Invitations échouées",
|
||||||
"leave_organization": "Quitter l'organisation",
|
"leave_organization": "Quitter l'organisation",
|
||||||
"leave_organization_description": "Vous quitterez cette organisation et perdrez l'accès à toutes les enquêtes et réponses. Vous ne pourrez revenir que si vous êtes de nouveau invité.",
|
"leave_organization_description": "Vous quitterez cette organisation et perdrez l'accès à toutes les enquêtes et réponses. Vous ne pourrez revenir que si vous êtes de nouveau invité.",
|
||||||
@@ -1193,6 +1102,8 @@
|
|||||||
"please_fill_all_workspace_fields": "Veuillez remplir tous les champs pour ajouter un nouvel espace de travail.",
|
"please_fill_all_workspace_fields": "Veuillez remplir tous les champs pour ajouter un nouvel espace de travail.",
|
||||||
"read": "Lire",
|
"read": "Lire",
|
||||||
"read_write": "Lire et Écrire",
|
"read_write": "Lire et Écrire",
|
||||||
|
"select_member": "Sélectionner membre",
|
||||||
|
"select_workspace": "Sélectionner un espace de travail",
|
||||||
"team_admin": "Administrateur d'équipe",
|
"team_admin": "Administrateur d'équipe",
|
||||||
"team_created_successfully": "Équipe créée avec succès.",
|
"team_created_successfully": "Équipe créée avec succès.",
|
||||||
"team_deleted_successfully": "Équipe supprimée avec succès.",
|
"team_deleted_successfully": "Équipe supprimée avec succès.",
|
||||||
@@ -1242,6 +1153,7 @@
|
|||||||
"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": "Ajoutez une bordure extérieure à votre carte d'enquête.",
|
||||||
"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",
|
||||||
@@ -1280,7 +1192,6 @@
|
|||||||
"block_duplicated": "Bloc dupliqué.",
|
"block_duplicated": "Bloc dupliqué.",
|
||||||
"bold": "Gras",
|
"bold": "Gras",
|
||||||
"brand_color": "Couleur de marque",
|
"brand_color": "Couleur de marque",
|
||||||
"brand_color_description": "Appliqué aux boutons, liens et éléments mis en évidence.",
|
|
||||||
"brightness": "Luminosité",
|
"brightness": "Luminosité",
|
||||||
"bulk_edit": "Modification en masse",
|
"bulk_edit": "Modification en masse",
|
||||||
"bulk_edit_description": "Modifiez toutes les options ci-dessous, une par ligne. Les lignes vides seront ignorées et les doublons supprimés.",
|
"bulk_edit_description": "Modifiez toutes les options ci-dessous, une par ligne. Les lignes vides seront ignorées et les doublons supprimés.",
|
||||||
@@ -1298,9 +1209,7 @@
|
|||||||
"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",
|
||||||
"card_background_color_description": "Remplit la zone de la carte d'enquête.",
|
|
||||||
"card_border_color": "Couleur de la bordure de la carte",
|
"card_border_color": "Couleur de la bordure de la carte",
|
||||||
"card_border_color_description": "Délimite la carte d'enquête.",
|
|
||||||
"card_styling": "Style de carte",
|
"card_styling": "Style de carte",
|
||||||
"casual": "Décontracté",
|
"casual": "Décontracté",
|
||||||
"caution_edit_duplicate": "Dupliquer et modifier",
|
"caution_edit_duplicate": "Dupliquer et modifier",
|
||||||
@@ -1311,12 +1220,20 @@
|
|||||||
"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.",
|
||||||
"caution_recommendation": "Cela peut entraîner des incohérences de données dans le résumé du sondage. Nous recommandons de dupliquer le sondage à la place.",
|
"caution_recommendation": "Cela peut entraîner des incohérences de données dans le résumé du sondage. Nous recommandons de dupliquer le sondage à la place.",
|
||||||
"caution_text": "Les changements entraîneront des incohérences.",
|
"caution_text": "Les changements entraîneront des incohérences.",
|
||||||
|
"centered_modal_overlay_color": "Couleur de superposition modale centrée",
|
||||||
"change_anyway": "Changer de toute façon",
|
"change_anyway": "Changer de toute façon",
|
||||||
"change_background": "Changer l'arrière-plan",
|
"change_background": "Changer l'arrière-plan",
|
||||||
"change_question_type": "Changer le type de question",
|
"change_question_type": "Changer le type de question",
|
||||||
"change_survey_type": "Le changement de type de sondage affecte l'accès existant",
|
"change_survey_type": "Le changement de type de sondage affecte l'accès existant",
|
||||||
|
"change_the_background_color_of_the_card": "Changez la couleur de fond de la carte.",
|
||||||
|
"change_the_background_color_of_the_input_fields": "Vous pouvez modifier la couleur d'arrière-plan des champs de saisie.",
|
||||||
"change_the_background_to_a_color_image_or_animation": "Changez l'arrière-plan en une couleur, une image ou une animation.",
|
"change_the_background_to_a_color_image_or_animation": "Changez l'arrière-plan en une couleur, une image ou une animation.",
|
||||||
|
"change_the_border_color_of_the_card": "Changez la couleur de la bordure de la carte.",
|
||||||
|
"change_the_border_color_of_the_input_fields": "Vous pouvez modifier la couleur de la bordure des champs de saisie.",
|
||||||
|
"change_the_border_radius_of_the_card_and_the_inputs": "Vous pouvez arrondir la bordure des encadrés et des champs de saisie.",
|
||||||
|
"change_the_brand_color_of_the_survey": "Vous pouvez modifier la couleur dominante d'une enquête.",
|
||||||
"change_the_placement_of_this_survey": "Changez le placement de cette enquête.",
|
"change_the_placement_of_this_survey": "Changez le placement de cette enquête.",
|
||||||
|
"change_the_question_color_of_the_survey": "Vous pouvez modifier la couleur des questions d'une enquête.",
|
||||||
"changes_saved": "Modifications enregistrées.",
|
"changes_saved": "Modifications enregistrées.",
|
||||||
"changing_survey_type_will_remove_existing_distribution_channels": "Le changement du type de sondage affectera la façon dont il peut être partagé. Si les répondants ont déjà des liens d'accès pour le type actuel, ils peuvent perdre l'accès après le changement.",
|
"changing_survey_type_will_remove_existing_distribution_channels": "Le changement du type de sondage affectera la façon dont il peut être partagé. Si les répondants ont déjà des liens d'accès pour le type actuel, ils peuvent perdre l'accès après le changement.",
|
||||||
"checkbox_label": "Étiquette de case à cocher",
|
"checkbox_label": "Étiquette de case à cocher",
|
||||||
@@ -1456,6 +1373,7 @@
|
|||||||
"hide_progress_bar": "Cacher la barre de progression",
|
"hide_progress_bar": "Cacher la barre de progression",
|
||||||
"hide_question_settings": "Masquer les paramètres de la question",
|
"hide_question_settings": "Masquer les paramètres de la question",
|
||||||
"hostname": "Nom d'hôte",
|
"hostname": "Nom d'hôte",
|
||||||
|
"how_funky_do_you_want_your_cards_in_survey_type_derived_surveys": "À quel point voulez-vous que vos cartes soient funky dans les enquêtes {surveyTypeDerived}",
|
||||||
"if_you_need_more_please": "Si vous avez besoin de plus, veuillez",
|
"if_you_need_more_please": "Si vous avez besoin de plus, veuillez",
|
||||||
"if_you_really_want_that_answer_ask_until_you_get_it": "Continuer à afficher à chaque déclenchement jusqu'à ce qu'une réponse soit soumise.",
|
"if_you_really_want_that_answer_ask_until_you_get_it": "Continuer à afficher à chaque déclenchement jusqu'à ce qu'une réponse soit soumise.",
|
||||||
"ignore_global_waiting_time": "Ignorer la période de refroidissement",
|
"ignore_global_waiting_time": "Ignorer la période de refroidissement",
|
||||||
@@ -1466,9 +1384,7 @@
|
|||||||
"initial_value": "Valeur initiale",
|
"initial_value": "Valeur initiale",
|
||||||
"inner_text": "Texte interne",
|
"inner_text": "Texte interne",
|
||||||
"input_border_color": "Couleur de la bordure des champs de saisie",
|
"input_border_color": "Couleur de la bordure des champs de saisie",
|
||||||
"input_border_color_description": "Délimite les champs de texte et les zones de texte.",
|
|
||||||
"input_color": "Couleur d'arrière-plan des champs de saisie",
|
"input_color": "Couleur d'arrière-plan des champs de saisie",
|
||||||
"input_color_description": "Remplit l'intérieur des champs de texte.",
|
|
||||||
"insert_link": "Insérer un lien",
|
"insert_link": "Insérer un lien",
|
||||||
"invalid_targeting": "Ciblage invalide : Veuillez vérifier vos filtres d'audience",
|
"invalid_targeting": "Ciblage invalide : Veuillez vérifier vos filtres d'audience",
|
||||||
"invalid_video_url_warning": "Merci d'entrer une URL YouTube, Vimeo ou Loom valide. Les autres plateformes vidéo ne sont pas encore supportées.",
|
"invalid_video_url_warning": "Merci d'entrer une URL YouTube, Vimeo ou Loom valide. Les autres plateformes vidéo ne sont pas encore supportées.",
|
||||||
@@ -1552,12 +1468,13 @@
|
|||||||
"protect_survey_with_pin_description": "Seules les personnes ayant le code PIN peuvent accéder à l'enquête.",
|
"protect_survey_with_pin_description": "Seules les personnes ayant le code PIN peuvent accéder à l'enquête.",
|
||||||
"publish": "Publier",
|
"publish": "Publier",
|
||||||
"question": "Question",
|
"question": "Question",
|
||||||
|
"question_color": "Couleur des questions",
|
||||||
"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_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}\"",
|
||||||
"question_used_in_recall": "Cette question est rappelée dans la question {questionIndex}.",
|
"question_used_in_recall": "Cette question est rappelée dans la question {questionIndex}.",
|
||||||
"question_used_in_recall_ending_card": "Cette question est rappelée dans la carte de fin.",
|
"question_used_in_recall_ending_card": "Cette question est rappelée dans la carte de fin.",
|
||||||
"quotas": {
|
"quotas": {
|
||||||
@@ -1613,7 +1530,6 @@
|
|||||||
"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 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",
|
||||||
@@ -1655,6 +1571,7 @@
|
|||||||
"styling_set_to_theme_styles": "Style défini sur les styles du thème",
|
"styling_set_to_theme_styles": "Style défini sur les styles du thème",
|
||||||
"subheading": "Sous-titre",
|
"subheading": "Sous-titre",
|
||||||
"subtract": "Soustraire -",
|
"subtract": "Soustraire -",
|
||||||
|
"suggest_colors": "Suggérer des couleurs",
|
||||||
"survey_completed_heading": "Enquête terminée",
|
"survey_completed_heading": "Enquête terminée",
|
||||||
"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",
|
||||||
@@ -1731,7 +1648,7 @@
|
|||||||
"validation_rules": "Règles de validation",
|
"validation_rules": "Règles de validation",
|
||||||
"validation_rules_description": "Accepter uniquement les réponses qui répondent aux critères suivants",
|
"validation_rules_description": "Accepter uniquement les réponses qui répondent aux critères suivants",
|
||||||
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} est utilisé dans la logique de la question {questionIndex}. Veuillez d'abord le supprimer de la logique.",
|
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} est utilisé dans la logique de la question {questionIndex}. Veuillez d'abord le supprimer de la logique.",
|
||||||
"variable_is_used_in_quota_please_remove_it_from_quota_first": "La variable “{variableName}” est utilisée dans le quota “{quotaName}”",
|
"variable_is_used_in_quota_please_remove_it_from_quota_first": "La variable \"{variableName}\" est utilisée dans le quota \"{quotaName}\"",
|
||||||
"variable_name_conflicts_with_hidden_field": "Le nom de la variable est en conflit avec un ID de champ masqué existant.",
|
"variable_name_conflicts_with_hidden_field": "Le nom de la variable est en conflit avec un ID de champ masqué existant.",
|
||||||
"variable_name_is_already_taken_please_choose_another": "Le nom de la variable est déjà pris, veuillez en choisir un autre.",
|
"variable_name_is_already_taken_please_choose_another": "Le nom de la variable est déjà pris, veuillez en choisir un autre.",
|
||||||
"variable_name_must_start_with_a_letter": "Le nom de la variable doit commencer par une lettre.",
|
"variable_name_must_start_with_a_letter": "Le nom de la variable doit commencer par une lettre.",
|
||||||
@@ -1957,7 +1874,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",
|
||||||
@@ -2000,7 +1916,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",
|
||||||
@@ -2140,71 +2055,9 @@
|
|||||||
"look": {
|
"look": {
|
||||||
"add_background_color": "Ajouter une couleur d'arrière-plan",
|
"add_background_color": "Ajouter une couleur d'arrière-plan",
|
||||||
"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_button_bg": "Arrière-plan du bouton",
|
|
||||||
"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_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_height_description": "Contrôle la hauteur du bouton.",
|
|
||||||
"advanced_styling_field_button_padding_x_description": "Ajoute de l'espace à gauche et à droite.",
|
|
||||||
"advanced_styling_field_button_padding_y_description": "Ajoute de l'espace en haut et en bas.",
|
|
||||||
"advanced_styling_field_button_text": "Texte du bouton",
|
|
||||||
"advanced_styling_field_button_text_description": "Colore le libellé à l'intérieur des boutons.",
|
|
||||||
"advanced_styling_field_description_color": "Couleur de la description",
|
|
||||||
"advanced_styling_field_description_color_description": "Colore le texte sous chaque titre.",
|
|
||||||
"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_weight": "Graisse de police de la description",
|
|
||||||
"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_weight": "Graisse de police",
|
|
||||||
"advanced_styling_field_headline_color": "Couleur du titre",
|
|
||||||
"advanced_styling_field_headline_color_description": "Colore le texte principal de la question.",
|
|
||||||
"advanced_styling_field_headline_size": "Taille de police du titre",
|
|
||||||
"advanced_styling_field_headline_size_description": "Ajuste la taille du texte du titre.",
|
|
||||||
"advanced_styling_field_headline_weight": "Graisse de police du titre",
|
|
||||||
"advanced_styling_field_headline_weight_description": "Rend le texte du titre plus léger ou plus gras.",
|
|
||||||
"advanced_styling_field_height": "Hauteur minimale",
|
|
||||||
"advanced_styling_field_indicator_bg": "Arrière-plan de l'indicateur",
|
|
||||||
"advanced_styling_field_indicator_bg_description": "Colore la partie remplie de la barre.",
|
|
||||||
"advanced_styling_field_input_border_radius_description": "Arrondit les coins du champ de saisie.",
|
|
||||||
"advanced_styling_field_input_font_size_description": "Ajuste la taille du texte saisi dans les champs.",
|
|
||||||
"advanced_styling_field_input_height_description": "Contrôle la hauteur minimale du champ de saisie.",
|
|
||||||
"advanced_styling_field_input_padding_x_description": "Ajoute de l'espace à gauche et à droite.",
|
|
||||||
"advanced_styling_field_input_padding_y_description": "Ajoute de l'espace en haut et en bas.",
|
|
||||||
"advanced_styling_field_input_placeholder_opacity_description": "Atténue le texte d'indication du placeholder.",
|
|
||||||
"advanced_styling_field_input_shadow_description": "Ajoute une ombre portée autour des champs de saisie.",
|
|
||||||
"advanced_styling_field_input_text": "Texte de saisie",
|
|
||||||
"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_description": "Remplit les éléments d'option.",
|
|
||||||
"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_label": "Couleur de l'étiquette",
|
|
||||||
"advanced_styling_field_option_label_description": "Colore le texte des libellés d'option.",
|
|
||||||
"advanced_styling_field_option_padding_x_description": "Ajoute de l'espace à gauche et à droite.",
|
|
||||||
"advanced_styling_field_option_padding_y_description": "Ajoute de l'espace en haut et en bas.",
|
|
||||||
"advanced_styling_field_padding_x": "Marge intérieure X",
|
|
||||||
"advanced_styling_field_padding_y": "Marge intérieure Y",
|
|
||||||
"advanced_styling_field_placeholder_opacity": "Opacité du placeholder",
|
|
||||||
"advanced_styling_field_shadow": "Ombre",
|
|
||||||
"advanced_styling_field_track_bg": "Arrière-plan de la piste",
|
|
||||||
"advanced_styling_field_track_bg_description": "Colore la partie non remplie de la barre.",
|
|
||||||
"advanced_styling_field_track_height": "Hauteur de la piste",
|
|
||||||
"advanced_styling_field_track_height_description": "Contrôle l'épaisseur de la barre de progression.",
|
|
||||||
"advanced_styling_field_upper_label_color": "Couleur de l'étiquette du titre",
|
|
||||||
"advanced_styling_field_upper_label_color_description": "Colore le petit libellé au-dessus des champs de saisie.",
|
|
||||||
"advanced_styling_field_upper_label_size": "Taille de police de l'étiquette du titre",
|
|
||||||
"advanced_styling_field_upper_label_size_description": "Ajuste la taille du petit libellé au-dessus des champs de saisie.",
|
|
||||||
"advanced_styling_field_upper_label_weight": "Graisse de police de l'étiquette du titre",
|
|
||||||
"advanced_styling_field_upper_label_weight_description": "Rend le libellé plus léger ou plus gras.",
|
|
||||||
"advanced_styling_section_buttons": "Boutons",
|
|
||||||
"advanced_styling_section_headlines": "Titres et descriptions",
|
|
||||||
"advanced_styling_section_inputs": "Champs de saisie",
|
|
||||||
"advanced_styling_section_options": "Options (boutons radio/cases à cocher)",
|
|
||||||
"app_survey_placement": "Placement du sondage d'application",
|
"app_survey_placement": "Placement du sondage d'application",
|
||||||
"app_survey_placement_settings_description": "Modifiez l'emplacement où les sondages seront affichés dans votre application web ou site web.",
|
"app_survey_placement_settings_description": "Modifiez l'emplacement où les sondages seront affichés dans votre application web ou site web.",
|
||||||
|
"centered_modal_overlay_color": "Couleur de superposition modale centrée",
|
||||||
"email_customization": "Personnalisation des e-mails",
|
"email_customization": "Personnalisation des e-mails",
|
||||||
"email_customization_description": "Modifiez l'apparence des e-mails que Formbricks envoie en votre nom.",
|
"email_customization_description": "Modifiez l'apparence des e-mails que Formbricks envoie en votre nom.",
|
||||||
"enable_custom_styling": "Activer le style personnalisé",
|
"enable_custom_styling": "Activer le style personnalisé",
|
||||||
@@ -2215,9 +2068,6 @@
|
|||||||
"formbricks_branding_hidden": "Le logo Formbricks est masqué.",
|
"formbricks_branding_hidden": "Le logo Formbricks est masqué.",
|
||||||
"formbricks_branding_settings_description": "Nous apprécions votre soutien mais comprenons si vous choisissez de le désactiver.",
|
"formbricks_branding_settings_description": "Nous apprécions votre soutien mais comprenons si vous choisissez de le désactiver.",
|
||||||
"formbricks_branding_shown": "Le logo Formbricks est affiché.",
|
"formbricks_branding_shown": "Le logo Formbricks est affiché.",
|
||||||
"generate_theme_btn": "Générer",
|
|
||||||
"generate_theme_confirmation": "Souhaitez-vous générer un thème de couleurs assorti basé sur votre couleur de marque ? Cela écrasera vos paramètres de couleur actuels.",
|
|
||||||
"generate_theme_header": "Générer un thème de couleurs ?",
|
|
||||||
"logo_removed_successfully": "Logo supprimé avec succès",
|
"logo_removed_successfully": "Logo supprimé avec succès",
|
||||||
"logo_settings_description": "Téléchargez le logo de votre entreprise pour personnaliser les enquêtes et les aperçus de liens.",
|
"logo_settings_description": "Téléchargez le logo de votre entreprise pour personnaliser les enquêtes et les aperçus de liens.",
|
||||||
"logo_updated_successfully": "Logo mis à jour avec succès",
|
"logo_updated_successfully": "Logo mis à jour avec succès",
|
||||||
@@ -2232,8 +2082,6 @@
|
|||||||
"show_formbricks_branding_in": "Afficher le logo Formbricks dans les enquêtes {type}",
|
"show_formbricks_branding_in": "Afficher le logo Formbricks dans les enquêtes {type}",
|
||||||
"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",
|
|
||||||
"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."
|
||||||
},
|
},
|
||||||
@@ -2997,7 +2845,6 @@
|
|||||||
"preview_survey_question_2_choice_1_label": "Oui, tenez-moi au courant.",
|
"preview_survey_question_2_choice_1_label": "Oui, tenez-moi au courant.",
|
||||||
"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_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",
|
||||||
|
|||||||
+47
-200
@@ -188,7 +188,6 @@
|
|||||||
"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": "napok",
|
|
||||||
"default": "Alapértelmezett",
|
"default": "Alapértelmezett",
|
||||||
"delete": "Törlés",
|
"delete": "Törlés",
|
||||||
"description": "Leírás",
|
"description": "Leírás",
|
||||||
@@ -218,7 +217,6 @@
|
|||||||
"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ése során",
|
|
||||||
"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",
|
||||||
@@ -256,7 +254,7 @@
|
|||||||
"label": "Címke",
|
"label": "Címke",
|
||||||
"language": "Nyelv",
|
"language": "Nyelv",
|
||||||
"learn_more": "Tudjon meg többet",
|
"learn_more": "Tudjon meg többet",
|
||||||
"license_expired": "A licenc lejárt",
|
"license_expired": "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": "Összekapcsolás",
|
"link": "Összekapcsolás",
|
||||||
@@ -277,7 +275,6 @@
|
|||||||
"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ó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",
|
||||||
@@ -288,7 +285,6 @@
|
|||||||
"no_background_image_found": "Nem található háttérkép.",
|
"no_background_image_found": "Nem található háttérkép.",
|
||||||
"no_code": "Kód nélkül",
|
"no_code": "Kód nélkül",
|
||||||
"no_files_uploaded": "Nem lettek fájlok feltöltve",
|
"no_files_uploaded": "Nem lettek fájlok feltöltve",
|
||||||
"no_overlay": "Nincs rávetítés",
|
|
||||||
"no_quotas_found": "Nem találhatók kvóták",
|
"no_quotas_found": "Nem találhatók kvóták",
|
||||||
"no_result_found": "Nem található eredmény",
|
"no_result_found": "Nem található eredmény",
|
||||||
"no_results": "Nincs találat",
|
"no_results": "Nincs találat",
|
||||||
@@ -315,7 +311,6 @@
|
|||||||
"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": "Egyebek",
|
"others": "Egyebek",
|
||||||
"overlay_color": "Rávetítés színe",
|
|
||||||
"overview": "Áttekintés",
|
"overview": "Áttekintés",
|
||||||
"password": "Jelszó",
|
"password": "Jelszó",
|
||||||
"paused": "Szüneteltetve",
|
"paused": "Szüneteltetve",
|
||||||
@@ -355,7 +350,6 @@
|
|||||||
"request_trial_license": "Próbalicenc kérése",
|
"request_trial_license": "Próbalicenc kérése",
|
||||||
"reset_to_default": "Visszaállítás az alapértelmezettre",
|
"reset_to_default": "Visszaállítás az alapértelmezettre",
|
||||||
"response": "Válasz",
|
"response": "Válasz",
|
||||||
"response_id": "Válaszazonosító",
|
|
||||||
"responses": "Válaszok",
|
"responses": "Válaszok",
|
||||||
"restart": "Újraindítás",
|
"restart": "Újraindítás",
|
||||||
"role": "Szerep",
|
"role": "Szerep",
|
||||||
@@ -396,7 +390,6 @@
|
|||||||
"status": "Állapot",
|
"status": "Állapot",
|
||||||
"step_by_step_manual": "Lépésenkénti kézikönyv",
|
"step_by_step_manual": "Lépésenkénti kézikönyv",
|
||||||
"storage_not_configured": "A fájltároló nincs beállítva, a feltöltések valószínűleg sikertelenek lesznek",
|
"storage_not_configured": "A fájltároló nincs beállítva, a feltöltések valószínűleg sikertelenek lesznek",
|
||||||
"string": "Szöveg",
|
|
||||||
"styling": "Stíluskészítés",
|
"styling": "Stíluskészítés",
|
||||||
"submit": "Elküldés",
|
"submit": "Elküldés",
|
||||||
"summary": "Összegzés",
|
"summary": "Összegzés",
|
||||||
@@ -429,7 +422,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",
|
||||||
@@ -453,7 +445,6 @@
|
|||||||
"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": "hetek",
|
|
||||||
"welcome_card": "Üdvözlő kártya",
|
"welcome_card": "Üdvözlő kártya",
|
||||||
"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",
|
||||||
@@ -464,7 +455,6 @@
|
|||||||
"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": "é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.",
|
||||||
@@ -472,7 +462,7 @@
|
|||||||
"you_have_reached_your_monthly_miu_limit_of": "Elérte a havi MIU-korlátját ennek:",
|
"you_have_reached_your_monthly_miu_limit_of": "Elérte a havi MIU-korlátját ennek:",
|
||||||
"you_have_reached_your_monthly_response_limit_of": "Elérte a havi válaszkorlátját ennek:",
|
"you_have_reached_your_monthly_response_limit_of": "Elérte a havi válaszkorlátját ennek:",
|
||||||
"you_will_be_downgraded_to_the_community_edition_on_date": "Vissza lesz állítva a közösségi kiadásra ekkor: {date}.",
|
"you_will_be_downgraded_to_the_community_edition_on_date": "Vissza lesz állítva a közösségi kiadásra ekkor: {date}.",
|
||||||
"your_license_has_expired_please_renew": "A vállalati licence lejárt. Újítsa meg, hogy továbbra is használhassa a vállalati funkciókat."
|
"your_license_has_expired_please_renew": "A vállalati licenced lejárt. Kérjük, újítsd meg, hogy továbbra is használhasd a vállalati funkciókat."
|
||||||
},
|
},
|
||||||
"emails": {
|
"emails": {
|
||||||
"accept": "Elfogadás",
|
"accept": "Elfogadás",
|
||||||
@@ -636,45 +626,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 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 adattípusa: {dataType})",
|
|
||||||
"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": "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": "Új '{key}' attribútum létrehozva '{dataType}' típussal",
|
|
||||||
"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",
|
|
||||||
"data_type": "Adattípus",
|
|
||||||
"data_type_cannot_be_changed": "Az adattípus létrehozás után nem módosítható",
|
|
||||||
"data_type_description": "Válaszd ki, hogyan legyen tárolva és szűrve ez az attribútum",
|
|
||||||
"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",
|
||||||
"edit_attribute_values_description": "Bizonyos attribútumok értékének megváltoztatása ennél a partnernél.",
|
"edit_attribute_values_description": "Bizonyos attribútumok értékének megváltoztatása ennél a partnernél.",
|
||||||
"edit_attributes": "Attribútumok szerkesztése",
|
|
||||||
"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 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. Kérlek, adj meg egy érvényes dátumot.",
|
|
||||||
"invalid_number_format": "Érvénytelen számformátum. Kérlek, adj meg egy érvényes számot.",
|
|
||||||
"no_activity_yet": "Még nincs aktivitás",
|
|
||||||
"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 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",
|
||||||
@@ -683,24 +656,13 @@
|
|||||||
"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 kiválasztása",
|
|
||||||
"survey_viewed": "Kérdőív megtekintve",
|
|
||||||
"survey_viewed_at": "Megtekintve",
|
|
||||||
"system_attributes": "Rendszer attribú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 típusa \"{dataType}\", de a CSV érvénytelen értékeket tartalmaz: {values}",
|
|
||||||
"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ájl mérete meghaladja a maximális 800KB-os limitet",
|
|
||||||
"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": "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 é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_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_should_be_mapped_to": "le kell képezni erre:",
|
||||||
"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_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?",
|
||||||
@@ -757,12 +719,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. Kérjük, csatlakozzon újra a válaszok szinkronizálásának folytatásához. A meglévő táblázathivatkozások és adatok megmaradnak.",
|
|
||||||
"reconnect_button_tooltip": "Csatlakoztassa újra az integrációt a hozzáférés frissítéséhez. A meglévő táblázathivatkozások és adatok megmaradnak.",
|
|
||||||
"spreadsheet_permission_error": "Nincs jogosultsága a táblázat eléréséhez. Kérjük, győződjön meg arról, hogy a táblázat meg van osztva a Google-fiókjával, és írási jogosultsággal rendelkezik 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. Kérjük, 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",
|
||||||
@@ -854,7 +811,7 @@
|
|||||||
"webhook_deleted_successfully": "A webhorog sikeresen törölve",
|
"webhook_deleted_successfully": "A webhorog sikeresen törölve",
|
||||||
"webhook_name_placeholder": "Választható: címkézze meg a webhorgot az egyszerű azonosításért",
|
"webhook_name_placeholder": "Választható: címkézze meg a webhorgot az egyszerű azonosításért",
|
||||||
"webhook_test_failed_due_to": "A webhorog tesztelése sikertelen a következő miatt:",
|
"webhook_test_failed_due_to": "A webhorog tesztelése sikertelen a következő miatt:",
|
||||||
"webhook_updated_successfully": "A webhorog sikeresen frissítve",
|
"webhook_updated_successfully": "A webhorog sikeresen frissítve.",
|
||||||
"webhook_url_placeholder": "Illessze be azt az URL-t, amelyen az eseményt aktiválni szeretné"
|
"webhook_url_placeholder": "Illessze be azt az URL-t, amelyen az eseményt aktiválni szeretné"
|
||||||
},
|
},
|
||||||
"website_or_app_integration_description": "A Formbricks integrálása a webhelyébe vagy alkalmazásába",
|
"website_or_app_integration_description": "A Formbricks integrálása a webhelyébe vagy alkalmazásába",
|
||||||
@@ -863,7 +820,7 @@
|
|||||||
"segments": {
|
"segments": {
|
||||||
"add_filter_below": "Szűrő hozzáadása lent",
|
"add_filter_below": "Szűrő hozzáadása lent",
|
||||||
"add_your_first_filter_to_get_started": "Adja hozzá az első szűrőt a kezdéshez",
|
"add_your_first_filter_to_get_started": "Adja hozzá az első szűrőt a kezdéshez",
|
||||||
"cannot_delete_segment_used_in_surveys": "Nem tudja törölni ezt a szakaszt, mert még mindig használatban van ezekben a kérdőívekben:",
|
"cannot_delete_segment_used_in_surveys": "Nem tudja eltávolítani ezt a szakaszt, mert még mindig használatban van ezekben a kérdőívekben:",
|
||||||
"clone_and_edit_segment": "Szakasz klónozása és szerkesztése",
|
"clone_and_edit_segment": "Szakasz klónozása és szerkesztése",
|
||||||
"create_group": "Csoport létrehozása",
|
"create_group": "Csoport létrehozása",
|
||||||
"create_your_first_segment": "Hozza létre az első szakaszt a kezdéshez",
|
"create_your_first_segment": "Hozza létre az első szakaszt a kezdéshez",
|
||||||
@@ -886,40 +843,6 @@
|
|||||||
"no_attributes_yet": "Még nincsenek attribútumok!",
|
"no_attributes_yet": "Még nincsenek attribútumok!",
|
||||||
"no_filters_yet": "Még nincsenek szűrők!",
|
"no_filters_yet": "Még nincsenek szűrők!",
|
||||||
"no_segments_yet": "Jelenleg nincsenek mentett szakaszai.",
|
"no_segments_yet": "Jelenleg nincsenek mentett szakaszai.",
|
||||||
"operator_contains": "tartalmazza",
|
|
||||||
"operator_does_not_contain": "nem tartalmazza",
|
|
||||||
"operator_ends_with": "ezzel végződik",
|
|
||||||
"operator_is_after": "ez után",
|
|
||||||
"operator_is_before": "ez előtt",
|
|
||||||
"operator_is_between": "között",
|
|
||||||
"operator_is_newer_than": "újabb mint",
|
|
||||||
"operator_is_not_set": "nincs beállítva",
|
|
||||||
"operator_is_older_than": "régebbi mint",
|
|
||||||
"operator_is_same_day": "ugyanazon a napon",
|
|
||||||
"operator_is_set": "beállítva",
|
|
||||||
"operator_starts_with": "ezzel kezdődik",
|
|
||||||
"operator_title_contains": "Tartalmazza",
|
|
||||||
"operator_title_does_not_contain": "Nem tartalmazza",
|
|
||||||
"operator_title_ends_with": "Ezzel végződik",
|
|
||||||
"operator_title_equals": "Egyenlő",
|
|
||||||
"operator_title_greater_equal": "Nagyobb vagy egyenlő",
|
|
||||||
"operator_title_greater_than": "Nagyobb mint",
|
|
||||||
"operator_title_is_after": "Ez után",
|
|
||||||
"operator_title_is_before": "Ez előtt",
|
|
||||||
"operator_title_is_between": "Között",
|
|
||||||
"operator_title_is_newer_than": "Újabb mint",
|
|
||||||
"operator_title_is_not_set": "Nincs beállítva",
|
|
||||||
"operator_title_is_older_than": "Régebbi mint",
|
|
||||||
"operator_title_is_same_day": "Ugyanazon a napon",
|
|
||||||
"operator_title_is_set": "Beállítva",
|
|
||||||
"operator_title_less_equal": "Kisebb vagy egyenlő",
|
|
||||||
"operator_title_less_than": "Kisebb mint",
|
|
||||||
"operator_title_not_equals": "Nem egyenlő",
|
|
||||||
"operator_title_starts_with": "Ezzel kezdődik",
|
|
||||||
"operator_title_user_is_in": "A felhasználó benne van",
|
|
||||||
"operator_title_user_is_not_in": "A felhasználó nincs benne",
|
|
||||||
"operator_user_is_in": "A felhasználó benne van",
|
|
||||||
"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.",
|
||||||
@@ -928,11 +851,11 @@
|
|||||||
"reset_all_filters": "Összes szűrő visszaállítása",
|
"reset_all_filters": "Összes szűrő visszaállítása",
|
||||||
"save_as_new_segment": "Mentés új szakaszként",
|
"save_as_new_segment": "Mentés új szakaszként",
|
||||||
"save_your_filters_as_a_segment_to_use_it_in_other_surveys": "A szűrők mentése szakaszként más kérdőívekben való használathoz",
|
"save_your_filters_as_a_segment_to_use_it_in_other_surveys": "A szűrők mentése szakaszként más kérdőívekben való használathoz",
|
||||||
"segment_created_successfully": "A szakasz sikeresen létrehozva",
|
"segment_created_successfully": "A szakasz sikeresen létrehozva!",
|
||||||
"segment_deleted_successfully": "A szakasz sikeresen törölve",
|
"segment_deleted_successfully": "A szakasz sikeresen törölve!",
|
||||||
"segment_id": "Szakaszazonosító",
|
"segment_id": "Szakaszazonosító",
|
||||||
"segment_saved_successfully": "A szakasz sikeresen elmentve",
|
"segment_saved_successfully": "A szakasz sikeresen elmentve",
|
||||||
"segment_updated_successfully": "A szakasz sikeresen frissítve",
|
"segment_updated_successfully": "A szakasz sikeresen frissítve!",
|
||||||
"segments_help_you_target_users_with_same_characteristics_easily": "A szakaszok segítik a hasonló jellemzőkkel rendelkező felhasználók könnyű megcélzását",
|
"segments_help_you_target_users_with_same_characteristics_easily": "A szakaszok segítik a hasonló jellemzőkkel rendelkező felhasználók könnyű megcélzását",
|
||||||
"target_audience": "Célközönség",
|
"target_audience": "Célközönség",
|
||||||
"this_action_resets_all_filters_in_this_survey": "Ez a művelet visszaállítja az összes szűrőt ebben a kérdőívben.",
|
"this_action_resets_all_filters_in_this_survey": "Ez a művelet visszaállítja az összes szűrőt ebben a kérdőívben.",
|
||||||
@@ -944,7 +867,6 @@
|
|||||||
"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ékének 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.",
|
|
||||||
"view_filters": "Szűrők megtekintése",
|
"view_filters": "Szűrők megtekintése",
|
||||||
"where": "Ahol",
|
"where": "Ahol",
|
||||||
"with_the_formbricks_sdk": "a Formbricks SDK-val"
|
"with_the_formbricks_sdk": "a Formbricks SDK-val"
|
||||||
@@ -1031,32 +953,19 @@
|
|||||||
"enterprise_features": "Vállalati funkciók",
|
"enterprise_features": "Vállalati funkciók",
|
||||||
"get_an_enterprise_license_to_get_access_to_all_features": "Vállalati licenc megszerzése az összes funkcióhoz való hozzáféréshez.",
|
"get_an_enterprise_license_to_get_access_to_all_features": "Vállalati licenc megszerzése az összes funkcióhoz való hozzáféréshez.",
|
||||||
"keep_full_control_over_your_data_privacy_and_security": "Az adatvédelem és biztonság fölötti rendelkezés teljes kézben tartása.",
|
"keep_full_control_over_your_data_privacy_and_security": "Az adatvédelem és biztonság fölötti rendelkezés teljes kézben tartása.",
|
||||||
"license_invalid_description": "Az ENTERPRISE_LICENSE_KEY környezeti változóban lévő licenckulcs nem érvényes. Ellenőrizze, hogy nem gépelte-e el, vagy kérjen új kulcsot.",
|
|
||||||
"license_status": "Licencállapot",
|
|
||||||
"license_status_active": "Aktív",
|
|
||||||
"license_status_description": "A vállalati licenc állapota.",
|
|
||||||
"license_status_expired": "Lejárt",
|
|
||||||
"license_status_invalid": "Érvénytelen licenc",
|
|
||||||
"license_status_unreachable": "Nem érhető el",
|
|
||||||
"license_unreachable_grace_period": "A licenckiszolgálót nem lehet elérni. A vállalati funkciók egy 3 napos türelmi időszak alatt aktívak maradnak, egészen eddig: {gracePeriodEnd}.",
|
|
||||||
"no_call_needed_no_strings_attached_request_a_free_30_day_trial_license_to_test_all_features_by_filling_out_this_form": "Nincs szükség telefonálásra, nincs feltételekhez kötöttség: kérjen 30 napos ingyenes próbalicencet az összes funkció kipróbálásához az alábbi űrlap kitöltésével:",
|
"no_call_needed_no_strings_attached_request_a_free_30_day_trial_license_to_test_all_features_by_filling_out_this_form": "Nincs szükség telefonálásra, nincs feltételekhez kötöttség: kérjen 30 napos ingyenes próbalicencet az összes funkció kipróbálásához az alábbi űrlap kitöltésével:",
|
||||||
"no_credit_card_no_sales_call_just_test_it": "Nem kell hitelkártya. Nincsenek értékesítési hívások. Egyszerűen csak próbálja ki :)",
|
"no_credit_card_no_sales_call_just_test_it": "Nem kell hitelkártya. Nincsenek értékesítési hívások. Egyszerűen csak próbálja ki :)",
|
||||||
"on_request": "Kérésre",
|
"on_request": "Kérésre",
|
||||||
"organization_roles": "Szervezeti szerepek (adminisztrátor, szerkesztő, fejlesztő stb.)",
|
"organization_roles": "Szervezeti szerepek (adminisztrátor, szerkesztő, fejlesztő stb.)",
|
||||||
"questions_please_reach_out_to": "Kérdése van? Írjon nekünk erre az e-mail-címre:",
|
"questions_please_reach_out_to": "Kérdése van? Írjon nekünk erre az e-mail-címre:",
|
||||||
"recheck_license": "Licenc újraellenőrzése",
|
|
||||||
"recheck_license_failed": "A licencellenőrzés nem sikerült. Lehet, hogy a licenckiszolgáló nem érhető el.",
|
|
||||||
"recheck_license_invalid": "A licenckulcs érvénytelen. Ellenőrizze az ENTERPRISE_LICENSE_KEY értékét.",
|
|
||||||
"recheck_license_success": "A licencellenőrzés sikeres",
|
|
||||||
"recheck_license_unreachable": "A licenckiszolgáló nem érhető el. Próbálja meg később újra.",
|
|
||||||
"rechecking": "Újraellenőrzés…",
|
|
||||||
"request_30_day_trial_license": "30 napos ingyenes licenc kérése",
|
"request_30_day_trial_license": "30 napos ingyenes licenc kérése",
|
||||||
"saml_sso": "SAML SSO",
|
"saml_sso": "SAML SSO",
|
||||||
"service_level_agreement": "Szolgáltatási megállapodás",
|
"service_level_agreement": "Szolgáltatási megállapodás",
|
||||||
"soc2_hipaa_iso_27001_compliance_check": "SOC2, HIPAA, ISO 27001 megfelelőségi ellenőrzés",
|
"soc2_hipaa_iso_27001_compliance_check": "SOC2, HIPAA, ISO 27001 megfelelőségi ellenőrzés",
|
||||||
"sso": "SSO (Google, Microsoft, OpenID-kapcsolat)",
|
"sso": "SSO (Google, Microsoft, OpenID-kapcsolat)",
|
||||||
"teams": "Csapatok és hozzáférési szerepek (olvasás, olvasás és írás, kezelés)",
|
"teams": "Csapatok és hozzáférési szerepek (olvasás, olvasás és írás, kezelés)",
|
||||||
"unlock_the_full_power_of_formbricks_free_for_30_days": "A Formbricks teljes erejének feloldása. 30 napig ingyen."
|
"unlock_the_full_power_of_formbricks_free_for_30_days": "A Formbricks teljes erejének feloldása. 30 napig ingyen.",
|
||||||
|
"your_enterprise_license_is_active_all_features_unlocked": "A vállalati licence aktív. Az összes funkció feloldva."
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"bulk_invite_warning_description": "Az ingyenes csomagban az összes szervezeti tag mindig a „Tulajdonos” szerephez van hozzárendelve.",
|
"bulk_invite_warning_description": "Az ingyenes csomagban az összes szervezeti tag mindig a „Tulajdonos” szerephez van hozzárendelve.",
|
||||||
@@ -1080,7 +989,7 @@
|
|||||||
"from_your_organization": "a szervezetétő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}",
|
"invited_on": "Meghívva ekkor: {date}",
|
||||||
"invites_failed": "A meghívás sikertelen",
|
"invites_failed": "A meghívás sikertelen",
|
||||||
"leave_organization": "Szervezet elhagyása",
|
"leave_organization": "Szervezet elhagyása",
|
||||||
"leave_organization_description": "Elhagyja ezt a szervezetet, és elveszíti az összes kérdőívhez és válaszhoz való hozzáférését. Csak akkor tud ismét csatlakozni, ha újra meghívják.",
|
"leave_organization_description": "Elhagyja ezt a szervezetet, és elveszíti az összes kérdőívhez és válaszhoz való hozzáférését. Csak akkor tud ismét csatlakozni, ha újra meghívják.",
|
||||||
@@ -1095,8 +1004,8 @@
|
|||||||
"member_invited_successfully": "A tag sikeresen meghívva",
|
"member_invited_successfully": "A tag sikeresen meghívva",
|
||||||
"once_its_gone_its_gone": "Ha egyszer eltűnt, akkor eltűnt.",
|
"once_its_gone_its_gone": "Ha egyszer eltűnt, akkor eltűnt.",
|
||||||
"only_org_owner_can_perform_action": "Csak a szervezet tulajdonosai férhetnek hozzá ehhez a beállításhoz.",
|
"only_org_owner_can_perform_action": "Csak a szervezet tulajdonosai férhetnek hozzá ehhez a beállításhoz.",
|
||||||
"organization_created_successfully": "A szervezet sikeresen létrehozva",
|
"organization_created_successfully": "A szervezet sikeresen létrehozva!",
|
||||||
"organization_deleted_successfully": "A szervezet sikeresen törölve",
|
"organization_deleted_successfully": "A szervezet sikeresen törölve.",
|
||||||
"organization_invite_link_ready": "A szervezete meghívási hivatkozása készen áll!",
|
"organization_invite_link_ready": "A szervezete meghívási hivatkozása készen áll!",
|
||||||
"organization_name": "Szervezet neve",
|
"organization_name": "Szervezet neve",
|
||||||
"organization_name_description": "Adjon a szervezetének egy leíró nevet.",
|
"organization_name_description": "Adjon a szervezetének egy leíró nevet.",
|
||||||
@@ -1193,9 +1102,11 @@
|
|||||||
"please_fill_all_workspace_fields": "Töltse ki az összes mezőt egy új munkaterület hozzáadásához.",
|
"please_fill_all_workspace_fields": "Töltse ki az összes mezőt egy új munkaterület hozzáadásához.",
|
||||||
"read": "Olvasás",
|
"read": "Olvasás",
|
||||||
"read_write": "Olvasás és írás",
|
"read_write": "Olvasás és írás",
|
||||||
|
"select_member": "Tag kiválasztása",
|
||||||
|
"select_workspace": "Munkaterület kiválasztása",
|
||||||
"team_admin": "Csapatadminisztrátor",
|
"team_admin": "Csapatadminisztrátor",
|
||||||
"team_created_successfully": "A csapat sikeresen létrehozva",
|
"team_created_successfully": "A csapat sikeresen létrehozva.",
|
||||||
"team_deleted_successfully": "A csapat sikeresen törölve",
|
"team_deleted_successfully": "A csapat sikeresen törölve.",
|
||||||
"team_deletion_not_allowed": "Önnek nem engedélyezett ennek a csapatnak a törlése.",
|
"team_deletion_not_allowed": "Önnek nem engedélyezett ennek a csapatnak a törlése.",
|
||||||
"team_name": "Csapat neve",
|
"team_name": "Csapat neve",
|
||||||
"team_name_settings_title": "{teamName} beállításai",
|
"team_name_settings_title": "{teamName} beállításai",
|
||||||
@@ -1218,7 +1129,7 @@
|
|||||||
"copy_survey_error": "Nem sikerült másolni a kérdőívet",
|
"copy_survey_error": "Nem sikerült másolni a kérdőívet",
|
||||||
"copy_survey_link_to_clipboard": "Kérdőív hivatkozásának másolása a vágólapra",
|
"copy_survey_link_to_clipboard": "Kérdőív hivatkozásának másolása a vágólapra",
|
||||||
"copy_survey_partially_success": "{success} kérdőív sikeresen másolva, {error} sikertelen.",
|
"copy_survey_partially_success": "{success} kérdőív sikeresen másolva, {error} sikertelen.",
|
||||||
"copy_survey_success": "A kérdőív sikeresen másolva",
|
"copy_survey_success": "A kérdőív sikeresen másolva!",
|
||||||
"delete_survey_and_responses_warning": "Biztosan törölni szeretné ezt a kérdőívet és az összes válaszát?",
|
"delete_survey_and_responses_warning": "Biztosan törölni szeretné ezt a kérdőívet és az összes válaszát?",
|
||||||
"edit": {
|
"edit": {
|
||||||
"1_choose_the_default_language_for_this_survey": "1. Válassza ki a kérdőív alapértelmezett nyelvét:",
|
"1_choose_the_default_language_for_this_survey": "1. Válassza ki a kérdőív alapértelmezett nyelvét:",
|
||||||
@@ -1242,6 +1153,7 @@
|
|||||||
"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": "Külső szegély hozzáadása a kérdőív kártyájához.",
|
||||||
"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",
|
||||||
@@ -1280,7 +1192,6 @@
|
|||||||
"block_duplicated": "A blokk kettőzve.",
|
"block_duplicated": "A blokk kettőzve.",
|
||||||
"bold": "Félkövér",
|
"bold": "Félkövér",
|
||||||
"brand_color": "Márkajel színe",
|
"brand_color": "Márkajel színe",
|
||||||
"brand_color_description": "Gombokra, hivatkozásokra és kiemelésekre alkalmazva.",
|
|
||||||
"brightness": "Fényerő",
|
"brightness": "Fényerő",
|
||||||
"bulk_edit": "Tömeges szerkesztés",
|
"bulk_edit": "Tömeges szerkesztés",
|
||||||
"bulk_edit_description": "Az összes lenti lehetőség szerkesztése, soronként egy. Az üres sorok kihagyásra kerülnek, az ismétlődések pedig el lesznek távolítva.",
|
"bulk_edit_description": "Az összes lenti lehetőség szerkesztése, soronként egy. Az üres sorok kihagyásra kerülnek, az ismétlődések pedig el lesznek távolítva.",
|
||||||
@@ -1298,9 +1209,7 @@
|
|||||||
"capture_new_action": "Új művelet rögzítése",
|
"capture_new_action": "Új művelet rögzítése",
|
||||||
"card_arrangement_for_survey_type_derived": "Kártyaelrendezés a(z) {surveyTypeDerived} kérdőíveknél",
|
"card_arrangement_for_survey_type_derived": "Kártyaelrendezés a(z) {surveyTypeDerived} kérdőíveknél",
|
||||||
"card_background_color": "Kártya hátterének színe",
|
"card_background_color": "Kártya hátterének színe",
|
||||||
"card_background_color_description": "Kitölti a kérdőívkártya területét.",
|
|
||||||
"card_border_color": "Kártya szegélyének színe",
|
"card_border_color": "Kártya szegélyének színe",
|
||||||
"card_border_color_description": "Körberajzolja a kérdőívkártyát.",
|
|
||||||
"card_styling": "Kártya stílusának beállítása",
|
"card_styling": "Kártya stílusának beállítása",
|
||||||
"casual": "Alkalmi",
|
"casual": "Alkalmi",
|
||||||
"caution_edit_duplicate": "Kettőzés és szerkesztés",
|
"caution_edit_duplicate": "Kettőzés és szerkesztés",
|
||||||
@@ -1311,12 +1220,20 @@
|
|||||||
"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.",
|
||||||
"caution_recommendation": "Ez adatellentmondásokat okozhat a kérdőív összegzésében. Azt javasoljuk, hogy inkább kettőzze meg a kérdőívet.",
|
"caution_recommendation": "Ez adatellentmondásokat okozhat a kérdőív összegzésében. Azt javasoljuk, hogy inkább kettőzze meg a kérdőívet.",
|
||||||
"caution_text": "A változtatások következetlenségekhez vezetnek",
|
"caution_text": "A változtatások következetlenségekhez vezetnek",
|
||||||
|
"centered_modal_overlay_color": "Középre helyezett kizárólagos rátét színe",
|
||||||
"change_anyway": "Változtatás mindenképp",
|
"change_anyway": "Változtatás mindenképp",
|
||||||
"change_background": "Háttér megváltoztatása",
|
"change_background": "Háttér megváltoztatása",
|
||||||
"change_question_type": "Kérdés típusának megváltoztatása",
|
"change_question_type": "Kérdés típusának megváltoztatása",
|
||||||
"change_survey_type": "A kérdőív típusának megváltoztatása befolyásolja a meglévő hozzáférést",
|
"change_survey_type": "A kérdőív típusának megváltoztatása befolyásolja a meglévő hozzáférést",
|
||||||
|
"change_the_background_color_of_the_card": "A kártya háttérszínének megváltoztatása.",
|
||||||
|
"change_the_background_color_of_the_input_fields": "A beviteli mezők háttérszínének megváltoztatása.",
|
||||||
"change_the_background_to_a_color_image_or_animation": "A háttér megváltoztatása színre, képre vagy animációra.",
|
"change_the_background_to_a_color_image_or_animation": "A háttér megváltoztatása színre, képre vagy animációra.",
|
||||||
|
"change_the_border_color_of_the_card": "A kártya szegélyszínének megváltoztatása.",
|
||||||
|
"change_the_border_color_of_the_input_fields": "A beviteli mezők szegélyszínének megváltoztatása.",
|
||||||
|
"change_the_border_radius_of_the_card_and_the_inputs": "A kártya és a beviteli mezők szegélysugarának megváltoztatása.",
|
||||||
|
"change_the_brand_color_of_the_survey": "A kérdőív márkajelszínének megváltoztatása.",
|
||||||
"change_the_placement_of_this_survey": "A kérdőív elhelyezésének megváltoztatása.",
|
"change_the_placement_of_this_survey": "A kérdőív elhelyezésének megváltoztatása.",
|
||||||
|
"change_the_question_color_of_the_survey": "A kérdőív kérdésszínének megváltoztatása.",
|
||||||
"changes_saved": "Változtatások elmentve.",
|
"changes_saved": "Változtatások elmentve.",
|
||||||
"changing_survey_type_will_remove_existing_distribution_channels": "A kérdőív típusának megváltoztatása hatással lesz arra, hogy hogyan lehet megosztani azt. Ha a válaszadók már rendelkeznek a jelenlegi típushoz tartozó hozzáférési hivatkozásokkal, akkor elveszíthetik a hozzáférést a váltás után.",
|
"changing_survey_type_will_remove_existing_distribution_channels": "A kérdőív típusának megváltoztatása hatással lesz arra, hogy hogyan lehet megosztani azt. Ha a válaszadók már rendelkeznek a jelenlegi típushoz tartozó hozzáférési hivatkozásokkal, akkor elveszíthetik a hozzáférést a váltás után.",
|
||||||
"checkbox_label": "Jelölőnégyzet címkéje",
|
"checkbox_label": "Jelölőnégyzet címkéje",
|
||||||
@@ -1356,7 +1273,7 @@
|
|||||||
"disable_the_visibility_of_survey_progress": "A kérdőív előrehaladási folyamata láthatóságának letiltása.",
|
"disable_the_visibility_of_survey_progress": "A kérdőív előrehaladási folyamata láthatóságának letiltása.",
|
||||||
"display_an_estimate_of_completion_time_for_survey": "A kérdőív becsült kitöltési idejének megjelenítése",
|
"display_an_estimate_of_completion_time_for_survey": "A kérdőív becsült kitöltési idejének megjelenítése",
|
||||||
"display_number_of_responses_for_survey": "A kérdőív válaszai számának megjelenítése",
|
"display_number_of_responses_for_survey": "A kérdőív válaszai számának megjelenítése",
|
||||||
"display_type": "Megjelenített típus",
|
"display_type": "Megjelenítési típus",
|
||||||
"divide": "Osztás /",
|
"divide": "Osztás /",
|
||||||
"does_not_contain": "Nem tartalmazza",
|
"does_not_contain": "Nem tartalmazza",
|
||||||
"does_not_end_with": "Nem ezzel végződik",
|
"does_not_end_with": "Nem ezzel végződik",
|
||||||
@@ -1364,7 +1281,7 @@
|
|||||||
"does_not_include_all_of": "Nem tartalmazza ezekből az összeset",
|
"does_not_include_all_of": "Nem tartalmazza ezekből az összeset",
|
||||||
"does_not_include_one_of": "Nem tartalmazza ezek egyikét",
|
"does_not_include_one_of": "Nem tartalmazza ezek egyikét",
|
||||||
"does_not_start_with": "Nem ezzel kezdődik",
|
"does_not_start_with": "Nem ezzel kezdődik",
|
||||||
"dropdown": "Legördülő",
|
"dropdown": "Legördülő menü",
|
||||||
"duplicate_block": "Blokk kettőzése",
|
"duplicate_block": "Blokk kettőzése",
|
||||||
"duplicate_question": "Kérdés kettőzése",
|
"duplicate_question": "Kérdés kettőzése",
|
||||||
"edit_link": "Hivatkozás szerkesztése",
|
"edit_link": "Hivatkozás szerkesztése",
|
||||||
@@ -1456,6 +1373,7 @@
|
|||||||
"hide_progress_bar": "Folyamatjelző elrejtése",
|
"hide_progress_bar": "Folyamatjelző elrejtése",
|
||||||
"hide_question_settings": "Kérdésbeállítások elrejtése",
|
"hide_question_settings": "Kérdésbeállítások elrejtése",
|
||||||
"hostname": "Gépnév",
|
"hostname": "Gépnév",
|
||||||
|
"how_funky_do_you_want_your_cards_in_survey_type_derived_surveys": "Mennyire szeretné vagánnyá tenni a kártyáit a(z) {surveyTypeDerived} kérdőívekben",
|
||||||
"if_you_need_more_please": "Ha többre van szüksége, akkor",
|
"if_you_need_more_please": "Ha többre van szüksége, akkor",
|
||||||
"if_you_really_want_that_answer_ask_until_you_get_it": "Maradjon megjelenítve bármikor is aktiválódott, amíg egy választ el nem küldenek.",
|
"if_you_really_want_that_answer_ask_until_you_get_it": "Maradjon megjelenítve bármikor is aktiválódott, amíg egy választ el nem küldenek.",
|
||||||
"ignore_global_waiting_time": "Várakozási időszak figyelmen kívül hagyása",
|
"ignore_global_waiting_time": "Várakozási időszak figyelmen kívül hagyása",
|
||||||
@@ -1466,9 +1384,7 @@
|
|||||||
"initial_value": "Kezdeti érték",
|
"initial_value": "Kezdeti érték",
|
||||||
"inner_text": "Belső szöveg",
|
"inner_text": "Belső szöveg",
|
||||||
"input_border_color": "Beviteli mező szegélyének színe",
|
"input_border_color": "Beviteli mező szegélyének színe",
|
||||||
"input_border_color_description": "Körberajzolja a szöveges beviteli mezőket és a szövegdobozokat.",
|
|
||||||
"input_color": "Beviteli mező színe",
|
"input_color": "Beviteli mező színe",
|
||||||
"input_color_description": "Kitölti a szöveges beviteli mezők belsejét.",
|
|
||||||
"insert_link": "Hivatkozás beszúrása",
|
"insert_link": "Hivatkozás beszúrása",
|
||||||
"invalid_targeting": "Érvénytelen célzás: ellenőrizze a közönség szűrőit",
|
"invalid_targeting": "Érvénytelen célzás: ellenőrizze a közönség szűrőit",
|
||||||
"invalid_video_url_warning": "Adjon meg egy érvényes YouTube, Vimeo vagy Loom URL-t. Jelenleg nem támogatunk más videomegosztó szolgáltatókat.",
|
"invalid_video_url_warning": "Adjon meg egy érvényes YouTube, Vimeo vagy Loom URL-t. Jelenleg nem támogatunk más videomegosztó szolgáltatókat.",
|
||||||
@@ -1552,6 +1468,7 @@
|
|||||||
"protect_survey_with_pin_description": "Csak a PIN-kóddal rendelkező felhasználók férhetnek hozzá a kérdőívhez.",
|
"protect_survey_with_pin_description": "Csak a PIN-kóddal rendelkező felhasználók férhetnek hozzá a kérdőívhez.",
|
||||||
"publish": "Közzététel",
|
"publish": "Közzététel",
|
||||||
"question": "Kérdés",
|
"question": "Kérdés",
|
||||||
|
"question_color": "Kérdés színe",
|
||||||
"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",
|
||||||
@@ -1613,7 +1530,6 @@
|
|||||||
"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 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",
|
||||||
@@ -1629,7 +1545,7 @@
|
|||||||
"send_survey_to_audience_who_match": "Kérdőív küldése az erre illeszkedő közönségnek…",
|
"send_survey_to_audience_who_match": "Kérdőív küldése az erre illeszkedő közönségnek…",
|
||||||
"send_your_respondents_to_a_page_of_your_choice": "A válaszadók küldése a választási lehetőség oldalára.",
|
"send_your_respondents_to_a_page_of_your_choice": "A válaszadók küldése a választási lehetőség oldalára.",
|
||||||
"set_the_global_placement_in_the_look_feel_settings": "A globális elhelyezés beállítása a megjelenítési beállításokban.",
|
"set_the_global_placement_in_the_look_feel_settings": "A globális elhelyezés beállítása a megjelenítési beállításokban.",
|
||||||
"settings_saved_successfully": "A beállítások sikeresen elmentve",
|
"settings_saved_successfully": "A beállítások sikeresen elmentve.",
|
||||||
"seven_points": "7 pont",
|
"seven_points": "7 pont",
|
||||||
"show_block_settings": "Blokkbeállítások megjelenítése",
|
"show_block_settings": "Blokkbeállítások megjelenítése",
|
||||||
"show_button": "Gomb megjelenítése",
|
"show_button": "Gomb megjelenítése",
|
||||||
@@ -1655,6 +1571,7 @@
|
|||||||
"styling_set_to_theme_styles": "A stílus a téma stílusaira állítva",
|
"styling_set_to_theme_styles": "A stílus a téma stílusaira állítva",
|
||||||
"subheading": "Alcím",
|
"subheading": "Alcím",
|
||||||
"subtract": "Kivonás -",
|
"subtract": "Kivonás -",
|
||||||
|
"suggest_colors": "Színek ajánlása",
|
||||||
"survey_completed_heading": "A kérdőív kitöltve",
|
"survey_completed_heading": "A kérdőív kitöltve",
|
||||||
"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",
|
||||||
@@ -1795,7 +1712,7 @@
|
|||||||
"person_attributes": "A személy jellemzői a beküldés időpontjában",
|
"person_attributes": "A személy jellemzői a beküldés időpontjában",
|
||||||
"phone": "Telefon",
|
"phone": "Telefon",
|
||||||
"respondent_skipped_questions": "A válaszadó kihagyta ezeket a kérdéseket.",
|
"respondent_skipped_questions": "A válaszadó kihagyta ezeket a kérdéseket.",
|
||||||
"response_deleted_successfully": "A válasz sikeresen törölve",
|
"response_deleted_successfully": "A válasz sikeresen törölve.",
|
||||||
"single_use_id": "Egyszer használatos azonosító",
|
"single_use_id": "Egyszer használatos azonosító",
|
||||||
"source": "Forrás",
|
"source": "Forrás",
|
||||||
"state_region": "Állam vagy régió",
|
"state_region": "Állam vagy régió",
|
||||||
@@ -1807,7 +1724,7 @@
|
|||||||
"search_by_survey_name": "Keresés kérőí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ót, akkor a „suid=…” bármilyen értéke működik egy válasznál.",
|
||||||
"custom_single_use_id_title": "Bármilyen értéket beállíthat egyszer használatos azonosítóként az URL-ben.",
|
"custom_single_use_id_title": "Bármilyen értéket beállíthat egyszer használatos azonosítóként az URL-ben.",
|
||||||
"custom_start_point": "Egyéni kezdési pont",
|
"custom_start_point": "Egyéni kezdési pont",
|
||||||
"data_prefilling": "Adatok előre kitöltése",
|
"data_prefilling": "Adatok előre kitöltése",
|
||||||
@@ -1957,7 +1874,6 @@
|
|||||||
"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": "Benyomások",
|
"impressions": "Benyomások",
|
||||||
"impressions_identified_only": "Csak az azonosított kapcsolatok megjelenítései láthatók",
|
|
||||||
"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",
|
||||||
@@ -2000,7 +1916,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 megjelenítések azonosított kapcsolatoktól",
|
|
||||||
"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",
|
||||||
@@ -2034,8 +1949,8 @@
|
|||||||
"your_survey_is_public": "A kérdőíve nyilvános",
|
"your_survey_is_public": "A kérdőíve nyilvános",
|
||||||
"youre_not_plugged_in_yet": "Még nincs csatlakoztatva!"
|
"youre_not_plugged_in_yet": "Még nincs csatlakoztatva!"
|
||||||
},
|
},
|
||||||
"survey_deleted_successfully": "A kérdőív sikeresen törölve",
|
"survey_deleted_successfully": "A kérdőív sikeresen törölve!",
|
||||||
"survey_duplicated_successfully": "A kérdőív sikeresen megkettőzve",
|
"survey_duplicated_successfully": "A kérdőív sikeresen megkettőzve.",
|
||||||
"survey_duplication_error": "Nem sikerült megkettőzni a kérdőívet.",
|
"survey_duplication_error": "Nem sikerült megkettőzni a kérdőívet.",
|
||||||
"templates": {
|
"templates": {
|
||||||
"all_channels": "Összes csatorna",
|
"all_channels": "Összes csatorna",
|
||||||
@@ -2098,8 +2013,8 @@
|
|||||||
"custom_scripts_updated_successfully": "Az egyéni parancsfájlok sikeres frissítve",
|
"custom_scripts_updated_successfully": "Az egyéni parancsfájlok sikeres frissítve",
|
||||||
"custom_scripts_warning": "A parancsfájlok teljes böngésző-hozzáféréssel kerülnek végrehajtásra. Csak megbízható forrásokból származó parancsfájlokat adjon hozzá.",
|
"custom_scripts_warning": "A parancsfájlok teljes böngésző-hozzáféréssel kerülnek végrehajtásra. Csak megbízható forrásokból származó parancsfájlokat adjon hozzá.",
|
||||||
"delete_workspace": "Munkaterület törlése",
|
"delete_workspace": "Munkaterület törlése",
|
||||||
"delete_workspace_confirmation": "Biztosan törölni szeretné a(z) {projectName} munkaterületet? Ezt a műveletet nem lehet visszavonni.",
|
"delete_workspace_confirmation": "Biztosan törölni szeretné a(z) {projectName} projektet? Ezt a műveletet nem lehet visszavonni.",
|
||||||
"delete_workspace_name_includes_surveys_responses_people_and_more": "A(z) {projectName} munkaterület törlése, beleértve az összes kérdőívet, választ, személyt, műveletet és attribútumot is.",
|
"delete_workspace_name_includes_surveys_responses_people_and_more": "A(z) {projectName} projekt törlése, beleértve az összes kérdőívet, választ, személyt, műveletet és attribútumot is.",
|
||||||
"delete_workspace_settings_description": "A munkaterület törlése az összes kérdőívvel, válasszal, személlyel, művelettel és attribútummal együtt. Ezt nem lehet visszavonni.",
|
"delete_workspace_settings_description": "A munkaterület törlése az összes kérdőívvel, válasszal, személlyel, művelettel és attribútummal együtt. Ezt nem lehet visszavonni.",
|
||||||
"error_saving_workspace_information": "Hiba a munkaterület-információk mentésekor",
|
"error_saving_workspace_information": "Hiba a munkaterület-információk mentésekor",
|
||||||
"only_owners_or_managers_can_delete_workspaces": "Csak tulajdonosok vagy kezelők törölhetnek munkaterületeket",
|
"only_owners_or_managers_can_delete_workspaces": "Csak tulajdonosok vagy kezelők törölhetnek munkaterületeket",
|
||||||
@@ -2140,71 +2055,9 @@
|
|||||||
"look": {
|
"look": {
|
||||||
"add_background_color": "Háttérszín hozzáadása",
|
"add_background_color": "Háttérszín hozzáadása",
|
||||||
"add_background_color_description": "Hátérszín hozzáadása a logó tárolódobozához.",
|
"add_background_color_description": "Hátérszín hozzáadása a logó tárolódobozához.",
|
||||||
"advanced_styling_field_border_radius": "Szegély sugara",
|
|
||||||
"advanced_styling_field_button_bg": "Gomb háttere",
|
|
||||||
"advanced_styling_field_button_bg_description": "Kitölti a „Következő” és az „Elküldés” gombokat.",
|
|
||||||
"advanced_styling_field_button_border_radius_description": "Lekerekíti a gomb sarkait.",
|
|
||||||
"advanced_styling_field_button_font_size_description": "Átméretezi a gomb címkéjének szövegét.",
|
|
||||||
"advanced_styling_field_button_font_weight_description": "Vékonyabbá vagy vastagabbá teszi a gomb szövegét.",
|
|
||||||
"advanced_styling_field_button_height_description": "A gomb magasságát vezérli.",
|
|
||||||
"advanced_styling_field_button_padding_x_description": "Térközt ad hozzá balra és jobbra.",
|
|
||||||
"advanced_styling_field_button_padding_y_description": "Térközt ad hozzá fent és lent.",
|
|
||||||
"advanced_styling_field_button_text": "Gomb szövege",
|
|
||||||
"advanced_styling_field_button_text_description": "Kiszínezi a gombokon belüli címkét.",
|
|
||||||
"advanced_styling_field_description_color": "Leírás színe",
|
|
||||||
"advanced_styling_field_description_color_description": "Kiszínezi az egyes címsorok alatti szöveget.",
|
|
||||||
"advanced_styling_field_description_size": "Leírás betűmérete",
|
|
||||||
"advanced_styling_field_description_size_description": "Átméretezi a leírás szövegét.",
|
|
||||||
"advanced_styling_field_description_weight": "Leírás betűvastagsága",
|
|
||||||
"advanced_styling_field_description_weight_description": "Vékonyabbá vagy vastagabbá teszi a leírás szövegét.",
|
|
||||||
"advanced_styling_field_font_size": "Betűméret",
|
|
||||||
"advanced_styling_field_font_weight": "Betűvastagság",
|
|
||||||
"advanced_styling_field_headline_color": "Címsor színe",
|
|
||||||
"advanced_styling_field_headline_color_description": "Kiszínezi a fő kérdés szövegét.",
|
|
||||||
"advanced_styling_field_headline_size": "Címsor betűmérete",
|
|
||||||
"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_description": "Vékonyabbá vagy vastagabbá teszi a címsor szövegét.",
|
|
||||||
"advanced_styling_field_height": "Minimális magasság",
|
|
||||||
"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_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_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_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_shadow_description": "Vetett árnyékot ad hozzá a beviteli mezők köré.",
|
|
||||||
"advanced_styling_field_input_text": "Beviteli mező szövege",
|
|
||||||
"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_description": "Kitölti a választási lehetőség elemeit.",
|
|
||||||
"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_label": "Címke színe",
|
|
||||||
"advanced_styling_field_option_label_description": "Kiszínezi a választási lehetőség címkéjének szövegét.",
|
|
||||||
"advanced_styling_field_option_padding_x_description": "Térközt ad hozzá balra és jobbra.",
|
|
||||||
"advanced_styling_field_option_padding_y_description": "Térközt ad hozzá fent és lent.",
|
|
||||||
"advanced_styling_field_padding_x": "X kitöltés",
|
|
||||||
"advanced_styling_field_padding_y": "Y kitöltés",
|
|
||||||
"advanced_styling_field_placeholder_opacity": "Helykitöltő átlátszatlansága",
|
|
||||||
"advanced_styling_field_shadow": "Árnyék",
|
|
||||||
"advanced_styling_field_track_bg": "Követés háttere",
|
|
||||||
"advanced_styling_field_track_bg_description": "Kiszínezi a sáv kitöltetlen részét.",
|
|
||||||
"advanced_styling_field_track_height": "Követés magassága",
|
|
||||||
"advanced_styling_field_track_height_description": "A folyamatjelző vastagságát vezérli.",
|
|
||||||
"advanced_styling_field_upper_label_color": "Címsor címkéjének színe",
|
|
||||||
"advanced_styling_field_upper_label_color_description": "Kiszínezi a beviteli mezők fölötti kis címkéket.",
|
|
||||||
"advanced_styling_field_upper_label_size": "Címsor címkéjének betűmérete",
|
|
||||||
"advanced_styling_field_upper_label_size_description": "Átméretezi a beviteli mezők fölötti kis címkéket.",
|
|
||||||
"advanced_styling_field_upper_label_weight": "Címsor címkéjének betűvastagsága",
|
|
||||||
"advanced_styling_field_upper_label_weight_description": "Vékonyabbá vagy vastagabbá teszi a címkét.",
|
|
||||||
"advanced_styling_section_buttons": "Gombok",
|
|
||||||
"advanced_styling_section_headlines": "Címsorok és leírások",
|
|
||||||
"advanced_styling_section_inputs": "Beviteli mezők",
|
|
||||||
"advanced_styling_section_options": "Lehetőségek (rádiógomb vagy jelölőnégyzet)",
|
|
||||||
"app_survey_placement": "Alkalmazás-kérdőív elhelyezése",
|
"app_survey_placement": "Alkalmazás-kérdőív elhelyezése",
|
||||||
"app_survey_placement_settings_description": "Annak megváltoztatása, hogy a kérdőívek hol jelennek meg a webalkalmazásban vagy a webhelyen.",
|
"app_survey_placement_settings_description": "Annak megváltoztatása, hogy a kérdőívek hol jelennek meg a webalkalmazásban vagy a webhelyen.",
|
||||||
|
"centered_modal_overlay_color": "Középre helyezett kizárólagos rátét színe",
|
||||||
"email_customization": "E-mail személyre szabás",
|
"email_customization": "E-mail személyre szabás",
|
||||||
"email_customization_description": "Azon e-mailek megjelenésének megváltoztatása, amelyeket a Formbricks az Ön nevében küld ki.",
|
"email_customization_description": "Azon e-mailek megjelenésének megváltoztatása, amelyeket a Formbricks az Ön nevében küld ki.",
|
||||||
"enable_custom_styling": "Egyéni stílus engedélyezése",
|
"enable_custom_styling": "Egyéni stílus engedélyezése",
|
||||||
@@ -2215,9 +2068,6 @@
|
|||||||
"formbricks_branding_hidden": "A Formbricks márkajel rejtve van.",
|
"formbricks_branding_hidden": "A Formbricks márkajel rejtve van.",
|
||||||
"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_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?",
|
|
||||||
"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.",
|
||||||
"logo_updated_successfully": "A logó sikeresen frissítve",
|
"logo_updated_successfully": "A logó sikeresen frissítve",
|
||||||
@@ -2232,8 +2082,6 @@
|
|||||||
"show_formbricks_branding_in": "Formbricks márkajel megjelenítése a(z) {type} kérdőívekben",
|
"show_formbricks_branding_in": "Formbricks márkajel megjelenítése a(z) {type} kérdőívekben",
|
||||||
"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",
|
|
||||||
"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."
|
||||||
},
|
},
|
||||||
@@ -2249,9 +2097,9 @@
|
|||||||
"search_tags": "Címkék keresése…",
|
"search_tags": "Címkék keresése…",
|
||||||
"tag": "Címke",
|
"tag": "Címke",
|
||||||
"tag_already_exists": "A címke már létezik",
|
"tag_already_exists": "A címke már létezik",
|
||||||
"tag_deleted": "A címke sikeresen törölve",
|
"tag_deleted": "Címke törölve",
|
||||||
"tag_updated": "A címke sikeresen frissítve",
|
"tag_updated": "Címke frissítve",
|
||||||
"tags_merged": "A címkék sikeresen egyesítve"
|
"tags_merged": "Címkék egyesítve"
|
||||||
},
|
},
|
||||||
"teams": {
|
"teams": {
|
||||||
"manage_teams": "Csapatok kezelése",
|
"manage_teams": "Csapatok kezelése",
|
||||||
@@ -2446,7 +2294,7 @@
|
|||||||
"career_development_survey_question_5_choice_5": "Üzemeltetés",
|
"career_development_survey_question_5_choice_5": "Üzemeltetés",
|
||||||
"career_development_survey_question_5_choice_6": "Egyéb",
|
"career_development_survey_question_5_choice_6": "Egyéb",
|
||||||
"career_development_survey_question_5_headline": "Milyen funkcióban dolgozik?",
|
"career_development_survey_question_5_headline": "Milyen funkcióban dolgozik?",
|
||||||
"career_development_survey_question_5_subheader": "Válassza ki a következő lehetőségek egyikét:",
|
"career_development_survey_question_5_subheader": "Válassza a következők egyikét",
|
||||||
"career_development_survey_question_6_choice_1": "Egyéni közreműködő",
|
"career_development_survey_question_6_choice_1": "Egyéni közreműködő",
|
||||||
"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ó",
|
||||||
@@ -2454,7 +2302,7 @@
|
|||||||
"career_development_survey_question_6_choice_5": "Igazgató",
|
"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 a következők egyikét",
|
||||||
"cess_survey_name": "Ügyfél-erőfeszítési pontszám kérdőív",
|
"cess_survey_name": "Ügyfél-erőfeszítési pontszám kérdőív",
|
||||||
"cess_survey_question_1_headline": "A(z) $[projectName] megkönnyíti számomra a [CÉL HOZZÁADÁSA] tevékenységet",
|
"cess_survey_question_1_headline": "A(z) $[projectName] megkönnyíti számomra a [CÉL HOZZÁADÁSA] tevékenységet",
|
||||||
"cess_survey_question_1_lower_label": "Egyáltalán nem értek egyet",
|
"cess_survey_question_1_lower_label": "Egyáltalán nem értek egyet",
|
||||||
@@ -2997,7 +2845,6 @@
|
|||||||
"preview_survey_question_2_choice_1_label": "Igen, folyamatosan tájékoztassanak.",
|
"preview_survey_question_2_choice_1_label": "Igen, folyamatosan tájékoztassanak.",
|
||||||
"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_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",
|
||||||
|
|||||||
+24
-177
@@ -188,7 +188,6 @@
|
|||||||
"customer_success": "カスタマーサクセス",
|
"customer_success": "カスタマーサクセス",
|
||||||
"dark_overlay": "暗いオーバーレイ",
|
"dark_overlay": "暗いオーバーレイ",
|
||||||
"date": "日付",
|
"date": "日付",
|
||||||
"days": "日",
|
|
||||||
"default": "デフォルト",
|
"default": "デフォルト",
|
||||||
"delete": "削除",
|
"delete": "削除",
|
||||||
"description": "説明",
|
"description": "説明",
|
||||||
@@ -218,7 +217,6 @@
|
|||||||
"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": "行を展開",
|
||||||
@@ -277,7 +275,6 @@
|
|||||||
"mobile_overlay_app_works_best_on_desktop": "Formbricks は より 大きな 画面 で最適に 作動します。 フォーム を 管理または 構築する には、 別の デバイス に 切り替える 必要が あります。",
|
"mobile_overlay_app_works_best_on_desktop": "Formbricks は より 大きな 画面 で最適に 作動します。 フォーム を 管理または 構築する には、 別の デバイス に 切り替える 必要が あります。",
|
||||||
"mobile_overlay_surveys_look_good": "ご安心ください - お使い の デバイス や 画面 サイズ に 関係なく、 フォーム は 素晴らしく 見えます!",
|
"mobile_overlay_surveys_look_good": "ご安心ください - お使い の デバイス や 画面 サイズ に 関係なく、 フォーム は 素晴らしく 見えます!",
|
||||||
"mobile_overlay_title": "おっと、 小さな 画面 が 検出されました!",
|
"mobile_overlay_title": "おっと、 小さな 画面 が 検出されました!",
|
||||||
"months": "ヶ月",
|
|
||||||
"move_down": "下に移動",
|
"move_down": "下に移動",
|
||||||
"move_up": "上に移動",
|
"move_up": "上に移動",
|
||||||
"multiple_languages": "多言語",
|
"multiple_languages": "多言語",
|
||||||
@@ -288,7 +285,6 @@
|
|||||||
"no_background_image_found": "背景画像が見つかりません。",
|
"no_background_image_found": "背景画像が見つかりません。",
|
||||||
"no_code": "ノーコード",
|
"no_code": "ノーコード",
|
||||||
"no_files_uploaded": "ファイルがアップロードされていません",
|
"no_files_uploaded": "ファイルがアップロードされていません",
|
||||||
"no_overlay": "オーバーレイなし",
|
|
||||||
"no_quotas_found": "クォータが見つかりません",
|
"no_quotas_found": "クォータが見つかりません",
|
||||||
"no_result_found": "結果が見つかりません",
|
"no_result_found": "結果が見つかりません",
|
||||||
"no_results": "結果なし",
|
"no_results": "結果なし",
|
||||||
@@ -315,7 +311,6 @@
|
|||||||
"organization_teams_not_found": "組織のチームが見つかりません",
|
"organization_teams_not_found": "組織のチームが見つかりません",
|
||||||
"other": "その他",
|
"other": "その他",
|
||||||
"others": "その他",
|
"others": "その他",
|
||||||
"overlay_color": "オーバーレイの色",
|
|
||||||
"overview": "概要",
|
"overview": "概要",
|
||||||
"password": "パスワード",
|
"password": "パスワード",
|
||||||
"paused": "一時停止",
|
"paused": "一時停止",
|
||||||
@@ -355,7 +350,6 @@
|
|||||||
"request_trial_license": "トライアルライセンスをリクエスト",
|
"request_trial_license": "トライアルライセンスをリクエスト",
|
||||||
"reset_to_default": "デフォルトにリセット",
|
"reset_to_default": "デフォルトにリセット",
|
||||||
"response": "回答",
|
"response": "回答",
|
||||||
"response_id": "回答ID",
|
|
||||||
"responses": "回答",
|
"responses": "回答",
|
||||||
"restart": "再開",
|
"restart": "再開",
|
||||||
"role": "役割",
|
"role": "役割",
|
||||||
@@ -396,7 +390,6 @@
|
|||||||
"status": "ステータス",
|
"status": "ステータス",
|
||||||
"step_by_step_manual": "ステップバイステップマニュアル",
|
"step_by_step_manual": "ステップバイステップマニュアル",
|
||||||
"storage_not_configured": "ファイルストレージが設定されていないため、アップロードは失敗する可能性があります",
|
"storage_not_configured": "ファイルストレージが設定されていないため、アップロードは失敗する可能性があります",
|
||||||
"string": "テキスト",
|
|
||||||
"styling": "スタイル",
|
"styling": "スタイル",
|
||||||
"submit": "送信",
|
"submit": "送信",
|
||||||
"summary": "概要",
|
"summary": "概要",
|
||||||
@@ -429,7 +422,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": "更新済み",
|
||||||
@@ -453,7 +445,6 @@
|
|||||||
"website_and_app_connection": "ウェブサイト&アプリ接続",
|
"website_and_app_connection": "ウェブサイト&アプリ接続",
|
||||||
"website_app_survey": "ウェブサイト&アプリフォーム",
|
"website_app_survey": "ウェブサイト&アプリフォーム",
|
||||||
"website_survey": "ウェブサイトフォーム",
|
"website_survey": "ウェブサイトフォーム",
|
||||||
"weeks": "週間",
|
|
||||||
"welcome_card": "ウェルカムカード",
|
"welcome_card": "ウェルカムカード",
|
||||||
"workspace_configuration": "ワークスペース設定",
|
"workspace_configuration": "ワークスペース設定",
|
||||||
"workspace_created_successfully": "ワークスペースが正常に作成されました",
|
"workspace_created_successfully": "ワークスペースが正常に作成されました",
|
||||||
@@ -464,7 +455,6 @@
|
|||||||
"workspace_not_found": "ワークスペースが見つかりません",
|
"workspace_not_found": "ワークスペースが見つかりません",
|
||||||
"workspace_permission_not_found": "ワークスペースの権限が見つかりません",
|
"workspace_permission_not_found": "ワークスペースの権限が見つかりません",
|
||||||
"workspaces": "ワークスペース",
|
"workspaces": "ワークスペース",
|
||||||
"years": "年",
|
|
||||||
"you": "あなた",
|
"you": "あなた",
|
||||||
"you_are_downgraded_to_the_community_edition": "コミュニティ版にダウングレードされました。",
|
"you_are_downgraded_to_the_community_edition": "コミュニティ版にダウングレードされました。",
|
||||||
"you_are_not_authorized_to_perform_this_action": "このアクションを実行する権限がありません。",
|
"you_are_not_authorized_to_perform_this_action": "このアクションを実行する権限がありません。",
|
||||||
@@ -625,7 +615,7 @@
|
|||||||
"attribute_created_successfully": "属性を作成しました",
|
"attribute_created_successfully": "属性を作成しました",
|
||||||
"attribute_description": "説明",
|
"attribute_description": "説明",
|
||||||
"attribute_description_placeholder": "簡単な説明",
|
"attribute_description_placeholder": "簡単な説明",
|
||||||
"attribute_key": "キー",
|
"attribute_key": "属性",
|
||||||
"attribute_key_cannot_be_changed": "キーは作成後に変更できません",
|
"attribute_key_cannot_be_changed": "キーは作成後に変更できません",
|
||||||
"attribute_key_hint": "小文字のアルファベット、数字、アンダースコアのみ使用可能です。アルファベットで始める必要があります。",
|
"attribute_key_hint": "小文字のアルファベット、数字、アンダースコアのみ使用可能です。アルファベットで始める必要があります。",
|
||||||
"attribute_key_placeholder": "例: date_of_birth",
|
"attribute_key_placeholder": "例: date_of_birth",
|
||||||
@@ -636,45 +626,28 @@
|
|||||||
"attribute_updated_successfully": "属性を更新しました",
|
"attribute_updated_successfully": "属性を更新しました",
|
||||||
"attribute_value": "値",
|
"attribute_value": "値",
|
||||||
"attribute_value_placeholder": "属性値",
|
"attribute_value_placeholder": "属性値",
|
||||||
"attributes_msg_attribute_limit_exceeded": "最大制限の{limit}個の属性クラスを超えるため、{count}個の新しい属性を作成できませんでした。既存の属性は正常に更新されました。",
|
|
||||||
"attributes_msg_attribute_type_validation_error": "{error}(属性'{key}'のデータ型: {dataType})",
|
|
||||||
"attributes_msg_email_already_exists": "このメールアドレスはこの環境に既に存在するため、更新されませんでした。",
|
|
||||||
"attributes_msg_email_or_userid_required": "メールアドレスまたはユーザーIDのいずれかが必要です。既存の値は保持されました。",
|
|
||||||
"attributes_msg_new_attribute_created": "新しい属性'{key}'をタイプ'{dataType}'で作成しました",
|
|
||||||
"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": "カスタム属性",
|
|
||||||
"data_type": "データ型",
|
|
||||||
"data_type_cannot_be_changed": "データ型は作成後に変更できません",
|
|
||||||
"data_type_description": "この属性の保存方法とフィルタリング方法を選択してください",
|
|
||||||
"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": "属性を編集",
|
||||||
"edit_attribute_values_description": "この連絡先の特定の属性の値を変更します。",
|
"edit_attribute_values_description": "この連絡先の特定の属性の値を変更します。",
|
||||||
"edit_attributes": "属性を編集",
|
|
||||||
"edit_attributes_success": "連絡先属性が正常に更新されました",
|
"edit_attributes_success": "連絡先属性が正常に更新されました",
|
||||||
"generate_personal_link": "個人リンクを生成",
|
"generate_personal_link": "個人リンクを生成",
|
||||||
"generate_personal_link_description": "公開されたフォームを選択して、この連絡先用のパーソナライズされたリンクを生成します。",
|
"generate_personal_link_description": "公開されたフォームを選択して、この連絡先用のパーソナライズされたリンクを生成します。",
|
||||||
"invalid_csv_column_names": "無効なCSV列名: {columns}。新しい属性となる列名は、小文字、数字、アンダースコアのみを含み、文字で始まる必要があります。",
|
|
||||||
"invalid_date_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": "回答が見つかりません",
|
||||||
"not_provided": "提供されていません",
|
"not_provided": "提供されていません",
|
||||||
"number_value_required": "数値が必要です。この属性を削除するには削除ボタンを使用してください。",
|
|
||||||
"personal_link_generated": "個人リンクが正常に生成されました",
|
"personal_link_generated": "個人リンクが正常に生成されました",
|
||||||
"personal_link_generated_but_clipboard_failed": "個人用リンクは生成されましたが、クリップボードへのコピーに失敗しました: {url}",
|
"personal_link_generated_but_clipboard_failed": "個人用リンクは生成されましたが、クリップボードへのコピーに失敗しました: {url}",
|
||||||
"personal_survey_link": "個人調査リンク",
|
"personal_survey_link": "個人調査リンク",
|
||||||
@@ -683,24 +656,13 @@
|
|||||||
"search_contact": "連絡先を検索",
|
"search_contact": "連絡先を検索",
|
||||||
"select_a_survey": "フォームを選択",
|
"select_a_survey": "フォームを選択",
|
||||||
"select_attribute": "属性を選択",
|
"select_attribute": "属性を選択",
|
||||||
"select_attribute_key": "属性キーを選択",
|
|
||||||
"survey_viewed": "フォームを閲覧",
|
|
||||||
"survey_viewed_at": "閲覧日時",
|
|
||||||
"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_duplicate_mappings": "次の属性に重複したマッピングが見つかりました:{attributes}",
|
|
||||||
"upload_contacts_error_file_too_large": "ファイルサイズが最大制限の800KBを超えています",
|
|
||||||
"upload_contacts_error_generic": "連絡先のアップロード中にエラーが発生しました。後でもう一度お試しください。",
|
|
||||||
"upload_contacts_error_invalid_file_type": "CSVファイルをアップロードしてください",
|
|
||||||
"upload_contacts_error_no_valid_contacts": "アップロードされたCSVファイルには有効な連絡先が含まれていません。正しい形式についてはサンプルCSVファイルをご確認ください。",
|
|
||||||
"upload_contacts_modal_attribute_header": "Formbricks属性",
|
|
||||||
"upload_contacts_modal_attributes_description": "CSVの列をFormbricksの属性にマッピングします。",
|
"upload_contacts_modal_attributes_description": "CSVの列をFormbricksの属性にマッピングします。",
|
||||||
"upload_contacts_modal_attributes_new": "新しい属性",
|
"upload_contacts_modal_attributes_new": "新しい属性",
|
||||||
"upload_contacts_modal_attributes_search_or_add": "属性を検索または追加",
|
"upload_contacts_modal_attributes_search_or_add": "属性を検索または追加",
|
||||||
|
"upload_contacts_modal_attributes_should_be_mapped_to": "は以下にマッピングする必要があります",
|
||||||
"upload_contacts_modal_attributes_title": "属性",
|
"upload_contacts_modal_attributes_title": "属性",
|
||||||
"upload_contacts_modal_csv_column_header": "CSV列",
|
|
||||||
"upload_contacts_modal_description": "CSVをアップロードして、属性を持つ連絡先をすばやくインポート",
|
"upload_contacts_modal_description": "CSVをアップロードして、属性を持つ連絡先をすばやくインポート",
|
||||||
"upload_contacts_modal_download_example_csv": "CSVの例をダウンロード",
|
"upload_contacts_modal_download_example_csv": "CSVの例をダウンロード",
|
||||||
"upload_contacts_modal_duplicates_description": "連絡先がすでに存在する場合、どのように処理しますか?",
|
"upload_contacts_modal_duplicates_description": "連絡先がすでに存在する場合、どのように処理しますか?",
|
||||||
@@ -757,12 +719,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": "非表示フィールドを含める",
|
||||||
@@ -886,40 +843,6 @@
|
|||||||
"no_attributes_yet": "属性がまだありません!",
|
"no_attributes_yet": "属性がまだありません!",
|
||||||
"no_filters_yet": "フィルターはまだありません!",
|
"no_filters_yet": "フィルターはまだありません!",
|
||||||
"no_segments_yet": "保存されたセグメントはまだありません。",
|
"no_segments_yet": "保存されたセグメントはまだありません。",
|
||||||
"operator_contains": "を含む",
|
|
||||||
"operator_does_not_contain": "を含まない",
|
|
||||||
"operator_ends_with": "で終わる",
|
|
||||||
"operator_is_after": "より後",
|
|
||||||
"operator_is_before": "より前",
|
|
||||||
"operator_is_between": "の間である",
|
|
||||||
"operator_is_newer_than": "より新しい",
|
|
||||||
"operator_is_not_set": "設定されていない",
|
|
||||||
"operator_is_older_than": "より古い",
|
|
||||||
"operator_is_same_day": "同じ日である",
|
|
||||||
"operator_is_set": "設定されている",
|
|
||||||
"operator_starts_with": "で始まる",
|
|
||||||
"operator_title_contains": "を含む",
|
|
||||||
"operator_title_does_not_contain": "を含まない",
|
|
||||||
"operator_title_ends_with": "で終わる",
|
|
||||||
"operator_title_equals": "と等しい",
|
|
||||||
"operator_title_greater_equal": "以上",
|
|
||||||
"operator_title_greater_than": "より大きい",
|
|
||||||
"operator_title_is_after": "より後",
|
|
||||||
"operator_title_is_before": "より前",
|
|
||||||
"operator_title_is_between": "の間である",
|
|
||||||
"operator_title_is_newer_than": "より新しい",
|
|
||||||
"operator_title_is_not_set": "設定されていない",
|
|
||||||
"operator_title_is_older_than": "より古い",
|
|
||||||
"operator_title_is_same_day": "同じ日である",
|
|
||||||
"operator_title_is_set": "設定されている",
|
|
||||||
"operator_title_less_equal": "以下",
|
|
||||||
"operator_title_less_than": "より小さい",
|
|
||||||
"operator_title_not_equals": "等しくない",
|
|
||||||
"operator_title_starts_with": "で始まる",
|
|
||||||
"operator_title_user_is_in": "ユーザーが含まれる",
|
|
||||||
"operator_title_user_is_not_in": "ユーザーが含まれない",
|
|
||||||
"operator_user_is_in": "ユーザーが含まれる",
|
|
||||||
"operator_user_is_not_in": "ユーザーが含まれない",
|
|
||||||
"person_and_attributes": "人物と属性",
|
"person_and_attributes": "人物と属性",
|
||||||
"phone": "電話",
|
"phone": "電話",
|
||||||
"please_remove_the_segment_from_these_surveys_in_order_to_delete_it": "このセグメントを削除するには、まず以下のフォームから外してください。",
|
"please_remove_the_segment_from_these_surveys_in_order_to_delete_it": "このセグメントを削除するには、まず以下のフォームから外してください。",
|
||||||
@@ -944,7 +867,6 @@
|
|||||||
"user_targeting_is_currently_only_available_when": "ユーザーターゲティングは現在、利用条件を満たす場合のみ利用可能です",
|
"user_targeting_is_currently_only_available_when": "ユーザーターゲティングは現在、利用条件を満たす場合のみ利用可能です",
|
||||||
"value_cannot_be_empty": "値は空にできません。",
|
"value_cannot_be_empty": "値は空にできません。",
|
||||||
"value_must_be_a_number": "値は数値である必要があります。",
|
"value_must_be_a_number": "値は数値である必要があります。",
|
||||||
"value_must_be_positive": "値は正の数である必要があります。",
|
|
||||||
"view_filters": "フィルターを表示",
|
"view_filters": "フィルターを表示",
|
||||||
"where": "条件",
|
"where": "条件",
|
||||||
"with_the_formbricks_sdk": "Formbricks SDK を利用して"
|
"with_the_formbricks_sdk": "Formbricks SDK を利用して"
|
||||||
@@ -1031,32 +953,19 @@
|
|||||||
"enterprise_features": "エンタープライズ機能",
|
"enterprise_features": "エンタープライズ機能",
|
||||||
"get_an_enterprise_license_to_get_access_to_all_features": "すべての機能にアクセスするには、エンタープライズライセンスを取得してください。",
|
"get_an_enterprise_license_to_get_access_to_all_features": "すべての機能にアクセスするには、エンタープライズライセンスを取得してください。",
|
||||||
"keep_full_control_over_your_data_privacy_and_security": "データのプライバシーとセキュリティを完全に制御できます。",
|
"keep_full_control_over_your_data_privacy_and_security": "データのプライバシーとセキュリティを完全に制御できます。",
|
||||||
"license_invalid_description": "ENTERPRISE_LICENSE_KEY環境変数のライセンスキーが無効です。入力ミスがないか確認するか、新しいキーをリクエストしてください。",
|
|
||||||
"license_status": "ライセンスステータス",
|
|
||||||
"license_status_active": "有効",
|
|
||||||
"license_status_description": "エンタープライズライセンスのステータス。",
|
|
||||||
"license_status_expired": "期限切れ",
|
|
||||||
"license_status_invalid": "無効なライセンス",
|
|
||||||
"license_status_unreachable": "接続不可",
|
|
||||||
"license_unreachable_grace_period": "ライセンスサーバーに接続できません。エンタープライズ機能は{gracePeriodEnd}までの3日間の猶予期間中は引き続き利用できます。",
|
|
||||||
"no_call_needed_no_strings_attached_request_a_free_30_day_trial_license_to_test_all_features_by_filling_out_this_form": "電話不要、制約なし: このフォームに記入して、すべての機能をテストするための無料の30日間トライアルライセンスをリクエストしてください:",
|
"no_call_needed_no_strings_attached_request_a_free_30_day_trial_license_to_test_all_features_by_filling_out_this_form": "電話不要、制約なし: このフォームに記入して、すべての機能をテストするための無料の30日間トライアルライセンスをリクエストしてください:",
|
||||||
"no_credit_card_no_sales_call_just_test_it": "クレジットカード不要。営業電話もありません。ただテストしてください :)",
|
"no_credit_card_no_sales_call_just_test_it": "クレジットカード不要。営業電話もありません。ただテストしてください :)",
|
||||||
"on_request": "リクエストに応じて",
|
"on_request": "リクエストに応じて",
|
||||||
"organization_roles": "組織ロール(管理者、編集者、開発者など)",
|
"organization_roles": "組織ロール(管理者、編集者、開発者など)",
|
||||||
"questions_please_reach_out_to": "質問はありますか?こちらまでお問い合わせください",
|
"questions_please_reach_out_to": "質問はありますか?こちらまでお問い合わせください",
|
||||||
"recheck_license": "ライセンスを再確認",
|
|
||||||
"recheck_license_failed": "ライセンスの確認に失敗しました。ライセンスサーバーに接続できない可能性があります。",
|
|
||||||
"recheck_license_invalid": "ライセンスキーが無効です。ENTERPRISE_LICENSE_KEYを確認してください。",
|
|
||||||
"recheck_license_success": "ライセンスの確認に成功しました",
|
|
||||||
"recheck_license_unreachable": "ライセンスサーバーに接続できません。後ほど再度お試しください。",
|
|
||||||
"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": "サービスレベル契約",
|
||||||
"soc2_hipaa_iso_27001_compliance_check": "SOC2、HIPAA、ISO 27001準拠チェック",
|
"soc2_hipaa_iso_27001_compliance_check": "SOC2、HIPAA、ISO 27001準拠チェック",
|
||||||
"sso": "SSO(Google、Microsoft、OpenID Connect)",
|
"sso": "SSO(Google、Microsoft、OpenID Connect)",
|
||||||
"teams": "チーム&アクセスロール(読み取り、読み書き、管理)",
|
"teams": "チーム&アクセスロール(読み取り、読み書き、管理)",
|
||||||
"unlock_the_full_power_of_formbricks_free_for_30_days": "Formbricksの全機能をアンロック。30日間無料。"
|
"unlock_the_full_power_of_formbricks_free_for_30_days": "Formbricksの全機能をアンロック。30日間無料。",
|
||||||
|
"your_enterprise_license_is_active_all_features_unlocked": "あなたのエンタープライズライセンスは有効です。すべての機能がアンロックされました。"
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"bulk_invite_warning_description": "無料プランでは、すべての組織メンバーに常に「オーナー」ロールが割り当てられます。",
|
"bulk_invite_warning_description": "無料プランでは、すべての組織メンバーに常に「オーナー」ロールが割り当てられます。",
|
||||||
@@ -1080,7 +989,7 @@
|
|||||||
"from_your_organization": "あなたの組織から",
|
"from_your_organization": "あなたの組織から",
|
||||||
"invitation_sent_once_more": "招待状を再度送信しました。",
|
"invitation_sent_once_more": "招待状を再度送信しました。",
|
||||||
"invite_deleted_successfully": "招待を正常に削除しました",
|
"invite_deleted_successfully": "招待を正常に削除しました",
|
||||||
"invite_expires_on": "招待は{date}に期限切れ",
|
"invited_on": "{date}に招待",
|
||||||
"invites_failed": "招待に失敗しました",
|
"invites_failed": "招待に失敗しました",
|
||||||
"leave_organization": "組織を離れる",
|
"leave_organization": "組織を離れる",
|
||||||
"leave_organization_description": "この組織を離れ、すべてのフォームと回答へのアクセス権を失います。再度招待された場合にのみ再参加できます。",
|
"leave_organization_description": "この組織を離れ、すべてのフォームと回答へのアクセス権を失います。再度招待された場合にのみ再参加できます。",
|
||||||
@@ -1193,6 +1102,8 @@
|
|||||||
"please_fill_all_workspace_fields": "新しいワークスペースを追加するには、すべてのフィールドを入力してください。",
|
"please_fill_all_workspace_fields": "新しいワークスペースを追加するには、すべてのフィールドを入力してください。",
|
||||||
"read": "読み取り",
|
"read": "読み取り",
|
||||||
"read_write": "読み書き",
|
"read_write": "読み書き",
|
||||||
|
"select_member": "メンバーを選択",
|
||||||
|
"select_workspace": "ワークスペースを選択",
|
||||||
"team_admin": "チーム管理者",
|
"team_admin": "チーム管理者",
|
||||||
"team_created_successfully": "チームを正常に作成しました。",
|
"team_created_successfully": "チームを正常に作成しました。",
|
||||||
"team_deleted_successfully": "チームを正常に削除しました。",
|
"team_deleted_successfully": "チームを正常に削除しました。",
|
||||||
@@ -1242,6 +1153,7 @@
|
|||||||
"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": "オプションを追加",
|
||||||
@@ -1280,7 +1192,6 @@
|
|||||||
"block_duplicated": "ブロックが複製されました。",
|
"block_duplicated": "ブロックが複製されました。",
|
||||||
"bold": "太字",
|
"bold": "太字",
|
||||||
"brand_color": "ブランドカラー",
|
"brand_color": "ブランドカラー",
|
||||||
"brand_color_description": "ボタン、リンク、ハイライトに適用されます。",
|
|
||||||
"brightness": "明るさ",
|
"brightness": "明るさ",
|
||||||
"bulk_edit": "一括編集",
|
"bulk_edit": "一括編集",
|
||||||
"bulk_edit_description": "以下のオプションを1行ずつ編集してください。空の行はスキップされ、重複は削除されます。",
|
"bulk_edit_description": "以下のオプションを1行ずつ編集してください。空の行はスキップされ、重複は削除されます。",
|
||||||
@@ -1298,9 +1209,7 @@
|
|||||||
"capture_new_action": "新しいアクションをキャプチャ",
|
"capture_new_action": "新しいアクションをキャプチャ",
|
||||||
"card_arrangement_for_survey_type_derived": "{surveyTypeDerived} フォームのカード配置",
|
"card_arrangement_for_survey_type_derived": "{surveyTypeDerived} フォームのカード配置",
|
||||||
"card_background_color": "カードの背景色",
|
"card_background_color": "カードの背景色",
|
||||||
"card_background_color_description": "フォームカードエリアを塗りつぶします。",
|
|
||||||
"card_border_color": "カードの枠線の色",
|
"card_border_color": "カードの枠線の色",
|
||||||
"card_border_color_description": "フォームカードの輪郭を描きます。",
|
|
||||||
"card_styling": "カードのスタイル設定",
|
"card_styling": "カードのスタイル設定",
|
||||||
"casual": "カジュアル",
|
"casual": "カジュアル",
|
||||||
"caution_edit_duplicate": "複製して編集",
|
"caution_edit_duplicate": "複製して編集",
|
||||||
@@ -1311,12 +1220,20 @@
|
|||||||
"caution_explanation_responses_are_safe": "古い回答と新しい回答が混ざり、データの概要が誤解を招く可能性があります。",
|
"caution_explanation_responses_are_safe": "古い回答と新しい回答が混ざり、データの概要が誤解を招く可能性があります。",
|
||||||
"caution_recommendation": "これにより、フォームの概要にデータの不整合が生じる可能性があります。代わりにフォームを複製することをお勧めします。",
|
"caution_recommendation": "これにより、フォームの概要にデータの不整合が生じる可能性があります。代わりにフォームを複製することをお勧めします。",
|
||||||
"caution_text": "変更は不整合を引き起こします",
|
"caution_text": "変更は不整合を引き起こします",
|
||||||
|
"centered_modal_overlay_color": "中央モーダルのオーバーレイ色",
|
||||||
"change_anyway": "とにかく変更",
|
"change_anyway": "とにかく変更",
|
||||||
"change_background": "背景を変更",
|
"change_background": "背景を変更",
|
||||||
"change_question_type": "質問の種類を変更",
|
"change_question_type": "質問の種類を変更",
|
||||||
"change_survey_type": "フォームの種類を変更すると、既存のアクセスに影響します",
|
"change_survey_type": "フォームの種類を変更すると、既存のアクセスに影響します",
|
||||||
|
"change_the_background_color_of_the_card": "カードの背景色を変更します。",
|
||||||
|
"change_the_background_color_of_the_input_fields": "入力フィールドの背景色を変更します。",
|
||||||
"change_the_background_to_a_color_image_or_animation": "背景を色、画像、またはアニメーションに変更します。",
|
"change_the_background_to_a_color_image_or_animation": "背景を色、画像、またはアニメーションに変更します。",
|
||||||
|
"change_the_border_color_of_the_card": "カードの枠線の色を変更します。",
|
||||||
|
"change_the_border_color_of_the_input_fields": "入力フィールドの枠線の色を変更します。",
|
||||||
|
"change_the_border_radius_of_the_card_and_the_inputs": "カードと入力の角丸を変更します。",
|
||||||
|
"change_the_brand_color_of_the_survey": "フォームのブランドカラーを変更します。",
|
||||||
"change_the_placement_of_this_survey": "このフォームの配置を変更します。",
|
"change_the_placement_of_this_survey": "このフォームの配置を変更します。",
|
||||||
|
"change_the_question_color_of_the_survey": "フォームの質問の色を変更します。",
|
||||||
"changes_saved": "変更を保存しました。",
|
"changes_saved": "変更を保存しました。",
|
||||||
"changing_survey_type_will_remove_existing_distribution_channels": "フォームの種類を変更すると、共有方法に影響します。回答者が現在のタイプのアクセスリンクをすでに持っている場合、切り替え後にアクセスを失う可能性があります。",
|
"changing_survey_type_will_remove_existing_distribution_channels": "フォームの種類を変更すると、共有方法に影響します。回答者が現在のタイプのアクセスリンクをすでに持っている場合、切り替え後にアクセスを失う可能性があります。",
|
||||||
"checkbox_label": "チェックボックスのラベル",
|
"checkbox_label": "チェックボックスのラベル",
|
||||||
@@ -1456,6 +1373,7 @@
|
|||||||
"hide_progress_bar": "プログレスバーを非表示",
|
"hide_progress_bar": "プログレスバーを非表示",
|
||||||
"hide_question_settings": "質問設定を非表示",
|
"hide_question_settings": "質問設定を非表示",
|
||||||
"hostname": "ホスト名",
|
"hostname": "ホスト名",
|
||||||
|
"how_funky_do_you_want_your_cards_in_survey_type_derived_surveys": "{surveyTypeDerived} フォームのカードをどれくらいユニークにしますか",
|
||||||
"if_you_need_more_please": "さらに必要な場合は、",
|
"if_you_need_more_please": "さらに必要な場合は、",
|
||||||
"if_you_really_want_that_answer_ask_until_you_get_it": "回答が提出されるまで、トリガーされるたびに表示し続けます。",
|
"if_you_really_want_that_answer_ask_until_you_get_it": "回答が提出されるまで、トリガーされるたびに表示し続けます。",
|
||||||
"ignore_global_waiting_time": "クールダウン期間を無視",
|
"ignore_global_waiting_time": "クールダウン期間を無視",
|
||||||
@@ -1466,9 +1384,7 @@
|
|||||||
"initial_value": "初期値",
|
"initial_value": "初期値",
|
||||||
"inner_text": "内部テキスト",
|
"inner_text": "内部テキスト",
|
||||||
"input_border_color": "入力の枠線の色",
|
"input_border_color": "入力の枠線の色",
|
||||||
"input_border_color_description": "テキスト入力とテキストエリアの輪郭を描きます。",
|
|
||||||
"input_color": "入力の色",
|
"input_color": "入力の色",
|
||||||
"input_color_description": "テキスト入力の内側を塗りつぶします。",
|
|
||||||
"insert_link": "リンク を 挿入",
|
"insert_link": "リンク を 挿入",
|
||||||
"invalid_targeting": "無効なターゲティング: オーディエンスフィルターを確認してください",
|
"invalid_targeting": "無効なターゲティング: オーディエンスフィルターを確認してください",
|
||||||
"invalid_video_url_warning": "有効なYouTube、Vimeo、またはLoomのURLを入力してください。現在、他の動画ホスティングプロバイダーはサポートしていません。",
|
"invalid_video_url_warning": "有効なYouTube、Vimeo、またはLoomのURLを入力してください。現在、他の動画ホスティングプロバイダーはサポートしていません。",
|
||||||
@@ -1552,12 +1468,13 @@
|
|||||||
"protect_survey_with_pin_description": "PINを持つユーザーのみがフォームにアクセスできます。",
|
"protect_survey_with_pin_description": "PINを持つユーザーのみがフォームにアクセスできます。",
|
||||||
"publish": "公開",
|
"publish": "公開",
|
||||||
"question": "質問",
|
"question": "質問",
|
||||||
|
"question_color": "質問の色",
|
||||||
"question_deleted": "質問を削除しました。",
|
"question_deleted": "質問を削除しました。",
|
||||||
"question_duplicated": "質問を複製しました。",
|
"question_duplicated": "質問を複製しました。",
|
||||||
"question_id_updated": "質問IDを更新しました",
|
"question_id_updated": "質問IDを更新しました",
|
||||||
"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}\" の クオータ に使用されています",
|
||||||
"question_used_in_recall": "この 質問 は 質問 {questionIndex} で 呼び出され て います 。",
|
"question_used_in_recall": "この 質問 は 質問 {questionIndex} で 呼び出され て います 。",
|
||||||
"question_used_in_recall_ending_card": "この 質問 は エンディング カード で 呼び出され て います。",
|
"question_used_in_recall_ending_card": "この 質問 は エンディング カード で 呼び出され て います。",
|
||||||
"quotas": {
|
"quotas": {
|
||||||
@@ -1613,7 +1530,6 @@
|
|||||||
"response_limits_redirections_and_more": "回答数の上限、リダイレクトなど。",
|
"response_limits_redirections_and_more": "回答数の上限、リダイレクトなど。",
|
||||||
"response_options": "回答オプション",
|
"response_options": "回答オプション",
|
||||||
"roundness": "丸み",
|
"roundness": "丸み",
|
||||||
"roundness_description": "カードの角の丸みを調整します。",
|
|
||||||
"row_used_in_logic_error": "この行は質問 {questionIndex} のロジックで使用されています。まず、ロジックから削除してください。",
|
"row_used_in_logic_error": "この行は質問 {questionIndex} のロジックで使用されています。まず、ロジックから削除してください。",
|
||||||
"rows": "行",
|
"rows": "行",
|
||||||
"save_and_close": "保存して閉じる",
|
"save_and_close": "保存して閉じる",
|
||||||
@@ -1655,6 +1571,7 @@
|
|||||||
"styling_set_to_theme_styles": "スタイルをテーマのスタイルに設定しました",
|
"styling_set_to_theme_styles": "スタイルをテーマのスタイルに設定しました",
|
||||||
"subheading": "サブ見出し",
|
"subheading": "サブ見出し",
|
||||||
"subtract": "減算 -",
|
"subtract": "減算 -",
|
||||||
|
"suggest_colors": "色を提案",
|
||||||
"survey_completed_heading": "フォームが完了しました",
|
"survey_completed_heading": "フォームが完了しました",
|
||||||
"survey_completed_subheading": "この無料のオープンソースフォームは閉鎖されました",
|
"survey_completed_subheading": "この無料のオープンソースフォームは閉鎖されました",
|
||||||
"survey_display_settings": "フォーム表示設定",
|
"survey_display_settings": "フォーム表示設定",
|
||||||
@@ -1731,7 +1648,7 @@
|
|||||||
"validation_rules": "検証ルール",
|
"validation_rules": "検証ルール",
|
||||||
"validation_rules_description": "次の条件を満たす回答のみを受け付ける",
|
"validation_rules_description": "次の条件を満たす回答のみを受け付ける",
|
||||||
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} は質問 {questionIndex} のロジックで使用されています。まず、ロジックから削除してください。",
|
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} は質問 {questionIndex} のロジックで使用されています。まず、ロジックから削除してください。",
|
||||||
"variable_is_used_in_quota_please_remove_it_from_quota_first": "変数“{variableName}”は“{quotaName}”クォータで使用されています",
|
"variable_is_used_in_quota_please_remove_it_from_quota_first": "変数 \"{variableName}\" は \"{quotaName}\" クォータ で使用されています",
|
||||||
"variable_name_conflicts_with_hidden_field": "変数名が既存の非表示フィールドIDと競合しています。",
|
"variable_name_conflicts_with_hidden_field": "変数名が既存の非表示フィールドIDと競合しています。",
|
||||||
"variable_name_is_already_taken_please_choose_another": "変数名はすでに使用されています。別の名前を選択してください。",
|
"variable_name_is_already_taken_please_choose_another": "変数名はすでに使用されています。別の名前を選択してください。",
|
||||||
"variable_name_must_start_with_a_letter": "変数名はアルファベットで始まらなければなりません。",
|
"variable_name_must_start_with_a_letter": "変数名はアルファベットで始まらなければなりません。",
|
||||||
@@ -1957,7 +1874,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": "このフォームは、以下の条件に一致するあなたのウェブサイトのユーザーに表示されます",
|
||||||
@@ -2000,7 +1916,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": "全体",
|
||||||
@@ -2140,71 +2055,9 @@
|
|||||||
"look": {
|
"look": {
|
||||||
"add_background_color": "背景色を追加",
|
"add_background_color": "背景色を追加",
|
||||||
"add_background_color_description": "ロゴコンテナに背景色を追加します。",
|
"add_background_color_description": "ロゴコンテナに背景色を追加します。",
|
||||||
"advanced_styling_field_border_radius": "境界線の丸み",
|
|
||||||
"advanced_styling_field_button_bg": "ボタンの背景",
|
|
||||||
"advanced_styling_field_button_bg_description": "次へ/送信ボタンを塗りつぶします。",
|
|
||||||
"advanced_styling_field_button_border_radius_description": "ボタンの角を丸めます。",
|
|
||||||
"advanced_styling_field_button_font_size_description": "ボタンラベルのテキストサイズを調整します。",
|
|
||||||
"advanced_styling_field_button_font_weight_description": "ボタンテキストを細くまたは太くします。",
|
|
||||||
"advanced_styling_field_button_height_description": "ボタンの高さを調整します。",
|
|
||||||
"advanced_styling_field_button_padding_x_description": "左右にスペースを追加します。",
|
|
||||||
"advanced_styling_field_button_padding_y_description": "上下にスペースを追加します。",
|
|
||||||
"advanced_styling_field_button_text": "ボタンのテキスト",
|
|
||||||
"advanced_styling_field_button_text_description": "ボタン内のラベルに色を付けます。",
|
|
||||||
"advanced_styling_field_description_color": "説明文の色",
|
|
||||||
"advanced_styling_field_description_color_description": "各見出しの下のテキストに色を付けます。",
|
|
||||||
"advanced_styling_field_description_size": "説明文のフォントサイズ",
|
|
||||||
"advanced_styling_field_description_size_description": "説明テキストのサイズを調整します。",
|
|
||||||
"advanced_styling_field_description_weight": "説明文のフォントの太さ",
|
|
||||||
"advanced_styling_field_description_weight_description": "説明テキストを細くまたは太くします。",
|
|
||||||
"advanced_styling_field_font_size": "フォントサイズ",
|
|
||||||
"advanced_styling_field_font_weight": "フォントの太さ",
|
|
||||||
"advanced_styling_field_headline_color": "見出しの色",
|
|
||||||
"advanced_styling_field_headline_color_description": "メインの質問テキストに色を付けます。",
|
|
||||||
"advanced_styling_field_headline_size": "見出しのフォントサイズ",
|
|
||||||
"advanced_styling_field_headline_size_description": "見出しテキストのサイズを調整します。",
|
|
||||||
"advanced_styling_field_headline_weight": "見出しのフォントの太さ",
|
|
||||||
"advanced_styling_field_headline_weight_description": "見出しテキストを細くまたは太くします。",
|
|
||||||
"advanced_styling_field_height": "最小の高さ",
|
|
||||||
"advanced_styling_field_indicator_bg": "インジケーターの背景",
|
|
||||||
"advanced_styling_field_indicator_bg_description": "バーの塗りつぶし部分に色を付けます。",
|
|
||||||
"advanced_styling_field_input_border_radius_description": "入力フィールドの角を丸めます。",
|
|
||||||
"advanced_styling_field_input_font_size_description": "入力フィールド内の入力テキストのサイズを調整します。",
|
|
||||||
"advanced_styling_field_input_height_description": "入力フィールドの最小の高さを制御します。",
|
|
||||||
"advanced_styling_field_input_padding_x_description": "左右にスペースを追加します。",
|
|
||||||
"advanced_styling_field_input_padding_y_description": "上下にスペースを追加します。",
|
|
||||||
"advanced_styling_field_input_placeholder_opacity_description": "プレースホルダーのヒントテキストを薄くします。",
|
|
||||||
"advanced_styling_field_input_shadow_description": "入力フィールドの周囲にドロップシャドウを追加します。",
|
|
||||||
"advanced_styling_field_input_text": "入力テキスト",
|
|
||||||
"advanced_styling_field_input_text_description": "入力フィールドに入力されたテキストの色を設定します。",
|
|
||||||
"advanced_styling_field_option_bg": "背景",
|
|
||||||
"advanced_styling_field_option_bg_description": "オプション項目を塗りつぶします。",
|
|
||||||
"advanced_styling_field_option_border_radius_description": "オプションの角を丸くします。",
|
|
||||||
"advanced_styling_field_option_font_size_description": "オプションラベルのテキストサイズを調整します。",
|
|
||||||
"advanced_styling_field_option_label": "ラベルの色",
|
|
||||||
"advanced_styling_field_option_label_description": "オプションラベルのテキストの色を設定します。",
|
|
||||||
"advanced_styling_field_option_padding_x_description": "左右にスペースを追加します。",
|
|
||||||
"advanced_styling_field_option_padding_y_description": "上下にスペースを追加します。",
|
|
||||||
"advanced_styling_field_padding_x": "パディングX",
|
|
||||||
"advanced_styling_field_padding_y": "パディングY",
|
|
||||||
"advanced_styling_field_placeholder_opacity": "プレースホルダーの不透明度",
|
|
||||||
"advanced_styling_field_shadow": "影",
|
|
||||||
"advanced_styling_field_track_bg": "トラックの背景",
|
|
||||||
"advanced_styling_field_track_bg_description": "バーの未入力部分の色を設定します。",
|
|
||||||
"advanced_styling_field_track_height": "トラックの高さ",
|
|
||||||
"advanced_styling_field_track_height_description": "プログレスバーの太さを調整します。",
|
|
||||||
"advanced_styling_field_upper_label_color": "見出しラベルの色",
|
|
||||||
"advanced_styling_field_upper_label_color_description": "入力フィールド上部の小さなラベルの色を設定します。",
|
|
||||||
"advanced_styling_field_upper_label_size": "見出しラベルのフォントサイズ",
|
|
||||||
"advanced_styling_field_upper_label_size_description": "入力フィールド上部の小さなラベルのサイズを調整します。",
|
|
||||||
"advanced_styling_field_upper_label_weight": "見出しラベルのフォントの太さ",
|
|
||||||
"advanced_styling_field_upper_label_weight_description": "ラベルを細くまたは太くします。",
|
|
||||||
"advanced_styling_section_buttons": "ボタン",
|
|
||||||
"advanced_styling_section_headlines": "見出しと説明",
|
|
||||||
"advanced_styling_section_inputs": "入力フィールド",
|
|
||||||
"advanced_styling_section_options": "選択肢(ラジオボタン/チェックボックス)",
|
|
||||||
"app_survey_placement": "アプリ内フォームの配置",
|
"app_survey_placement": "アプリ内フォームの配置",
|
||||||
"app_survey_placement_settings_description": "Webアプリまたはウェブサイトでフォームを表示する場所を変更します。",
|
"app_survey_placement_settings_description": "Webアプリまたはウェブサイトでフォームを表示する場所を変更します。",
|
||||||
|
"centered_modal_overlay_color": "中央モーダルのオーバーレイ色",
|
||||||
"email_customization": "メールのカスタマイズ",
|
"email_customization": "メールのカスタマイズ",
|
||||||
"email_customization_description": "Formbricksがあなたに代わって送信するメールの外観を変更します。",
|
"email_customization_description": "Formbricksがあなたに代わって送信するメールの外観を変更します。",
|
||||||
"enable_custom_styling": "カスタムスタイルを有効化",
|
"enable_custom_styling": "カスタムスタイルを有効化",
|
||||||
@@ -2215,9 +2068,6 @@
|
|||||||
"formbricks_branding_hidden": "Formbricksブランディングは非表示です。",
|
"formbricks_branding_hidden": "Formbricksブランディングは非表示です。",
|
||||||
"formbricks_branding_settings_description": "あなたのサポートに感謝していますが、オフにすることもご理解いただけます。",
|
"formbricks_branding_settings_description": "あなたのサポートに感謝していますが、オフにすることもご理解いただけます。",
|
||||||
"formbricks_branding_shown": "Formbricksブランディングは表示されています。",
|
"formbricks_branding_shown": "Formbricksブランディングは表示されています。",
|
||||||
"generate_theme_btn": "生成",
|
|
||||||
"generate_theme_confirmation": "ブランドカラーに基づいて、マッチするカラーテーマを生成しますか?現在のカラー設定は上書きされます。",
|
|
||||||
"generate_theme_header": "カラーテーマを生成しますか?",
|
|
||||||
"logo_removed_successfully": "ロゴを正常に削除しました",
|
"logo_removed_successfully": "ロゴを正常に削除しました",
|
||||||
"logo_settings_description": "会社のロゴをアップロードして、アンケートとリンクプレビューにブランディングを適用します。",
|
"logo_settings_description": "会社のロゴをアップロードして、アンケートとリンクプレビューにブランディングを適用します。",
|
||||||
"logo_updated_successfully": "ロゴを正常に更新しました",
|
"logo_updated_successfully": "ロゴを正常に更新しました",
|
||||||
@@ -2232,8 +2082,6 @@
|
|||||||
"show_formbricks_branding_in": "{type}アンケートにFormbricksブランディングを表示",
|
"show_formbricks_branding_in": "{type}アンケートにFormbricksブランディングを表示",
|
||||||
"show_powered_by_formbricks": "「Powered by Formbricks」署名を表示",
|
"show_powered_by_formbricks": "「Powered by Formbricks」署名を表示",
|
||||||
"styling_updated_successfully": "スタイルを正常に更新しました",
|
"styling_updated_successfully": "スタイルを正常に更新しました",
|
||||||
"suggest_colors": "カラーを提案",
|
|
||||||
"suggested_colors_applied_please_save": "推奨カラーが正常に生成されました。変更を保存するには「保存」を押してください。",
|
|
||||||
"theme": "テーマ",
|
"theme": "テーマ",
|
||||||
"theme_settings_description": "すべてのアンケート用のスタイルテーマを作成します。各アンケートでカスタムスタイルを有効にできます。"
|
"theme_settings_description": "すべてのアンケート用のスタイルテーマを作成します。各アンケートでカスタムスタイルを有効にできます。"
|
||||||
},
|
},
|
||||||
@@ -2997,7 +2845,6 @@
|
|||||||
"preview_survey_question_2_choice_1_label": "はい、最新情報を知りたいです。",
|
"preview_survey_question_2_choice_1_label": "はい、最新情報を知りたいです。",
|
||||||
"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_welcome_card_headline": "ようこそ!",
|
"preview_survey_welcome_card_headline": "ようこそ!",
|
||||||
"prioritize_features_description": "ユーザーが最も必要とする機能と最も必要としない機能を特定する。",
|
"prioritize_features_description": "ユーザーが最も必要とする機能と最も必要としない機能を特定する。",
|
||||||
"prioritize_features_name": "機能の優先順位付け",
|
"prioritize_features_name": "機能の優先順位付け",
|
||||||
|
|||||||
+24
-177
@@ -188,7 +188,6 @@
|
|||||||
"customer_success": "Klant succes",
|
"customer_success": "Klant succes",
|
||||||
"dark_overlay": "Donkere overlay",
|
"dark_overlay": "Donkere overlay",
|
||||||
"date": "Datum",
|
"date": "Datum",
|
||||||
"days": "dagen",
|
|
||||||
"default": "Standaard",
|
"default": "Standaard",
|
||||||
"delete": "Verwijderen",
|
"delete": "Verwijderen",
|
||||||
"description": "Beschrijving",
|
"description": "Beschrijving",
|
||||||
@@ -218,7 +217,6 @@
|
|||||||
"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",
|
||||||
@@ -277,7 +275,6 @@
|
|||||||
"mobile_overlay_app_works_best_on_desktop": "Formbricks werkt het beste op een groter scherm. Schakel over naar een ander apparaat om enquêtes te beheren of samen te stellen.",
|
"mobile_overlay_app_works_best_on_desktop": "Formbricks werkt het beste op een groter scherm. Schakel over naar een ander apparaat om enquêtes te beheren of samen te stellen.",
|
||||||
"mobile_overlay_surveys_look_good": "Maakt u zich geen zorgen: uw enquêtes zien er geweldig uit op elk apparaat en schermformaat!",
|
"mobile_overlay_surveys_look_good": "Maakt u zich geen zorgen: uw enquêtes zien er geweldig uit op elk apparaat en schermformaat!",
|
||||||
"mobile_overlay_title": "Oeps, klein scherm gedetecteerd!",
|
"mobile_overlay_title": "Oeps, klein scherm gedetecteerd!",
|
||||||
"months": "maanden",
|
|
||||||
"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",
|
||||||
@@ -288,7 +285,6 @@
|
|||||||
"no_background_image_found": "Geen achtergrondafbeelding gevonden.",
|
"no_background_image_found": "Geen achtergrondafbeelding gevonden.",
|
||||||
"no_code": "Geen code",
|
"no_code": "Geen code",
|
||||||
"no_files_uploaded": "Er zijn geen bestanden geüpload",
|
"no_files_uploaded": "Er zijn geen bestanden geüpload",
|
||||||
"no_overlay": "Geen overlay",
|
|
||||||
"no_quotas_found": "Geen quota gevonden",
|
"no_quotas_found": "Geen quota gevonden",
|
||||||
"no_result_found": "Geen resultaat gevonden",
|
"no_result_found": "Geen resultaat gevonden",
|
||||||
"no_results": "Geen resultaten",
|
"no_results": "Geen resultaten",
|
||||||
@@ -315,7 +311,6 @@
|
|||||||
"organization_teams_not_found": "Organisatieteams niet gevonden",
|
"organization_teams_not_found": "Organisatieteams niet gevonden",
|
||||||
"other": "Ander",
|
"other": "Ander",
|
||||||
"others": "Anderen",
|
"others": "Anderen",
|
||||||
"overlay_color": "Overlaykleur",
|
|
||||||
"overview": "Overzicht",
|
"overview": "Overzicht",
|
||||||
"password": "Wachtwoord",
|
"password": "Wachtwoord",
|
||||||
"paused": "Gepauzeerd",
|
"paused": "Gepauzeerd",
|
||||||
@@ -355,7 +350,6 @@
|
|||||||
"request_trial_license": "Proeflicentie aanvragen",
|
"request_trial_license": "Proeflicentie aanvragen",
|
||||||
"reset_to_default": "Resetten naar standaard",
|
"reset_to_default": "Resetten naar standaard",
|
||||||
"response": "Antwoord",
|
"response": "Antwoord",
|
||||||
"response_id": "Antwoord-ID",
|
|
||||||
"responses": "Reacties",
|
"responses": "Reacties",
|
||||||
"restart": "Opnieuw opstarten",
|
"restart": "Opnieuw opstarten",
|
||||||
"role": "Rol",
|
"role": "Rol",
|
||||||
@@ -396,7 +390,6 @@
|
|||||||
"status": "Status",
|
"status": "Status",
|
||||||
"step_by_step_manual": "Stap voor stap handleiding",
|
"step_by_step_manual": "Stap voor stap handleiding",
|
||||||
"storage_not_configured": "Bestandsopslag is niet ingesteld, uploads zullen waarschijnlijk mislukken",
|
"storage_not_configured": "Bestandsopslag is niet ingesteld, uploads zullen waarschijnlijk mislukken",
|
||||||
"string": "Tekst",
|
|
||||||
"styling": "Styling",
|
"styling": "Styling",
|
||||||
"submit": "Indienen",
|
"submit": "Indienen",
|
||||||
"summary": "Samenvatting",
|
"summary": "Samenvatting",
|
||||||
@@ -429,7 +422,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",
|
||||||
@@ -453,7 +445,6 @@
|
|||||||
"website_and_app_connection": "Website- en app-verbinding",
|
"website_and_app_connection": "Website- en app-verbinding",
|
||||||
"website_app_survey": "Website- en app-enquête",
|
"website_app_survey": "Website- en app-enquête",
|
||||||
"website_survey": "Website-enquête",
|
"website_survey": "Website-enquête",
|
||||||
"weeks": "weken",
|
|
||||||
"welcome_card": "Welkomstkaart",
|
"welcome_card": "Welkomstkaart",
|
||||||
"workspace_configuration": "Werkruimte-configuratie",
|
"workspace_configuration": "Werkruimte-configuratie",
|
||||||
"workspace_created_successfully": "Project succesvol aangemaakt",
|
"workspace_created_successfully": "Project succesvol aangemaakt",
|
||||||
@@ -464,7 +455,6 @@
|
|||||||
"workspace_not_found": "Werkruimte niet gevonden",
|
"workspace_not_found": "Werkruimte niet gevonden",
|
||||||
"workspace_permission_not_found": "Werkruimte-machtiging niet gevonden",
|
"workspace_permission_not_found": "Werkruimte-machtiging niet gevonden",
|
||||||
"workspaces": "Werkruimtes",
|
"workspaces": "Werkruimtes",
|
||||||
"years": "jaren",
|
|
||||||
"you": "Jij",
|
"you": "Jij",
|
||||||
"you_are_downgraded_to_the_community_edition": "Je bent gedowngraded naar de Community-editie.",
|
"you_are_downgraded_to_the_community_edition": "Je bent gedowngraded naar de Community-editie.",
|
||||||
"you_are_not_authorized_to_perform_this_action": "U bent niet geautoriseerd om deze actie uit te voeren.",
|
"you_are_not_authorized_to_perform_this_action": "U bent niet geautoriseerd om deze actie uit te voeren.",
|
||||||
@@ -625,7 +615,7 @@
|
|||||||
"attribute_created_successfully": "Attribuut succesvol aangemaakt",
|
"attribute_created_successfully": "Attribuut succesvol aangemaakt",
|
||||||
"attribute_description": "Beschrijving",
|
"attribute_description": "Beschrijving",
|
||||||
"attribute_description_placeholder": "Korte beschrijving",
|
"attribute_description_placeholder": "Korte beschrijving",
|
||||||
"attribute_key": "Sleutel",
|
"attribute_key": "Kenmerk",
|
||||||
"attribute_key_cannot_be_changed": "Sleutel kan niet worden gewijzigd na aanmaak",
|
"attribute_key_cannot_be_changed": "Sleutel kan niet worden gewijzigd na aanmaak",
|
||||||
"attribute_key_hint": "Alleen kleine letters, cijfers en onderstrepingstekens. Moet beginnen met een letter.",
|
"attribute_key_hint": "Alleen kleine letters, cijfers en onderstrepingstekens. Moet beginnen met een letter.",
|
||||||
"attribute_key_placeholder": "bijv. geboortedatum",
|
"attribute_key_placeholder": "bijv. geboortedatum",
|
||||||
@@ -636,45 +626,28 @@
|
|||||||
"attribute_updated_successfully": "Attribuut succesvol bijgewerkt",
|
"attribute_updated_successfully": "Attribuut succesvol bijgewerkt",
|
||||||
"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_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_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_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",
|
|
||||||
"data_type": "Gegevenstype",
|
|
||||||
"data_type_cannot_be_changed": "Gegevenstype kan niet worden gewijzigd na aanmaak",
|
|
||||||
"data_type_description": "Kies hoe dit attribuut moet worden opgeslagen en gefilterd",
|
|
||||||
"date_value_required": "Datumwaarde is vereist. Gebruik de verwijderknop om dit attribuut te verwijderen als je geen datum wilt instellen.",
|
|
||||||
"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",
|
||||||
"edit_attribute_values_description": "Wijzig de waarden voor specifieke attributen voor dit contact.",
|
"edit_attribute_values_description": "Wijzig de waarden voor specifieke attributen voor dit contact.",
|
||||||
"edit_attributes": "Attributen bewerken",
|
|
||||||
"edit_attributes_success": "Contactattributen succesvol bijgewerkt",
|
"edit_attributes_success": "Contactattributen succesvol bijgewerkt",
|
||||||
"generate_personal_link": "Persoonlijke link genereren",
|
"generate_personal_link": "Persoonlijke link genereren",
|
||||||
"generate_personal_link_description": "Selecteer een gepubliceerde enquête om een gepersonaliseerde link voor dit contact te genereren.",
|
"generate_personal_link_description": "Selecteer een gepubliceerde enquête om een gepersonaliseerde link voor dit contact te genereren.",
|
||||||
"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_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",
|
||||||
"not_provided": "Niet voorzien",
|
"not_provided": "Niet voorzien",
|
||||||
"number_value_required": "Getalwaarde is verplicht. Gebruik de verwijderknop om dit attribuut te verwijderen.",
|
|
||||||
"personal_link_generated": "Persoonlijke link succesvol gegenereerd",
|
"personal_link_generated": "Persoonlijke link succesvol gegenereerd",
|
||||||
"personal_link_generated_but_clipboard_failed": "Persoonlijke link gegenereerd maar kopiëren naar klembord mislukt: {url}",
|
"personal_link_generated_but_clipboard_failed": "Persoonlijke link gegenereerd maar kopiëren naar klembord mislukt: {url}",
|
||||||
"personal_survey_link": "Persoonlijke enquêtelink",
|
"personal_survey_link": "Persoonlijke enquêtelink",
|
||||||
@@ -683,24 +656,13 @@
|
|||||||
"search_contact": "Zoek contactpersoon",
|
"search_contact": "Zoek contactpersoon",
|
||||||
"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",
|
|
||||||
"survey_viewed": "Enquête bekeken",
|
|
||||||
"survey_viewed_at": "Bekeken op",
|
|
||||||
"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_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_generic": "Er is een fout opgetreden bij het uploaden van de contacten. Probeer het later opnieuw.",
|
|
||||||
"upload_contacts_error_invalid_file_type": "Upload een CSV-bestand",
|
|
||||||
"upload_contacts_error_no_valid_contacts": "Het geüploade CSV-bestand bevat geen geldige contacten, zie het voorbeeld CSV-bestand voor het juiste formaat.",
|
|
||||||
"upload_contacts_modal_attribute_header": "Formbricks attribuut",
|
|
||||||
"upload_contacts_modal_attributes_description": "Wijs de kolommen in uw CSV toe aan de attributen in Formbricks.",
|
"upload_contacts_modal_attributes_description": "Wijs de kolommen in uw CSV toe aan de attributen in Formbricks.",
|
||||||
"upload_contacts_modal_attributes_new": "Nieuw attribuut",
|
"upload_contacts_modal_attributes_new": "Nieuw attribuut",
|
||||||
"upload_contacts_modal_attributes_search_or_add": "Kenmerk zoeken of toevoegen",
|
"upload_contacts_modal_attributes_search_or_add": "Kenmerk zoeken of toevoegen",
|
||||||
|
"upload_contacts_modal_attributes_should_be_mapped_to": "in kaart moeten worden gebracht",
|
||||||
"upload_contacts_modal_attributes_title": "Kenmerken",
|
"upload_contacts_modal_attributes_title": "Kenmerken",
|
||||||
"upload_contacts_modal_csv_column_header": "CSV kolom",
|
|
||||||
"upload_contacts_modal_description": "Upload een CSV om snel contacten met attributen te importeren",
|
"upload_contacts_modal_description": "Upload een CSV om snel contacten met attributen te importeren",
|
||||||
"upload_contacts_modal_download_example_csv": "Voorbeeld-CSV downloaden",
|
"upload_contacts_modal_download_example_csv": "Voorbeeld-CSV downloaden",
|
||||||
"upload_contacts_modal_duplicates_description": "Hoe moeten we omgaan als er al een contact bestaat in uw contacten?",
|
"upload_contacts_modal_duplicates_description": "Hoe moeten we omgaan als er al een contact bestaat in uw contacten?",
|
||||||
@@ -757,12 +719,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 toestemming om deze spreadsheet te openen. Zorg ervoor dat de spreadsheet is gedeeld met je Google-account en dat je schrijftoegang hebt tot de 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",
|
||||||
@@ -886,40 +843,6 @@
|
|||||||
"no_attributes_yet": "Nog geen attributen!",
|
"no_attributes_yet": "Nog geen attributen!",
|
||||||
"no_filters_yet": "Er zijn nog geen filters!",
|
"no_filters_yet": "Er zijn nog geen filters!",
|
||||||
"no_segments_yet": "Je hebt momenteel geen opgeslagen segmenten.",
|
"no_segments_yet": "Je hebt momenteel geen opgeslagen segmenten.",
|
||||||
"operator_contains": "bevat",
|
|
||||||
"operator_does_not_contain": "bevat niet",
|
|
||||||
"operator_ends_with": "eindigt met",
|
|
||||||
"operator_is_after": "is na",
|
|
||||||
"operator_is_before": "is eerder",
|
|
||||||
"operator_is_between": "is tussen",
|
|
||||||
"operator_is_newer_than": "is nieuwer dan",
|
|
||||||
"operator_is_not_set": "is niet ingesteld",
|
|
||||||
"operator_is_older_than": "is ouder dan",
|
|
||||||
"operator_is_same_day": "is dezelfde dag",
|
|
||||||
"operator_is_set": "is ingesteld",
|
|
||||||
"operator_starts_with": "begint met",
|
|
||||||
"operator_title_contains": "Bevat",
|
|
||||||
"operator_title_does_not_contain": "Bevat niet",
|
|
||||||
"operator_title_ends_with": "Eindigt met",
|
|
||||||
"operator_title_equals": "Gelijk aan",
|
|
||||||
"operator_title_greater_equal": "Groter dan of gelijk aan",
|
|
||||||
"operator_title_greater_than": "Groter dan",
|
|
||||||
"operator_title_is_after": "Is na",
|
|
||||||
"operator_title_is_before": "Is eerder",
|
|
||||||
"operator_title_is_between": "Is tussen",
|
|
||||||
"operator_title_is_newer_than": "Is nieuwer dan",
|
|
||||||
"operator_title_is_not_set": "Is niet ingesteld",
|
|
||||||
"operator_title_is_older_than": "Is ouder dan",
|
|
||||||
"operator_title_is_same_day": "Is dezelfde dag",
|
|
||||||
"operator_title_is_set": "Is ingesteld",
|
|
||||||
"operator_title_less_equal": "Kleiner dan of gelijk aan",
|
|
||||||
"operator_title_less_than": "Kleiner dan",
|
|
||||||
"operator_title_not_equals": "Is niet gelijk aan",
|
|
||||||
"operator_title_starts_with": "Begint met",
|
|
||||||
"operator_title_user_is_in": "Gebruiker zit in",
|
|
||||||
"operator_title_user_is_not_in": "Gebruiker zit niet in",
|
|
||||||
"operator_user_is_in": "Gebruiker zit in",
|
|
||||||
"operator_user_is_not_in": "Gebruiker zit niet in",
|
|
||||||
"person_and_attributes": "Persoon & attributen",
|
"person_and_attributes": "Persoon & attributen",
|
||||||
"phone": "Telefoon",
|
"phone": "Telefoon",
|
||||||
"please_remove_the_segment_from_these_surveys_in_order_to_delete_it": "Verwijder het segment uit deze enquêtes om het te kunnen verwijderen.",
|
"please_remove_the_segment_from_these_surveys_in_order_to_delete_it": "Verwijder het segment uit deze enquêtes om het te kunnen verwijderen.",
|
||||||
@@ -944,7 +867,6 @@
|
|||||||
"user_targeting_is_currently_only_available_when": "Gebruikerstargeting is momenteel alleen beschikbaar wanneer",
|
"user_targeting_is_currently_only_available_when": "Gebruikerstargeting is momenteel alleen beschikbaar wanneer",
|
||||||
"value_cannot_be_empty": "Waarde kan niet leeg zijn.",
|
"value_cannot_be_empty": "Waarde kan niet leeg zijn.",
|
||||||
"value_must_be_a_number": "Waarde moet een getal zijn.",
|
"value_must_be_a_number": "Waarde moet een getal zijn.",
|
||||||
"value_must_be_positive": "Waarde moet een positief getal zijn.",
|
|
||||||
"view_filters": "Bekijk filters",
|
"view_filters": "Bekijk filters",
|
||||||
"where": "Waar",
|
"where": "Waar",
|
||||||
"with_the_formbricks_sdk": "met de Formbricks SDK"
|
"with_the_formbricks_sdk": "met de Formbricks SDK"
|
||||||
@@ -1031,32 +953,19 @@
|
|||||||
"enterprise_features": "Enterprise-functies",
|
"enterprise_features": "Enterprise-functies",
|
||||||
"get_an_enterprise_license_to_get_access_to_all_features": "Ontvang een Enterprise-licentie om toegang te krijgen tot alle functies.",
|
"get_an_enterprise_license_to_get_access_to_all_features": "Ontvang een Enterprise-licentie om toegang te krijgen tot alle functies.",
|
||||||
"keep_full_control_over_your_data_privacy_and_security": "Houd de volledige controle over de privacy en beveiliging van uw gegevens.",
|
"keep_full_control_over_your_data_privacy_and_security": "Houd de volledige controle over de privacy en beveiliging van uw gegevens.",
|
||||||
"license_invalid_description": "De licentiesleutel in je ENTERPRISE_LICENSE_KEY omgevingsvariabele is niet geldig. Controleer op typefouten of vraag een nieuwe sleutel aan.",
|
|
||||||
"license_status": "Licentiestatus",
|
|
||||||
"license_status_active": "Actief",
|
|
||||||
"license_status_description": "Status van je enterprise-licentie.",
|
|
||||||
"license_status_expired": "Verlopen",
|
|
||||||
"license_status_invalid": "Ongeldige licentie",
|
|
||||||
"license_status_unreachable": "Niet bereikbaar",
|
|
||||||
"license_unreachable_grace_period": "Licentieserver is niet bereikbaar. Je enterprise functies blijven actief tijdens een respijtperiode van 3 dagen die eindigt op {gracePeriodEnd}.",
|
|
||||||
"no_call_needed_no_strings_attached_request_a_free_30_day_trial_license_to_test_all_features_by_filling_out_this_form": "Geen telefoontje nodig, geen verplichtingen: vraag een gratis proeflicentie van 30 dagen aan om alle functies te testen door dit formulier in te vullen:",
|
"no_call_needed_no_strings_attached_request_a_free_30_day_trial_license_to_test_all_features_by_filling_out_this_form": "Geen telefoontje nodig, geen verplichtingen: vraag een gratis proeflicentie van 30 dagen aan om alle functies te testen door dit formulier in te vullen:",
|
||||||
"no_credit_card_no_sales_call_just_test_it": "Geen creditcard. Geen verkoopgesprek. Gewoon testen :)",
|
"no_credit_card_no_sales_call_just_test_it": "Geen creditcard. Geen verkoopgesprek. Gewoon testen :)",
|
||||||
"on_request": "Op aanvraag",
|
"on_request": "Op aanvraag",
|
||||||
"organization_roles": "Organisatierollen (beheerder, redacteur, ontwikkelaar, etc.)",
|
"organization_roles": "Organisatierollen (beheerder, redacteur, ontwikkelaar, etc.)",
|
||||||
"questions_please_reach_out_to": "Vragen? Neem contact op met",
|
"questions_please_reach_out_to": "Vragen? Neem contact op met",
|
||||||
"recheck_license": "Licentie opnieuw controleren",
|
|
||||||
"recheck_license_failed": "Licentiecontrole mislukt. De licentieserver is mogelijk niet bereikbaar.",
|
|
||||||
"recheck_license_invalid": "De licentiesleutel is ongeldig. Controleer je ENTERPRISE_LICENSE_KEY.",
|
|
||||||
"recheck_license_success": "Licentiecontrole geslaagd",
|
|
||||||
"recheck_license_unreachable": "Licentieserver is niet bereikbaar. Probeer het later opnieuw.",
|
|
||||||
"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",
|
||||||
"soc2_hipaa_iso_27001_compliance_check": "SOC2, HIPAA, ISO 27001 Conformiteitscontrole",
|
"soc2_hipaa_iso_27001_compliance_check": "SOC2, HIPAA, ISO 27001 Conformiteitscontrole",
|
||||||
"sso": "SSO (Google, Microsoft, OpenID Connect)",
|
"sso": "SSO (Google, Microsoft, OpenID Connect)",
|
||||||
"teams": "Teams en toegangsrollen (lezen, lezen en schrijven, beheren)",
|
"teams": "Teams en toegangsrollen (lezen, lezen en schrijven, beheren)",
|
||||||
"unlock_the_full_power_of_formbricks_free_for_30_days": "Ontgrendel de volledige kracht van Formbricks. 30 dagen gratis."
|
"unlock_the_full_power_of_formbricks_free_for_30_days": "Ontgrendel de volledige kracht van Formbricks. 30 dagen gratis.",
|
||||||
|
"your_enterprise_license_is_active_all_features_unlocked": "Uw Enterprise-licentie is actief. Alle functies ontgrendeld."
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"bulk_invite_warning_description": "Bij het gratis abonnement krijgen alle organisatieleden altijd de rol 'Eigenaar' toegewezen.",
|
"bulk_invite_warning_description": "Bij het gratis abonnement krijgen alle organisatieleden altijd de rol 'Eigenaar' toegewezen.",
|
||||||
@@ -1080,7 +989,7 @@
|
|||||||
"from_your_organization": "vanuit uw 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}",
|
"invited_on": "Uitgenodigd op {date}",
|
||||||
"invites_failed": "Uitnodigingen zijn mislukt",
|
"invites_failed": "Uitnodigingen zijn mislukt",
|
||||||
"leave_organization": "Verlaat de organisatie",
|
"leave_organization": "Verlaat de organisatie",
|
||||||
"leave_organization_description": "U verlaat deze organisatie en verliest de toegang tot alle enquêtes en reacties. Je kunt alleen weer meedoen als je opnieuw wordt uitgenodigd.",
|
"leave_organization_description": "U verlaat deze organisatie en verliest de toegang tot alle enquêtes en reacties. Je kunt alleen weer meedoen als je opnieuw wordt uitgenodigd.",
|
||||||
@@ -1193,6 +1102,8 @@
|
|||||||
"please_fill_all_workspace_fields": "Vul alle velden in om een nieuwe werkruimte toe te voegen.",
|
"please_fill_all_workspace_fields": "Vul alle velden in om een nieuwe werkruimte toe te voegen.",
|
||||||
"read": "Lezen",
|
"read": "Lezen",
|
||||||
"read_write": "Lezen en schrijven",
|
"read_write": "Lezen en schrijven",
|
||||||
|
"select_member": "Selecteer lid",
|
||||||
|
"select_workspace": "Selecteer werkruimte",
|
||||||
"team_admin": "Teambeheerder",
|
"team_admin": "Teambeheerder",
|
||||||
"team_created_successfully": "Team succesvol aangemaakt.",
|
"team_created_successfully": "Team succesvol aangemaakt.",
|
||||||
"team_deleted_successfully": "Team succesvol verwijderd.",
|
"team_deleted_successfully": "Team succesvol verwijderd.",
|
||||||
@@ -1242,6 +1153,7 @@
|
|||||||
"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": "Voeg een buitenrand toe aan uw enquêtekaart.",
|
||||||
"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",
|
||||||
@@ -1280,7 +1192,6 @@
|
|||||||
"block_duplicated": "Blok gedupliceerd.",
|
"block_duplicated": "Blok gedupliceerd.",
|
||||||
"bold": "Vetgedrukt",
|
"bold": "Vetgedrukt",
|
||||||
"brand_color": "Merk kleur",
|
"brand_color": "Merk kleur",
|
||||||
"brand_color_description": "Toegepast op knoppen, links en highlights.",
|
|
||||||
"brightness": "Helderheid",
|
"brightness": "Helderheid",
|
||||||
"bulk_edit": "Bulkbewerking",
|
"bulk_edit": "Bulkbewerking",
|
||||||
"bulk_edit_description": "Bewerk alle opties hieronder, één per regel. Lege regels worden overgeslagen en duplicaten verwijderd.",
|
"bulk_edit_description": "Bewerk alle opties hieronder, één per regel. Lege regels worden overgeslagen en duplicaten verwijderd.",
|
||||||
@@ -1298,9 +1209,7 @@
|
|||||||
"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",
|
||||||
"card_background_color_description": "Vult het enquêtekaartgebied.",
|
|
||||||
"card_border_color": "Randkleur kaart",
|
"card_border_color": "Randkleur kaart",
|
||||||
"card_border_color_description": "Omlijnt de enquêtekaart.",
|
|
||||||
"card_styling": "Kaartstijl",
|
"card_styling": "Kaartstijl",
|
||||||
"casual": "Casual",
|
"casual": "Casual",
|
||||||
"caution_edit_duplicate": "Dupliceren en bewerken",
|
"caution_edit_duplicate": "Dupliceren en bewerken",
|
||||||
@@ -1311,12 +1220,20 @@
|
|||||||
"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.",
|
||||||
"caution_recommendation": "Dit kan inconsistenties in de gegevens in de onderzoekssamenvatting veroorzaken. Wij raden u aan de enquête te dupliceren.",
|
"caution_recommendation": "Dit kan inconsistenties in de gegevens in de onderzoekssamenvatting veroorzaken. Wij raden u aan de enquête te dupliceren.",
|
||||||
"caution_text": "Veranderingen zullen tot inconsistenties leiden",
|
"caution_text": "Veranderingen zullen tot inconsistenties leiden",
|
||||||
|
"centered_modal_overlay_color": "Gecentreerde modale overlaykleur",
|
||||||
"change_anyway": "Hoe dan ook veranderen",
|
"change_anyway": "Hoe dan ook veranderen",
|
||||||
"change_background": "Achtergrond wijzigen",
|
"change_background": "Achtergrond wijzigen",
|
||||||
"change_question_type": "Vraagtype wijzigen",
|
"change_question_type": "Vraagtype wijzigen",
|
||||||
"change_survey_type": "Als u van enquêtetype verandert, heeft dit invloed op de bestaande toegang",
|
"change_survey_type": "Als u van enquêtetype verandert, heeft dit invloed op de bestaande toegang",
|
||||||
|
"change_the_background_color_of_the_card": "Verander de achtergrondkleur van de kaart.",
|
||||||
|
"change_the_background_color_of_the_input_fields": "Verander de achtergrondkleur van de invoervelden.",
|
||||||
"change_the_background_to_a_color_image_or_animation": "Verander de achtergrond in een kleur, afbeelding of animatie.",
|
"change_the_background_to_a_color_image_or_animation": "Verander de achtergrond in een kleur, afbeelding of animatie.",
|
||||||
|
"change_the_border_color_of_the_card": "Verander de randkleur van de kaart.",
|
||||||
|
"change_the_border_color_of_the_input_fields": "Wijzig de randkleur van de invoervelden.",
|
||||||
|
"change_the_border_radius_of_the_card_and_the_inputs": "Wijzig de randradius van de kaart en de ingangen.",
|
||||||
|
"change_the_brand_color_of_the_survey": "Wijzig de merkkleur van de enquête.",
|
||||||
"change_the_placement_of_this_survey": "Wijzig de plaatsing van deze enquête.",
|
"change_the_placement_of_this_survey": "Wijzig de plaatsing van deze enquête.",
|
||||||
|
"change_the_question_color_of_the_survey": "Verander de vraagkleur van de enquête.",
|
||||||
"changes_saved": "Wijzigingen opgeslagen.",
|
"changes_saved": "Wijzigingen opgeslagen.",
|
||||||
"changing_survey_type_will_remove_existing_distribution_channels": "Het wijzigen van het enquêtetype heeft invloed op de manier waarop deze kan worden gedeeld. Als respondenten al toegangslinks hebben voor het huidige type, verliezen ze mogelijk de toegang na de overstap.",
|
"changing_survey_type_will_remove_existing_distribution_channels": "Het wijzigen van het enquêtetype heeft invloed op de manier waarop deze kan worden gedeeld. Als respondenten al toegangslinks hebben voor het huidige type, verliezen ze mogelijk de toegang na de overstap.",
|
||||||
"checkbox_label": "Selectievakje-label",
|
"checkbox_label": "Selectievakje-label",
|
||||||
@@ -1456,6 +1373,7 @@
|
|||||||
"hide_progress_bar": "Voortgangsbalk verbergen",
|
"hide_progress_bar": "Voortgangsbalk verbergen",
|
||||||
"hide_question_settings": "Vraaginstellingen verbergen",
|
"hide_question_settings": "Vraaginstellingen verbergen",
|
||||||
"hostname": "Hostnaam",
|
"hostname": "Hostnaam",
|
||||||
|
"how_funky_do_you_want_your_cards_in_survey_type_derived_surveys": "Hoe funky wil je je kaarten hebben in {surveyTypeDerived} Enquêtes",
|
||||||
"if_you_need_more_please": "Als je meer nodig hebt,",
|
"if_you_need_more_please": "Als je meer nodig hebt,",
|
||||||
"if_you_really_want_that_answer_ask_until_you_get_it": "Blijf tonen wanneer geactiveerd totdat een reactie is ingediend.",
|
"if_you_really_want_that_answer_ask_until_you_get_it": "Blijf tonen wanneer geactiveerd totdat een reactie is ingediend.",
|
||||||
"ignore_global_waiting_time": "Afkoelperiode negeren",
|
"ignore_global_waiting_time": "Afkoelperiode negeren",
|
||||||
@@ -1466,9 +1384,7 @@
|
|||||||
"initial_value": "Initiële waarde",
|
"initial_value": "Initiële waarde",
|
||||||
"inner_text": "Innerlijke tekst",
|
"inner_text": "Innerlijke tekst",
|
||||||
"input_border_color": "Randkleur invoeren",
|
"input_border_color": "Randkleur invoeren",
|
||||||
"input_border_color_description": "Omlijnt tekstvelden en tekstgebieden.",
|
|
||||||
"input_color": "Kleur invoeren",
|
"input_color": "Kleur invoeren",
|
||||||
"input_color_description": "Vult de binnenkant van tekstvelden.",
|
|
||||||
"insert_link": "Link invoegen",
|
"insert_link": "Link invoegen",
|
||||||
"invalid_targeting": "Ongeldige targeting: controleer uw doelgroepfilters",
|
"invalid_targeting": "Ongeldige targeting: controleer uw doelgroepfilters",
|
||||||
"invalid_video_url_warning": "Voer een geldige YouTube-, Vimeo- of Loom-URL in. We ondersteunen momenteel geen andere videohostingproviders.",
|
"invalid_video_url_warning": "Voer een geldige YouTube-, Vimeo- of Loom-URL in. We ondersteunen momenteel geen andere videohostingproviders.",
|
||||||
@@ -1552,12 +1468,13 @@
|
|||||||
"protect_survey_with_pin_description": "Alleen gebruikers die de pincode hebben, hebben toegang tot de enquête.",
|
"protect_survey_with_pin_description": "Alleen gebruikers die de pincode hebben, hebben toegang tot de enquête.",
|
||||||
"publish": "Publiceren",
|
"publish": "Publiceren",
|
||||||
"question": "Vraag",
|
"question": "Vraag",
|
||||||
|
"question_color": "Vraag kleur",
|
||||||
"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_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}'",
|
||||||
"question_used_in_recall": "Deze vraag wordt teruggehaald in vraag {questionIndex}.",
|
"question_used_in_recall": "Deze vraag wordt teruggehaald in vraag {questionIndex}.",
|
||||||
"question_used_in_recall_ending_card": "Deze vraag wordt teruggeroepen in de Eindkaart",
|
"question_used_in_recall_ending_card": "Deze vraag wordt teruggeroepen in de Eindkaart",
|
||||||
"quotas": {
|
"quotas": {
|
||||||
@@ -1613,7 +1530,6 @@
|
|||||||
"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 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",
|
||||||
@@ -1655,6 +1571,7 @@
|
|||||||
"styling_set_to_theme_styles": "Styling ingesteld op themastijlen",
|
"styling_set_to_theme_styles": "Styling ingesteld op themastijlen",
|
||||||
"subheading": "Ondertitel",
|
"subheading": "Ondertitel",
|
||||||
"subtract": "Aftrekken -",
|
"subtract": "Aftrekken -",
|
||||||
|
"suggest_colors": "Stel kleuren voor",
|
||||||
"survey_completed_heading": "Enquête voltooid",
|
"survey_completed_heading": "Enquête voltooid",
|
||||||
"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",
|
||||||
@@ -1731,7 +1648,7 @@
|
|||||||
"validation_rules": "Validatieregels",
|
"validation_rules": "Validatieregels",
|
||||||
"validation_rules_description": "Accepteer alleen antwoorden die voldoen aan de volgende criteria",
|
"validation_rules_description": "Accepteer alleen antwoorden die voldoen aan de volgende criteria",
|
||||||
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} wordt gebruikt in de logica van vraag {questionIndex}. Verwijder het eerst uit de logica.",
|
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} wordt gebruikt in de logica van vraag {questionIndex}. Verwijder het eerst uit de logica.",
|
||||||
"variable_is_used_in_quota_please_remove_it_from_quota_first": "Variabele “{variableName}” wordt gebruikt in het quotum “{quotaName}”",
|
"variable_is_used_in_quota_please_remove_it_from_quota_first": "Variabele \"{variableName}\" wordt gebruikt in het \"{quotaName}\" quotum",
|
||||||
"variable_name_conflicts_with_hidden_field": "Variabelenaam conflicteert met een bestaande verborgen veld-ID.",
|
"variable_name_conflicts_with_hidden_field": "Variabelenaam conflicteert met een bestaande verborgen veld-ID.",
|
||||||
"variable_name_is_already_taken_please_choose_another": "Variabelenaam is al in gebruik, kies een andere.",
|
"variable_name_is_already_taken_please_choose_another": "Variabelenaam is al in gebruik, kies een andere.",
|
||||||
"variable_name_must_start_with_a_letter": "Variabelenaam moet beginnen met een letter.",
|
"variable_name_must_start_with_a_letter": "Variabelenaam moet beginnen met een letter.",
|
||||||
@@ -1957,7 +1874,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",
|
||||||
@@ -2000,7 +1916,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",
|
||||||
@@ -2140,71 +2055,9 @@
|
|||||||
"look": {
|
"look": {
|
||||||
"add_background_color": "Achtergrondkleur toevoegen",
|
"add_background_color": "Achtergrondkleur toevoegen",
|
||||||
"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_button_bg": "Knopachtergrond",
|
|
||||||
"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_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_height_description": "Bepaalt de knophoogte.",
|
|
||||||
"advanced_styling_field_button_padding_x_description": "Voegt ruimte toe aan de linker- en rechterkant.",
|
|
||||||
"advanced_styling_field_button_padding_y_description": "Voegt ruimte toe aan de boven- en onderkant.",
|
|
||||||
"advanced_styling_field_button_text": "Knoptekst",
|
|
||||||
"advanced_styling_field_button_text_description": "Kleurt het label binnen knoppen.",
|
|
||||||
"advanced_styling_field_description_color": "Beschrijvingskleur",
|
|
||||||
"advanced_styling_field_description_color_description": "Kleurt de tekst onder elke kop.",
|
|
||||||
"advanced_styling_field_description_size": "Lettergrootte beschrijving",
|
|
||||||
"advanced_styling_field_description_size_description": "Schaalt de beschrijvingstekst.",
|
|
||||||
"advanced_styling_field_description_weight": "Letterdikte beschrijving",
|
|
||||||
"advanced_styling_field_description_weight_description": "Maakt beschrijvingstekst lichter of vetter.",
|
|
||||||
"advanced_styling_field_font_size": "Lettergrootte",
|
|
||||||
"advanced_styling_field_font_weight": "Letterdikte",
|
|
||||||
"advanced_styling_field_headline_color": "Kopkleur",
|
|
||||||
"advanced_styling_field_headline_color_description": "Kleurt de hoofdvraagtekst.",
|
|
||||||
"advanced_styling_field_headline_size": "Lettergrootte kop",
|
|
||||||
"advanced_styling_field_headline_size_description": "Schaalt de koptekst.",
|
|
||||||
"advanced_styling_field_headline_weight": "Letterdikte kop",
|
|
||||||
"advanced_styling_field_headline_weight_description": "Maakt koptekst lichter of vetter.",
|
|
||||||
"advanced_styling_field_height": "Minimale hoogte",
|
|
||||||
"advanced_styling_field_indicator_bg": "Indicatorachtergrond",
|
|
||||||
"advanced_styling_field_indicator_bg_description": "Kleurt het gevulde deel van de balk.",
|
|
||||||
"advanced_styling_field_input_border_radius_description": "Rondt de invoerhoeken af.",
|
|
||||||
"advanced_styling_field_input_font_size_description": "Schaalt de getypte tekst in invoervelden.",
|
|
||||||
"advanced_styling_field_input_height_description": "Bepaalt de minimale hoogte van het invoerveld.",
|
|
||||||
"advanced_styling_field_input_padding_x_description": "Voegt ruimte toe aan de linker- en rechterkant.",
|
|
||||||
"advanced_styling_field_input_padding_y_description": "Voegt ruimte toe aan de boven- en onderkant.",
|
|
||||||
"advanced_styling_field_input_placeholder_opacity_description": "Vervaagt de tijdelijke aanwijzingstekst.",
|
|
||||||
"advanced_styling_field_input_shadow_description": "Voegt een slagschaduw toe rond invoervelden.",
|
|
||||||
"advanced_styling_field_input_text": "Invoertekst",
|
|
||||||
"advanced_styling_field_input_text_description": "Kleurt de getypte tekst in invoervelden.",
|
|
||||||
"advanced_styling_field_option_bg": "Achtergrond",
|
|
||||||
"advanced_styling_field_option_bg_description": "Vult de optie-items.",
|
|
||||||
"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_label": "Labelkleur",
|
|
||||||
"advanced_styling_field_option_label_description": "Kleurt de tekst van optielabels.",
|
|
||||||
"advanced_styling_field_option_padding_x_description": "Voegt ruimte toe aan de linker- en rechterkant.",
|
|
||||||
"advanced_styling_field_option_padding_y_description": "Voegt ruimte toe aan de boven- en onderkant.",
|
|
||||||
"advanced_styling_field_padding_x": "Opvulling X",
|
|
||||||
"advanced_styling_field_padding_y": "Opvulling Y",
|
|
||||||
"advanced_styling_field_placeholder_opacity": "Plaatshouderopaciteit",
|
|
||||||
"advanced_styling_field_shadow": "Schaduw",
|
|
||||||
"advanced_styling_field_track_bg": "Sporachtergrond",
|
|
||||||
"advanced_styling_field_track_bg_description": "Kleurt het ongevulde gedeelte van de balk.",
|
|
||||||
"advanced_styling_field_track_height": "Spoorhoogte",
|
|
||||||
"advanced_styling_field_track_height_description": "Regelt de dikte van de voortgangsbalk.",
|
|
||||||
"advanced_styling_field_upper_label_color": "Koplabelkleur",
|
|
||||||
"advanced_styling_field_upper_label_color_description": "Kleurt het kleine label boven invoervelden.",
|
|
||||||
"advanced_styling_field_upper_label_size": "Lettergrootte koplabel",
|
|
||||||
"advanced_styling_field_upper_label_size_description": "Schaalt het kleine label boven invoervelden.",
|
|
||||||
"advanced_styling_field_upper_label_weight": "Letterdikte koplabel",
|
|
||||||
"advanced_styling_field_upper_label_weight_description": "Maakt het label lichter of vetter.",
|
|
||||||
"advanced_styling_section_buttons": "Knoppen",
|
|
||||||
"advanced_styling_section_headlines": "Koppen & beschrijvingen",
|
|
||||||
"advanced_styling_section_inputs": "Invoervelden",
|
|
||||||
"advanced_styling_section_options": "Opties (radio/checkbox)",
|
|
||||||
"app_survey_placement": "App-enquête plaatsing",
|
"app_survey_placement": "App-enquête plaatsing",
|
||||||
"app_survey_placement_settings_description": "Wijzig waar enquêtes worden weergegeven in uw web-app of website.",
|
"app_survey_placement_settings_description": "Wijzig waar enquêtes worden weergegeven in uw web-app of website.",
|
||||||
|
"centered_modal_overlay_color": "Gecentreerde modale overlaykleur",
|
||||||
"email_customization": "E-mail aanpassing",
|
"email_customization": "E-mail aanpassing",
|
||||||
"email_customization_description": "Wijzig het uiterlijk van e-mails die Formbricks namens u verstuurt.",
|
"email_customization_description": "Wijzig het uiterlijk van e-mails die Formbricks namens u verstuurt.",
|
||||||
"enable_custom_styling": "Aangepaste styling inschakelen",
|
"enable_custom_styling": "Aangepaste styling inschakelen",
|
||||||
@@ -2215,9 +2068,6 @@
|
|||||||
"formbricks_branding_hidden": "Formbricks-branding is verborgen.",
|
"formbricks_branding_hidden": "Formbricks-branding is verborgen.",
|
||||||
"formbricks_branding_settings_description": "We waarderen uw steun, maar begrijpen het als u dit uitschakelt.",
|
"formbricks_branding_settings_description": "We waarderen uw steun, maar begrijpen het als u dit uitschakelt.",
|
||||||
"formbricks_branding_shown": "Formbricks-branding wordt weergegeven.",
|
"formbricks_branding_shown": "Formbricks-branding wordt weergegeven.",
|
||||||
"generate_theme_btn": "Genereren",
|
|
||||||
"generate_theme_confirmation": "Wil je een passend kleurthema genereren op basis van je merkkleur? Dit overschrijft je huidige kleurinstellingen.",
|
|
||||||
"generate_theme_header": "Kleurthema genereren?",
|
|
||||||
"logo_removed_successfully": "Logo succesvol verwijderd",
|
"logo_removed_successfully": "Logo succesvol verwijderd",
|
||||||
"logo_settings_description": "Upload uw bedrijfslogo om enquêtes en linkvoorbeelden te voorzien van uw huisstijl.",
|
"logo_settings_description": "Upload uw bedrijfslogo om enquêtes en linkvoorbeelden te voorzien van uw huisstijl.",
|
||||||
"logo_updated_successfully": "Logo succesvol bijgewerkt",
|
"logo_updated_successfully": "Logo succesvol bijgewerkt",
|
||||||
@@ -2232,8 +2082,6 @@
|
|||||||
"show_formbricks_branding_in": "Toon Formbricks-branding in {type} enquêtes",
|
"show_formbricks_branding_in": "Toon Formbricks-branding in {type} enquêtes",
|
||||||
"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",
|
|
||||||
"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."
|
||||||
},
|
},
|
||||||
@@ -2997,7 +2845,6 @@
|
|||||||
"preview_survey_question_2_choice_1_label": "Ja, houd mij op de hoogte.",
|
"preview_survey_question_2_choice_1_label": "Ja, houd mij op de hoogte.",
|
||||||
"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_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",
|
||||||
|
|||||||
+24
-177
@@ -188,7 +188,6 @@
|
|||||||
"customer_success": "Sucesso do Cliente",
|
"customer_success": "Sucesso do Cliente",
|
||||||
"dark_overlay": "sobreposição escura",
|
"dark_overlay": "sobreposição escura",
|
||||||
"date": "Encontro",
|
"date": "Encontro",
|
||||||
"days": "dias",
|
|
||||||
"default": "Padrão",
|
"default": "Padrão",
|
||||||
"delete": "Apagar",
|
"delete": "Apagar",
|
||||||
"description": "Descrição",
|
"description": "Descrição",
|
||||||
@@ -218,7 +217,6 @@
|
|||||||
"error": "Erro",
|
"error": "Erro",
|
||||||
"error_component_description": "Esse recurso não existe ou você não tem permissão para acessá-lo.",
|
"error_component_description": "Esse recurso não existe ou você não tem permissão para acessá-lo.",
|
||||||
"error_component_title": "Erro ao carregar recursos",
|
"error_component_title": "Erro ao carregar recursos",
|
||||||
"error_loading_data": "Erro ao carregar dados",
|
|
||||||
"error_rate_limit_description": "Número máximo de requisições atingido. Por favor, tente novamente mais tarde.",
|
"error_rate_limit_description": "Número máximo de requisições atingido. Por favor, tente novamente mais tarde.",
|
||||||
"error_rate_limit_title": "Limite de Taxa Excedido",
|
"error_rate_limit_title": "Limite de Taxa Excedido",
|
||||||
"expand_rows": "Expandir linhas",
|
"expand_rows": "Expandir linhas",
|
||||||
@@ -277,7 +275,6 @@
|
|||||||
"mobile_overlay_app_works_best_on_desktop": "Formbricks funciona melhor em uma tela maior. Para gerenciar ou criar pesquisas, mude para outro dispositivo.",
|
"mobile_overlay_app_works_best_on_desktop": "Formbricks funciona melhor em uma tela maior. Para gerenciar ou criar pesquisas, mude para outro dispositivo.",
|
||||||
"mobile_overlay_surveys_look_good": "Não se preocupe – suas pesquisas ficam ótimas em qualquer dispositivo e tamanho de tela!",
|
"mobile_overlay_surveys_look_good": "Não se preocupe – suas pesquisas ficam ótimas em qualquer dispositivo e tamanho de tela!",
|
||||||
"mobile_overlay_title": "Eita, tela pequena detectada!",
|
"mobile_overlay_title": "Eita, tela pequena detectada!",
|
||||||
"months": "meses",
|
|
||||||
"move_down": "Descer",
|
"move_down": "Descer",
|
||||||
"move_up": "Subir",
|
"move_up": "Subir",
|
||||||
"multiple_languages": "Vários idiomas",
|
"multiple_languages": "Vários idiomas",
|
||||||
@@ -288,7 +285,6 @@
|
|||||||
"no_background_image_found": "Imagem de fundo não encontrada.",
|
"no_background_image_found": "Imagem de fundo não encontrada.",
|
||||||
"no_code": "Sem código",
|
"no_code": "Sem código",
|
||||||
"no_files_uploaded": "Nenhum arquivo foi enviado",
|
"no_files_uploaded": "Nenhum arquivo foi enviado",
|
||||||
"no_overlay": "Sem sobreposição",
|
|
||||||
"no_quotas_found": "Nenhuma cota encontrada",
|
"no_quotas_found": "Nenhuma cota encontrada",
|
||||||
"no_result_found": "Nenhum resultado encontrado",
|
"no_result_found": "Nenhum resultado encontrado",
|
||||||
"no_results": "Nenhum resultado",
|
"no_results": "Nenhum resultado",
|
||||||
@@ -315,7 +311,6 @@
|
|||||||
"organization_teams_not_found": "Equipes da organização não encontradas",
|
"organization_teams_not_found": "Equipes da organização não encontradas",
|
||||||
"other": "outro",
|
"other": "outro",
|
||||||
"others": "Outros",
|
"others": "Outros",
|
||||||
"overlay_color": "Cor da sobreposição",
|
|
||||||
"overview": "Visão Geral",
|
"overview": "Visão Geral",
|
||||||
"password": "Senha",
|
"password": "Senha",
|
||||||
"paused": "Pausado",
|
"paused": "Pausado",
|
||||||
@@ -355,7 +350,6 @@
|
|||||||
"request_trial_license": "Pedir licença de teste",
|
"request_trial_license": "Pedir licença de teste",
|
||||||
"reset_to_default": "Restaurar para o padrão",
|
"reset_to_default": "Restaurar para o padrão",
|
||||||
"response": "Resposta",
|
"response": "Resposta",
|
||||||
"response_id": "ID da resposta",
|
|
||||||
"responses": "Respostas",
|
"responses": "Respostas",
|
||||||
"restart": "Reiniciar",
|
"restart": "Reiniciar",
|
||||||
"role": "Rolê",
|
"role": "Rolê",
|
||||||
@@ -396,7 +390,6 @@
|
|||||||
"status": "status",
|
"status": "status",
|
||||||
"step_by_step_manual": "Manual passo a passo",
|
"step_by_step_manual": "Manual passo a passo",
|
||||||
"storage_not_configured": "Armazenamento de arquivos não configurado, uploads provavelmente falharão",
|
"storage_not_configured": "Armazenamento de arquivos não configurado, uploads provavelmente falharão",
|
||||||
"string": "Texto",
|
|
||||||
"styling": "Estilização",
|
"styling": "Estilização",
|
||||||
"submit": "Enviar",
|
"submit": "Enviar",
|
||||||
"summary": "Resumo",
|
"summary": "Resumo",
|
||||||
@@ -429,7 +422,6 @@
|
|||||||
"top_right": "Canto Superior Direito",
|
"top_right": "Canto Superior Direito",
|
||||||
"try_again": "Tenta de novo",
|
"try_again": "Tenta de novo",
|
||||||
"type": "Tipo",
|
"type": "Tipo",
|
||||||
"unknown_survey": "Pesquisa desconhecida",
|
|
||||||
"unlock_more_workspaces_with_a_higher_plan": "Desbloqueie mais projetos com um plano superior.",
|
"unlock_more_workspaces_with_a_higher_plan": "Desbloqueie mais projetos com um plano superior.",
|
||||||
"update": "atualizar",
|
"update": "atualizar",
|
||||||
"updated": "atualizado",
|
"updated": "atualizado",
|
||||||
@@ -453,7 +445,6 @@
|
|||||||
"website_and_app_connection": "Conexão de Site e App",
|
"website_and_app_connection": "Conexão de Site e App",
|
||||||
"website_app_survey": "Pesquisa de Site e App",
|
"website_app_survey": "Pesquisa de Site e App",
|
||||||
"website_survey": "Pesquisa de Site",
|
"website_survey": "Pesquisa de Site",
|
||||||
"weeks": "semanas",
|
|
||||||
"welcome_card": "Cartão de boas-vindas",
|
"welcome_card": "Cartão de boas-vindas",
|
||||||
"workspace_configuration": "Configuração do projeto",
|
"workspace_configuration": "Configuração do projeto",
|
||||||
"workspace_created_successfully": "Projeto criado com sucesso",
|
"workspace_created_successfully": "Projeto criado com sucesso",
|
||||||
@@ -464,7 +455,6 @@
|
|||||||
"workspace_not_found": "Projeto não encontrado",
|
"workspace_not_found": "Projeto não encontrado",
|
||||||
"workspace_permission_not_found": "Permissão do projeto não encontrada",
|
"workspace_permission_not_found": "Permissão do projeto não encontrada",
|
||||||
"workspaces": "Projetos",
|
"workspaces": "Projetos",
|
||||||
"years": "anos",
|
|
||||||
"you": "Você",
|
"you": "Você",
|
||||||
"you_are_downgraded_to_the_community_edition": "Você foi rebaixado para a Edição Comunitária.",
|
"you_are_downgraded_to_the_community_edition": "Você foi rebaixado para a Edição Comunitária.",
|
||||||
"you_are_not_authorized_to_perform_this_action": "Você não tem autorização para realizar essa ação.",
|
"you_are_not_authorized_to_perform_this_action": "Você não tem autorização para realizar essa ação.",
|
||||||
@@ -625,7 +615,7 @@
|
|||||||
"attribute_created_successfully": "Atributo criado com sucesso",
|
"attribute_created_successfully": "Atributo criado com sucesso",
|
||||||
"attribute_description": "Descrição",
|
"attribute_description": "Descrição",
|
||||||
"attribute_description_placeholder": "Descrição curta",
|
"attribute_description_placeholder": "Descrição curta",
|
||||||
"attribute_key": "Chave",
|
"attribute_key": "atributo",
|
||||||
"attribute_key_cannot_be_changed": "A chave não pode ser alterada após a criação",
|
"attribute_key_cannot_be_changed": "A chave não pode ser alterada após a criação",
|
||||||
"attribute_key_hint": "Apenas letras minúsculas, números e underscores. Deve começar com uma letra.",
|
"attribute_key_hint": "Apenas letras minúsculas, números e underscores. Deve começar com uma letra.",
|
||||||
"attribute_key_placeholder": "ex: data_de_nascimento",
|
"attribute_key_placeholder": "ex: data_de_nascimento",
|
||||||
@@ -636,45 +626,28 @@
|
|||||||
"attribute_updated_successfully": "Atributo atualizado com sucesso",
|
"attribute_updated_successfully": "Atributo atualizado com sucesso",
|
||||||
"attribute_value": "Valor",
|
"attribute_value": "Valor",
|
||||||
"attribute_value_placeholder": "Valor do atributo",
|
"attribute_value_placeholder": "Valor do atributo",
|
||||||
"attributes_msg_attribute_limit_exceeded": "Não foi possível criar {count} novo(s) atributo(s), pois excederia o limite máximo de {limit} classes de atributos. Os atributos existentes foram atualizados com sucesso.",
|
|
||||||
"attributes_msg_attribute_type_validation_error": "{error} (atributo '{key}' tem dataType: {dataType})",
|
|
||||||
"attributes_msg_email_already_exists": "O e-mail já existe para este ambiente e não foi atualizado.",
|
|
||||||
"attributes_msg_email_or_userid_required": "E-mail ou userId é obrigatório. Os valores existentes foram preservados.",
|
|
||||||
"attributes_msg_new_attribute_created": "Novo atributo '{key}' criado com tipo '{dataType}'",
|
|
||||||
"attributes_msg_userid_already_exists": "O userId já existe para este ambiente e não foi atualizado.",
|
|
||||||
"contact_deleted_successfully": "Contato excluído com sucesso",
|
"contact_deleted_successfully": "Contato excluído com sucesso",
|
||||||
"contact_not_found": "Nenhum contato encontrado",
|
"contact_not_found": "Nenhum contato encontrado",
|
||||||
"contacts_table_refresh": "Atualizar contatos",
|
"contacts_table_refresh": "Atualizar contatos",
|
||||||
"contacts_table_refresh_success": "Contatos atualizados com sucesso",
|
"contacts_table_refresh_success": "Contatos atualizados com sucesso",
|
||||||
"create_attribute": "Criar atributo",
|
"create_attribute": "Criar atributo",
|
||||||
|
"create_key": "Criar chave",
|
||||||
"create_new_attribute": "Criar novo atributo",
|
"create_new_attribute": "Criar novo atributo",
|
||||||
"create_new_attribute_description": "Crie um novo atributo para fins de segmentação.",
|
"create_new_attribute_description": "Crie um novo atributo para fins de segmentação.",
|
||||||
"custom_attributes": "Atributos personalizados",
|
|
||||||
"data_type": "Tipo de dados",
|
|
||||||
"data_type_cannot_be_changed": "O tipo de dados não pode ser alterado após a criação",
|
|
||||||
"data_type_description": "Escolha como este atributo deve ser armazenado e filtrado",
|
|
||||||
"date_value_required": "O valor da data é obrigatório. Use o botão excluir para remover este atributo se você não quiser definir uma data.",
|
|
||||||
"delete_attribute_confirmation": "{value, plural, one {Isso excluirá o atributo selecionado. Todos os dados de contato associados a este atributo serão perdidos.} other {Isso excluirá os atributos selecionados. Todos os dados de contato associados a estes atributos serão perdidos.}}",
|
"delete_attribute_confirmation": "{value, plural, one {Isso excluirá o atributo selecionado. Todos os dados de contato associados a este atributo serão perdidos.} other {Isso excluirá os atributos selecionados. Todos os dados de contato associados a estes atributos serão perdidos.}}",
|
||||||
"delete_contact_confirmation": "Isso irá apagar todas as respostas da pesquisa e atributos de contato associados a este contato. Qualquer direcionamento e personalização baseados nos dados deste contato serão perdidos.",
|
"delete_contact_confirmation": "Isso irá apagar todas as respostas da pesquisa e atributos de contato associados a este contato. Qualquer direcionamento e personalização baseados nos dados deste contato serão perdidos.",
|
||||||
"delete_contact_confirmation_with_quotas": "{value, plural, other {Isso irá apagar todas as respostas da pesquisa e atributos de contato associados a este contato. Qualquer direcionamento e personalização baseados nos dados deste contato serão perdidos. Se este contato tiver respostas que contam para cotas da pesquisa, as contagens das cotas serão reduzidas, mas os limites das cotas permanecerão inalterados.}}",
|
"delete_contact_confirmation_with_quotas": "{value, plural, other {Isso irá apagar todas as respostas da pesquisa e atributos de contato associados a este contato. Qualquer direcionamento e personalização baseados nos dados deste contato serão perdidos. Se este contato tiver respostas que contam para cotas da pesquisa, as contagens das cotas serão reduzidas, mas os limites das cotas permanecerão inalterados.}}",
|
||||||
"displays": "Exibições",
|
|
||||||
"edit_attribute": "Editar atributo",
|
"edit_attribute": "Editar atributo",
|
||||||
"edit_attribute_description": "Atualize a etiqueta e a descrição deste atributo.",
|
"edit_attribute_description": "Atualize a etiqueta e a descrição deste atributo.",
|
||||||
"edit_attribute_values": "Editar atributos",
|
"edit_attribute_values": "Editar atributos",
|
||||||
"edit_attribute_values_description": "Altere os valores de atributos específicos para este contato.",
|
"edit_attribute_values_description": "Altere os valores de atributos específicos para este contato.",
|
||||||
"edit_attributes": "Editar atributos",
|
|
||||||
"edit_attributes_success": "Atributos do contato atualizados com sucesso",
|
"edit_attributes_success": "Atributos do contato atualizados com sucesso",
|
||||||
"generate_personal_link": "Gerar link pessoal",
|
"generate_personal_link": "Gerar link pessoal",
|
||||||
"generate_personal_link_description": "Selecione uma pesquisa publicada para gerar um link personalizado para este contato.",
|
"generate_personal_link_description": "Selecione uma pesquisa publicada para gerar um link personalizado para este contato.",
|
||||||
"invalid_csv_column_names": "Nome(s) de coluna CSV inválido(s): {columns}. Os nomes de colunas que se tornarão novos atributos devem conter apenas letras minúsculas, números e sublinhados, e devem começar com uma letra.",
|
|
||||||
"invalid_date_format": "Formato de data inválido. Por favor, use uma data válida.",
|
|
||||||
"invalid_number_format": "Formato de número inválido. Por favor, insira um número válido.",
|
|
||||||
"no_activity_yet": "Nenhuma atividade ainda",
|
|
||||||
"no_published_link_surveys_available": "Não há pesquisas de link publicadas disponíveis. Por favor, publique uma pesquisa de link primeiro.",
|
"no_published_link_surveys_available": "Não há pesquisas de link publicadas disponíveis. Por favor, publique uma pesquisa de link primeiro.",
|
||||||
"no_published_surveys": "Sem pesquisas publicadas",
|
"no_published_surveys": "Sem pesquisas publicadas",
|
||||||
"no_responses_found": "Nenhuma resposta encontrada",
|
"no_responses_found": "Nenhuma resposta encontrada",
|
||||||
"not_provided": "Não fornecido",
|
"not_provided": "Não fornecido",
|
||||||
"number_value_required": "O valor numérico é obrigatório. Use o botão excluir para remover este atributo.",
|
|
||||||
"personal_link_generated": "Link pessoal gerado com sucesso",
|
"personal_link_generated": "Link pessoal gerado com sucesso",
|
||||||
"personal_link_generated_but_clipboard_failed": "Link pessoal gerado, mas falha ao copiar para a área de transferência: {url}",
|
"personal_link_generated_but_clipboard_failed": "Link pessoal gerado, mas falha ao copiar para a área de transferência: {url}",
|
||||||
"personal_survey_link": "Link da pesquisa pessoal",
|
"personal_survey_link": "Link da pesquisa pessoal",
|
||||||
@@ -683,24 +656,13 @@
|
|||||||
"search_contact": "Buscar contato",
|
"search_contact": "Buscar contato",
|
||||||
"select_a_survey": "Selecione uma pesquisa",
|
"select_a_survey": "Selecione uma pesquisa",
|
||||||
"select_attribute": "Selecionar Atributo",
|
"select_attribute": "Selecionar Atributo",
|
||||||
"select_attribute_key": "Selecionar chave de atributo",
|
|
||||||
"survey_viewed": "Pesquisa visualizada",
|
|
||||||
"survey_viewed_at": "Visualizada em",
|
|
||||||
"system_attributes": "Atributos do sistema",
|
|
||||||
"unlock_contacts_description": "Gerencie contatos e envie pesquisas direcionadas",
|
"unlock_contacts_description": "Gerencie contatos e envie pesquisas direcionadas",
|
||||||
"unlock_contacts_title": "Desbloqueie contatos com um plano superior",
|
"unlock_contacts_title": "Desbloqueie contatos com um plano superior",
|
||||||
"upload_contacts_error_attribute_type_mismatch": "O atributo \"{key}\" está tipado como \"{dataType}\", mas o CSV contém valores inválidos: {values}",
|
|
||||||
"upload_contacts_error_duplicate_mappings": "Mapeamentos duplicados encontrados para os seguintes atributos: {attributes}",
|
|
||||||
"upload_contacts_error_file_too_large": "O tamanho do arquivo excede o limite máximo de 800KB",
|
|
||||||
"upload_contacts_error_generic": "Ocorreu um erro ao fazer upload dos contatos. Por favor, tente novamente mais tarde.",
|
|
||||||
"upload_contacts_error_invalid_file_type": "Por favor, faça upload de um arquivo CSV",
|
|
||||||
"upload_contacts_error_no_valid_contacts": "O arquivo CSV enviado não contém nenhum contato válido, por favor veja o arquivo CSV de exemplo para o formato correto.",
|
|
||||||
"upload_contacts_modal_attribute_header": "Atributo do Formbricks",
|
|
||||||
"upload_contacts_modal_attributes_description": "Mapeie as colunas do seu CSV para os atributos no Formbricks.",
|
"upload_contacts_modal_attributes_description": "Mapeie as colunas do seu CSV para os atributos no Formbricks.",
|
||||||
"upload_contacts_modal_attributes_new": "Novo atributo",
|
"upload_contacts_modal_attributes_new": "Novo atributo",
|
||||||
"upload_contacts_modal_attributes_search_or_add": "Buscar ou adicionar atributo",
|
"upload_contacts_modal_attributes_search_or_add": "Buscar ou adicionar atributo",
|
||||||
|
"upload_contacts_modal_attributes_should_be_mapped_to": "deve ser mapeado para",
|
||||||
"upload_contacts_modal_attributes_title": "Atributos",
|
"upload_contacts_modal_attributes_title": "Atributos",
|
||||||
"upload_contacts_modal_csv_column_header": "Coluna CSV",
|
|
||||||
"upload_contacts_modal_description": "Faça upload de um CSV para importar contatos com atributos rapidamente",
|
"upload_contacts_modal_description": "Faça upload de um CSV para importar contatos com atributos rapidamente",
|
||||||
"upload_contacts_modal_download_example_csv": "Baixar exemplo de CSV",
|
"upload_contacts_modal_download_example_csv": "Baixar exemplo de CSV",
|
||||||
"upload_contacts_modal_duplicates_description": "O que devemos fazer se um contato já existir nos seus contatos?",
|
"upload_contacts_modal_duplicates_description": "O que devemos fazer se um contato já existir nos seus contatos?",
|
||||||
@@ -757,12 +719,7 @@
|
|||||||
"link_google_sheet": "Link da Planilha do Google",
|
"link_google_sheet": "Link da Planilha do Google",
|
||||||
"link_new_sheet": "Vincular nova planilha",
|
"link_new_sheet": "Vincular nova planilha",
|
||||||
"no_integrations_yet": "Suas integrações do Google Sheets vão aparecer aqui assim que você adicioná-las. ⏲️",
|
"no_integrations_yet": "Suas integrações do Google Sheets vão aparecer aqui assim que você adicioná-las. ⏲️",
|
||||||
"reconnect_button": "Reconectar",
|
"spreadsheet_url": "URL da planilha"
|
||||||
"reconnect_button_description": "Sua conexão com o Google Sheets expirou. Reconecte para continuar sincronizando respostas. Seus links de planilhas e dados existentes serão preservados.",
|
|
||||||
"reconnect_button_tooltip": "Reconecte a integração para atualizar seu acesso. Seus links de planilhas e dados existentes serão preservados.",
|
|
||||||
"spreadsheet_permission_error": "Você não tem permissão para acessar esta planilha. Certifique-se de que a planilha está compartilhada com sua conta do Google e que você tem acesso de escrita à planilha.",
|
|
||||||
"spreadsheet_url": "URL da planilha",
|
|
||||||
"token_expired_error": "O token de atualização do Google Sheets expirou ou foi revogado. Reconecte a integração."
|
|
||||||
},
|
},
|
||||||
"include_created_at": "Incluir Data de Criação",
|
"include_created_at": "Incluir Data de Criação",
|
||||||
"include_hidden_fields": "Incluir Campos Ocultos",
|
"include_hidden_fields": "Incluir Campos Ocultos",
|
||||||
@@ -886,40 +843,6 @@
|
|||||||
"no_attributes_yet": "Ainda não tem atributos!",
|
"no_attributes_yet": "Ainda não tem atributos!",
|
||||||
"no_filters_yet": "Ainda não tem filtros!",
|
"no_filters_yet": "Ainda não tem filtros!",
|
||||||
"no_segments_yet": "Você não tem segmentos salvos no momento.",
|
"no_segments_yet": "Você não tem segmentos salvos no momento.",
|
||||||
"operator_contains": "contém",
|
|
||||||
"operator_does_not_contain": "não contém",
|
|
||||||
"operator_ends_with": "termina com",
|
|
||||||
"operator_is_after": "é depois",
|
|
||||||
"operator_is_before": "é antes",
|
|
||||||
"operator_is_between": "está entre",
|
|
||||||
"operator_is_newer_than": "é mais recente que",
|
|
||||||
"operator_is_not_set": "não está definido",
|
|
||||||
"operator_is_older_than": "é mais antigo que",
|
|
||||||
"operator_is_same_day": "é no mesmo dia",
|
|
||||||
"operator_is_set": "está definido",
|
|
||||||
"operator_starts_with": "começa com",
|
|
||||||
"operator_title_contains": "Contém",
|
|
||||||
"operator_title_does_not_contain": "Não contém",
|
|
||||||
"operator_title_ends_with": "Termina com",
|
|
||||||
"operator_title_equals": "Igual",
|
|
||||||
"operator_title_greater_equal": "Maior ou igual a",
|
|
||||||
"operator_title_greater_than": "Maior que",
|
|
||||||
"operator_title_is_after": "É depois",
|
|
||||||
"operator_title_is_before": "É antes",
|
|
||||||
"operator_title_is_between": "Está entre",
|
|
||||||
"operator_title_is_newer_than": "É mais recente que",
|
|
||||||
"operator_title_is_not_set": "Não está definido",
|
|
||||||
"operator_title_is_older_than": "É mais antigo que",
|
|
||||||
"operator_title_is_same_day": "É no mesmo dia",
|
|
||||||
"operator_title_is_set": "Está definido",
|
|
||||||
"operator_title_less_equal": "Menor ou igual a",
|
|
||||||
"operator_title_less_than": "Menor que",
|
|
||||||
"operator_title_not_equals": "Diferente de",
|
|
||||||
"operator_title_starts_with": "Começa com",
|
|
||||||
"operator_title_user_is_in": "Usuário está em",
|
|
||||||
"operator_title_user_is_not_in": "Usuário não está em",
|
|
||||||
"operator_user_is_in": "Usuário está em",
|
|
||||||
"operator_user_is_not_in": "Usuário não está em",
|
|
||||||
"person_and_attributes": "Pessoa & Atributos",
|
"person_and_attributes": "Pessoa & Atributos",
|
||||||
"phone": "Celular",
|
"phone": "Celular",
|
||||||
"please_remove_the_segment_from_these_surveys_in_order_to_delete_it": "Por favor, remova o segmento dessas pesquisas para deletá-lo.",
|
"please_remove_the_segment_from_these_surveys_in_order_to_delete_it": "Por favor, remova o segmento dessas pesquisas para deletá-lo.",
|
||||||
@@ -944,7 +867,6 @@
|
|||||||
"user_targeting_is_currently_only_available_when": "A segmentação de usuários está disponível apenas quando",
|
"user_targeting_is_currently_only_available_when": "A segmentação de usuários está disponível apenas quando",
|
||||||
"value_cannot_be_empty": "O valor não pode estar vazio.",
|
"value_cannot_be_empty": "O valor não pode estar vazio.",
|
||||||
"value_must_be_a_number": "O valor deve ser um número.",
|
"value_must_be_a_number": "O valor deve ser um número.",
|
||||||
"value_must_be_positive": "O valor deve ser um número positivo.",
|
|
||||||
"view_filters": "Ver filtros",
|
"view_filters": "Ver filtros",
|
||||||
"where": "Onde",
|
"where": "Onde",
|
||||||
"with_the_formbricks_sdk": "com o SDK do Formbricks."
|
"with_the_formbricks_sdk": "com o SDK do Formbricks."
|
||||||
@@ -1031,32 +953,19 @@
|
|||||||
"enterprise_features": "Recursos Empresariais",
|
"enterprise_features": "Recursos Empresariais",
|
||||||
"get_an_enterprise_license_to_get_access_to_all_features": "Adquira uma licença Enterprise para ter acesso a todos os recursos.",
|
"get_an_enterprise_license_to_get_access_to_all_features": "Adquira uma licença Enterprise para ter acesso a todos os recursos.",
|
||||||
"keep_full_control_over_your_data_privacy_and_security": "Mantenha controle total sobre a privacidade e segurança dos seus dados.",
|
"keep_full_control_over_your_data_privacy_and_security": "Mantenha controle total sobre a privacidade e segurança dos seus dados.",
|
||||||
"license_invalid_description": "A chave de licença na sua variável de ambiente ENTERPRISE_LICENSE_KEY não é válida. Verifique se há erros de digitação ou solicite uma nova chave.",
|
|
||||||
"license_status": "Status da licença",
|
|
||||||
"license_status_active": "Ativa",
|
|
||||||
"license_status_description": "Status da sua licença enterprise.",
|
|
||||||
"license_status_expired": "Expirada",
|
|
||||||
"license_status_invalid": "Licença inválida",
|
|
||||||
"license_status_unreachable": "Inacessível",
|
|
||||||
"license_unreachable_grace_period": "O servidor de licenças não pode ser alcançado. Seus recursos empresariais permanecem ativos durante um período de carência de 3 dias que termina em {gracePeriodEnd}.",
|
|
||||||
"no_call_needed_no_strings_attached_request_a_free_30_day_trial_license_to_test_all_features_by_filling_out_this_form": "Sem necessidade de ligação, sem compromisso: Solicite uma licença de teste gratuita de 30 dias para testar todas as funcionalidades preenchendo este formulário:",
|
"no_call_needed_no_strings_attached_request_a_free_30_day_trial_license_to_test_all_features_by_filling_out_this_form": "Sem necessidade de ligação, sem compromisso: Solicite uma licença de teste gratuita de 30 dias para testar todas as funcionalidades preenchendo este formulário:",
|
||||||
"no_credit_card_no_sales_call_just_test_it": "Sem cartão de crédito. Sem ligação de vendas. Só teste :)",
|
"no_credit_card_no_sales_call_just_test_it": "Sem cartão de crédito. Sem ligação de vendas. Só teste :)",
|
||||||
"on_request": "Quando solicitado",
|
"on_request": "Quando solicitado",
|
||||||
"organization_roles": "Funções na Organização (Admin, Editor, Desenvolvedor, etc.)",
|
"organization_roles": "Funções na Organização (Admin, Editor, Desenvolvedor, etc.)",
|
||||||
"questions_please_reach_out_to": "Perguntas? Entre em contato com",
|
"questions_please_reach_out_to": "Perguntas? Entre em contato com",
|
||||||
"recheck_license": "Verificar licença novamente",
|
|
||||||
"recheck_license_failed": "Falha na verificação da licença. O servidor de licenças pode estar inacessível.",
|
|
||||||
"recheck_license_invalid": "A chave de licença é inválida. Verifique sua ENTERPRISE_LICENSE_KEY.",
|
|
||||||
"recheck_license_success": "Verificação da licença bem-sucedida",
|
|
||||||
"recheck_license_unreachable": "Servidor de licenças inacessível. Por favor, tente novamente mais tarde.",
|
|
||||||
"rechecking": "Verificando novamente...",
|
|
||||||
"request_30_day_trial_license": "Pedir Licença de Teste de 30 Dias",
|
"request_30_day_trial_license": "Pedir Licença de Teste de 30 Dias",
|
||||||
"saml_sso": "SSO SAML",
|
"saml_sso": "SSO SAML",
|
||||||
"service_level_agreement": "Acordo de Nível de Serviço",
|
"service_level_agreement": "Acordo de Nível de Serviço",
|
||||||
"soc2_hipaa_iso_27001_compliance_check": "Verificação de conformidade SOC2, HIPAA, ISO 27001",
|
"soc2_hipaa_iso_27001_compliance_check": "Verificação de conformidade SOC2, HIPAA, ISO 27001",
|
||||||
"sso": "SSO (Google, Microsoft, OpenID Connect)",
|
"sso": "SSO (Google, Microsoft, OpenID Connect)",
|
||||||
"teams": "Equipes e Funções de Acesso (Ler, Ler e Escrever, Gerenciar)",
|
"teams": "Equipes e Funções de Acesso (Ler, Ler e Escrever, Gerenciar)",
|
||||||
"unlock_the_full_power_of_formbricks_free_for_30_days": "Desbloqueie todo o poder do Formbricks. Grátis por 30 dias."
|
"unlock_the_full_power_of_formbricks_free_for_30_days": "Desbloqueie todo o poder do Formbricks. Grátis por 30 dias.",
|
||||||
|
"your_enterprise_license_is_active_all_features_unlocked": "Sua licença empresarial está ativa. Todos os recursos estão desbloqueados."
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"bulk_invite_warning_description": "Por favor, note que no Plano Gratuito, todos os membros da organização são automaticamente atribuídos ao papel de 'Owner', independentemente do papel especificado no arquivo CSV.",
|
"bulk_invite_warning_description": "Por favor, note que no Plano Gratuito, todos os membros da organização são automaticamente atribuídos ao papel de 'Owner', independentemente do papel especificado no arquivo CSV.",
|
||||||
@@ -1080,7 +989,7 @@
|
|||||||
"from_your_organization": "da sua organização",
|
"from_your_organization": "da sua organização",
|
||||||
"invitation_sent_once_more": "Convite enviado de novo.",
|
"invitation_sent_once_more": "Convite enviado de novo.",
|
||||||
"invite_deleted_successfully": "Convite deletado com sucesso",
|
"invite_deleted_successfully": "Convite deletado com sucesso",
|
||||||
"invite_expires_on": "O convite expira em {date}",
|
"invited_on": "Convidado em {date}",
|
||||||
"invites_failed": "Convites falharam",
|
"invites_failed": "Convites falharam",
|
||||||
"leave_organization": "Sair da organização",
|
"leave_organization": "Sair da organização",
|
||||||
"leave_organization_description": "Você vai sair dessa organização e perder acesso a todas as pesquisas e respostas. Você só pode voltar se for convidado de novo.",
|
"leave_organization_description": "Você vai sair dessa organização e perder acesso a todas as pesquisas e respostas. Você só pode voltar se for convidado de novo.",
|
||||||
@@ -1193,6 +1102,8 @@
|
|||||||
"please_fill_all_workspace_fields": "Preencha todos os campos para adicionar um novo espaço de trabalho.",
|
"please_fill_all_workspace_fields": "Preencha todos os campos para adicionar um novo espaço de trabalho.",
|
||||||
"read": "Leitura",
|
"read": "Leitura",
|
||||||
"read_write": "Leitura & Escrita",
|
"read_write": "Leitura & Escrita",
|
||||||
|
"select_member": "Selecionar membro",
|
||||||
|
"select_workspace": "Selecionar espaço de trabalho",
|
||||||
"team_admin": "Administrador da equipe",
|
"team_admin": "Administrador da equipe",
|
||||||
"team_created_successfully": "Equipe criada com sucesso.",
|
"team_created_successfully": "Equipe criada com sucesso.",
|
||||||
"team_deleted_successfully": "Equipe excluída com sucesso.",
|
"team_deleted_successfully": "Equipe excluída com sucesso.",
|
||||||
@@ -1242,6 +1153,7 @@
|
|||||||
"add_fallback_placeholder": "Adicionar um texto padrão para mostrar se a pergunta for ignorada:",
|
"add_fallback_placeholder": "Adicionar um texto padrão para mostrar se a pergunta for ignorada:",
|
||||||
"add_hidden_field_id": "Adicionar campo oculto ID",
|
"add_hidden_field_id": "Adicionar campo oculto ID",
|
||||||
"add_highlight_border": "Adicionar borda de destaque",
|
"add_highlight_border": "Adicionar borda de destaque",
|
||||||
|
"add_highlight_border_description": "Adicione uma borda externa ao seu cartão de pesquisa.",
|
||||||
"add_logic": "Adicionar lógica",
|
"add_logic": "Adicionar lógica",
|
||||||
"add_none_of_the_above": "Adicionar \"Nenhuma das opções acima\"",
|
"add_none_of_the_above": "Adicionar \"Nenhuma das opções acima\"",
|
||||||
"add_option": "Adicionar opção",
|
"add_option": "Adicionar opção",
|
||||||
@@ -1280,7 +1192,6 @@
|
|||||||
"block_duplicated": "Bloco duplicado.",
|
"block_duplicated": "Bloco duplicado.",
|
||||||
"bold": "Negrito",
|
"bold": "Negrito",
|
||||||
"brand_color": "Cor da marca",
|
"brand_color": "Cor da marca",
|
||||||
"brand_color_description": "Aplicado a botões, links e destaques.",
|
|
||||||
"brightness": "brilho",
|
"brightness": "brilho",
|
||||||
"bulk_edit": "Edição em massa",
|
"bulk_edit": "Edição em massa",
|
||||||
"bulk_edit_description": "Edite todas as opções abaixo, uma por linha. Linhas vazias serão ignoradas e duplicatas removidas.",
|
"bulk_edit_description": "Edite todas as opções abaixo, uma por linha. Linhas vazias serão ignoradas e duplicatas removidas.",
|
||||||
@@ -1298,9 +1209,7 @@
|
|||||||
"capture_new_action": "Capturar nova ação",
|
"capture_new_action": "Capturar nova ação",
|
||||||
"card_arrangement_for_survey_type_derived": "Arranjo de Cartões para Pesquisas {surveyTypeDerived}",
|
"card_arrangement_for_survey_type_derived": "Arranjo de Cartões para Pesquisas {surveyTypeDerived}",
|
||||||
"card_background_color": "Cor de fundo do cartão",
|
"card_background_color": "Cor de fundo do cartão",
|
||||||
"card_background_color_description": "Preenche a área do cartão da pesquisa.",
|
|
||||||
"card_border_color": "Cor da borda do cartão",
|
"card_border_color": "Cor da borda do cartão",
|
||||||
"card_border_color_description": "Contorna o cartão da pesquisa.",
|
|
||||||
"card_styling": "Estilo do cartão",
|
"card_styling": "Estilo do cartão",
|
||||||
"casual": "Casual",
|
"casual": "Casual",
|
||||||
"caution_edit_duplicate": "Duplicar e editar",
|
"caution_edit_duplicate": "Duplicar e editar",
|
||||||
@@ -1311,12 +1220,20 @@
|
|||||||
"caution_explanation_responses_are_safe": "Respostas antigas e novas são misturadas, o que pode levar a resumos de dados enganosos.",
|
"caution_explanation_responses_are_safe": "Respostas antigas e novas são misturadas, o que pode levar a resumos de dados enganosos.",
|
||||||
"caution_recommendation": "Isso pode causar inconsistências de dados no resumo da pesquisa. Recomendamos duplicar a pesquisa em vez disso.",
|
"caution_recommendation": "Isso pode causar inconsistências de dados no resumo da pesquisa. Recomendamos duplicar a pesquisa em vez disso.",
|
||||||
"caution_text": "Mudanças vão levar a inconsistências",
|
"caution_text": "Mudanças vão levar a inconsistências",
|
||||||
|
"centered_modal_overlay_color": "cor de sobreposição modal centralizada",
|
||||||
"change_anyway": "Mudar mesmo assim",
|
"change_anyway": "Mudar mesmo assim",
|
||||||
"change_background": "Mudar fundo",
|
"change_background": "Mudar fundo",
|
||||||
"change_question_type": "Mudar tipo de pergunta",
|
"change_question_type": "Mudar tipo de pergunta",
|
||||||
"change_survey_type": "Alterar o tipo de pesquisa afeta o acesso existente",
|
"change_survey_type": "Alterar o tipo de pesquisa afeta o acesso existente",
|
||||||
|
"change_the_background_color_of_the_card": "Muda a cor de fundo do cartão.",
|
||||||
|
"change_the_background_color_of_the_input_fields": "Mude a cor de fundo dos campos de entrada.",
|
||||||
"change_the_background_to_a_color_image_or_animation": "Mude o fundo para uma cor, imagem ou animação.",
|
"change_the_background_to_a_color_image_or_animation": "Mude o fundo para uma cor, imagem ou animação.",
|
||||||
|
"change_the_border_color_of_the_card": "Muda a cor da borda do cartão.",
|
||||||
|
"change_the_border_color_of_the_input_fields": "Mude a cor da borda dos campos de entrada.",
|
||||||
|
"change_the_border_radius_of_the_card_and_the_inputs": "Muda o raio da borda do card e dos inputs.",
|
||||||
|
"change_the_brand_color_of_the_survey": "Muda a cor da marca da pesquisa.",
|
||||||
"change_the_placement_of_this_survey": "Muda a posição dessa pesquisa.",
|
"change_the_placement_of_this_survey": "Muda a posição dessa pesquisa.",
|
||||||
|
"change_the_question_color_of_the_survey": "Muda a cor da pergunta da pesquisa.",
|
||||||
"changes_saved": "Mudanças salvas.",
|
"changes_saved": "Mudanças salvas.",
|
||||||
"changing_survey_type_will_remove_existing_distribution_channels": "Alterar o tipo de pesquisa afetará a forma como ela pode ser compartilhada. Se os respondentes já tiverem links de acesso para o tipo atual, podem perder o acesso após a mudança.",
|
"changing_survey_type_will_remove_existing_distribution_channels": "Alterar o tipo de pesquisa afetará a forma como ela pode ser compartilhada. Se os respondentes já tiverem links de acesso para o tipo atual, podem perder o acesso após a mudança.",
|
||||||
"checkbox_label": "Rótulo da Caixa de Seleção",
|
"checkbox_label": "Rótulo da Caixa de Seleção",
|
||||||
@@ -1456,6 +1373,7 @@
|
|||||||
"hide_progress_bar": "Esconder barra de progresso",
|
"hide_progress_bar": "Esconder barra de progresso",
|
||||||
"hide_question_settings": "Ocultar configurações da pergunta",
|
"hide_question_settings": "Ocultar configurações da pergunta",
|
||||||
"hostname": "nome do host",
|
"hostname": "nome do host",
|
||||||
|
"how_funky_do_you_want_your_cards_in_survey_type_derived_surveys": "Quão descoladas você quer suas cartas em Pesquisas {surveyTypeDerived}",
|
||||||
"if_you_need_more_please": "Se você precisar de mais, por favor",
|
"if_you_need_more_please": "Se você precisar de mais, por favor",
|
||||||
"if_you_really_want_that_answer_ask_until_you_get_it": "Continuar mostrando sempre que acionada até que uma resposta seja enviada.",
|
"if_you_really_want_that_answer_ask_until_you_get_it": "Continuar mostrando sempre que acionada até que uma resposta seja enviada.",
|
||||||
"ignore_global_waiting_time": "Ignorar período de espera",
|
"ignore_global_waiting_time": "Ignorar período de espera",
|
||||||
@@ -1466,9 +1384,7 @@
|
|||||||
"initial_value": "Valor inicial",
|
"initial_value": "Valor inicial",
|
||||||
"inner_text": "Texto Interno",
|
"inner_text": "Texto Interno",
|
||||||
"input_border_color": "Cor da borda de entrada",
|
"input_border_color": "Cor da borda de entrada",
|
||||||
"input_border_color_description": "Contorna campos de texto e áreas de texto.",
|
|
||||||
"input_color": "Cor de entrada",
|
"input_color": "Cor de entrada",
|
||||||
"input_color_description": "Preenche o interior dos campos de texto.",
|
|
||||||
"insert_link": "Inserir link",
|
"insert_link": "Inserir link",
|
||||||
"invalid_targeting": "Segmentação inválida: Por favor, verifique os filtros do seu público",
|
"invalid_targeting": "Segmentação inválida: Por favor, verifique os filtros do seu público",
|
||||||
"invalid_video_url_warning": "Por favor, insira uma URL válida do YouTube, Vimeo ou Loom. No momento, não suportamos outros provedores de vídeo.",
|
"invalid_video_url_warning": "Por favor, insira uma URL válida do YouTube, Vimeo ou Loom. No momento, não suportamos outros provedores de vídeo.",
|
||||||
@@ -1552,12 +1468,13 @@
|
|||||||
"protect_survey_with_pin_description": "Somente usuários que têm o PIN podem acessar a pesquisa.",
|
"protect_survey_with_pin_description": "Somente usuários que têm o PIN podem acessar a pesquisa.",
|
||||||
"publish": "Publicar",
|
"publish": "Publicar",
|
||||||
"question": "Pergunta",
|
"question": "Pergunta",
|
||||||
|
"question_color": "Cor da pergunta",
|
||||||
"question_deleted": "Pergunta deletada.",
|
"question_deleted": "Pergunta deletada.",
|
||||||
"question_duplicated": "Pergunta duplicada.",
|
"question_duplicated": "Pergunta duplicada.",
|
||||||
"question_id_updated": "ID da pergunta atualizado",
|
"question_id_updated": "ID da pergunta atualizado",
|
||||||
"question_used_in_logic_warning_text": "Elementos deste bloco são usados em uma regra de lógica, tem certeza de que deseja excluí-lo?",
|
"question_used_in_logic_warning_text": "Elementos deste bloco são usados em uma regra de lógica, tem certeza de que deseja excluí-lo?",
|
||||||
"question_used_in_logic_warning_title": "Inconsistência de lógica",
|
"question_used_in_logic_warning_title": "Inconsistência de lógica",
|
||||||
"question_used_in_quota": "Esta pergunta está sendo usada na cota \"{quotaName}\"",
|
"question_used_in_quota": "Esta questão está sendo usada na cota \"{quotaName}\"",
|
||||||
"question_used_in_recall": "Esta pergunta está sendo recordada na pergunta {questionIndex}.",
|
"question_used_in_recall": "Esta pergunta está sendo recordada na pergunta {questionIndex}.",
|
||||||
"question_used_in_recall_ending_card": "Esta pergunta está sendo recordada no card de Encerramento",
|
"question_used_in_recall_ending_card": "Esta pergunta está sendo recordada no card de Encerramento",
|
||||||
"quotas": {
|
"quotas": {
|
||||||
@@ -1613,7 +1530,6 @@
|
|||||||
"response_limits_redirections_and_more": "Limites de resposta, redirecionamentos e mais.",
|
"response_limits_redirections_and_more": "Limites de resposta, redirecionamentos e mais.",
|
||||||
"response_options": "Opções de Resposta",
|
"response_options": "Opções de Resposta",
|
||||||
"roundness": "Circularidade",
|
"roundness": "Circularidade",
|
||||||
"roundness_description": "Controla o arredondamento dos cantos do cartão.",
|
|
||||||
"row_used_in_logic_error": "Esta linha é usada na lógica da pergunta {questionIndex}. Por favor, remova-a da lógica primeiro.",
|
"row_used_in_logic_error": "Esta linha é usada na lógica da pergunta {questionIndex}. Por favor, remova-a da lógica primeiro.",
|
||||||
"rows": "linhas",
|
"rows": "linhas",
|
||||||
"save_and_close": "Salvar e Fechar",
|
"save_and_close": "Salvar e Fechar",
|
||||||
@@ -1655,6 +1571,7 @@
|
|||||||
"styling_set_to_theme_styles": "Estilo definido para os estilos do tema",
|
"styling_set_to_theme_styles": "Estilo definido para os estilos do tema",
|
||||||
"subheading": "Subtítulo",
|
"subheading": "Subtítulo",
|
||||||
"subtract": "Subtrair -",
|
"subtract": "Subtrair -",
|
||||||
|
"suggest_colors": "Sugerir cores",
|
||||||
"survey_completed_heading": "Pesquisa Concluída",
|
"survey_completed_heading": "Pesquisa Concluída",
|
||||||
"survey_completed_subheading": "Essa pesquisa gratuita e de código aberto foi encerrada",
|
"survey_completed_subheading": "Essa pesquisa gratuita e de código aberto foi encerrada",
|
||||||
"survey_display_settings": "Configurações de Exibição da Pesquisa",
|
"survey_display_settings": "Configurações de Exibição da Pesquisa",
|
||||||
@@ -1731,7 +1648,7 @@
|
|||||||
"validation_rules": "Regras de validação",
|
"validation_rules": "Regras de validação",
|
||||||
"validation_rules_description": "Aceitar apenas respostas que atendam aos seguintes critérios",
|
"validation_rules_description": "Aceitar apenas respostas que atendam aos seguintes critérios",
|
||||||
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} está sendo usado na lógica da pergunta {questionIndex}. Por favor, remova-o da lógica primeiro.",
|
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} está sendo usado na lógica da pergunta {questionIndex}. Por favor, remova-o da lógica primeiro.",
|
||||||
"variable_is_used_in_quota_please_remove_it_from_quota_first": "A variável \"{variableName}\" está sendo usada na cota \"{quotaName}\"",
|
"variable_is_used_in_quota_please_remove_it_from_quota_first": "Variável \"{variableName}\" está sendo usada na cota \"{quotaName}\"",
|
||||||
"variable_name_conflicts_with_hidden_field": "O nome da variável está em conflito com um ID de campo oculto existente.",
|
"variable_name_conflicts_with_hidden_field": "O nome da variável está em conflito com um ID de campo oculto existente.",
|
||||||
"variable_name_is_already_taken_please_choose_another": "O nome da variável já está em uso, por favor escolha outro.",
|
"variable_name_is_already_taken_please_choose_another": "O nome da variável já está em uso, por favor escolha outro.",
|
||||||
"variable_name_must_start_with_a_letter": "O nome da variável deve começar com uma letra.",
|
"variable_name_must_start_with_a_letter": "O nome da variável deve começar com uma letra.",
|
||||||
@@ -1957,7 +1874,6 @@
|
|||||||
"filtered_responses_excel": "Respostas filtradas (Excel)",
|
"filtered_responses_excel": "Respostas filtradas (Excel)",
|
||||||
"generating_qr_code": "Gerando código QR",
|
"generating_qr_code": "Gerando código QR",
|
||||||
"impressions": "Impressões",
|
"impressions": "Impressões",
|
||||||
"impressions_identified_only": "Mostrando apenas impressões de contatos identificados",
|
|
||||||
"impressions_tooltip": "Número de vezes que a pesquisa foi visualizada.",
|
"impressions_tooltip": "Número de vezes que a pesquisa foi visualizada.",
|
||||||
"in_app": {
|
"in_app": {
|
||||||
"connection_description": "A pesquisa será exibida para usuários do seu site, que atendam aos critérios listados abaixo",
|
"connection_description": "A pesquisa será exibida para usuários do seu site, que atendam aos critérios listados abaixo",
|
||||||
@@ -2000,7 +1916,6 @@
|
|||||||
"last_quarter": "Último trimestre",
|
"last_quarter": "Último trimestre",
|
||||||
"last_year": "Último ano",
|
"last_year": "Último ano",
|
||||||
"limit": "Limite",
|
"limit": "Limite",
|
||||||
"no_identified_impressions": "Nenhuma impressão de contatos identificados",
|
|
||||||
"no_responses_found": "Nenhuma resposta encontrada",
|
"no_responses_found": "Nenhuma resposta encontrada",
|
||||||
"other_values_found": "Outros valores encontrados",
|
"other_values_found": "Outros valores encontrados",
|
||||||
"overall": "No geral",
|
"overall": "No geral",
|
||||||
@@ -2140,71 +2055,9 @@
|
|||||||
"look": {
|
"look": {
|
||||||
"add_background_color": "Adicionar cor de fundo",
|
"add_background_color": "Adicionar cor de fundo",
|
||||||
"add_background_color_description": "Adicione uma cor de fundo ao container do logo.",
|
"add_background_color_description": "Adicione uma cor de fundo ao container do logo.",
|
||||||
"advanced_styling_field_border_radius": "Raio da borda",
|
|
||||||
"advanced_styling_field_button_bg": "Fundo do botão",
|
|
||||||
"advanced_styling_field_button_bg_description": "Preenche o botão Próximo / Enviar.",
|
|
||||||
"advanced_styling_field_button_border_radius_description": "Arredonda os cantos do botão.",
|
|
||||||
"advanced_styling_field_button_font_size_description": "Ajusta o tamanho do texto do rótulo do botão.",
|
|
||||||
"advanced_styling_field_button_font_weight_description": "Torna o texto do botão mais leve ou mais negrito.",
|
|
||||||
"advanced_styling_field_button_height_description": "Controla a altura do botão.",
|
|
||||||
"advanced_styling_field_button_padding_x_description": "Adiciona espaço à esquerda e à direita.",
|
|
||||||
"advanced_styling_field_button_padding_y_description": "Adiciona espaço no topo e na base.",
|
|
||||||
"advanced_styling_field_button_text": "Texto do botão",
|
|
||||||
"advanced_styling_field_button_text_description": "Colore o rótulo dentro dos botões.",
|
|
||||||
"advanced_styling_field_description_color": "Cor da descrição",
|
|
||||||
"advanced_styling_field_description_color_description": "Colore o texto abaixo de cada título.",
|
|
||||||
"advanced_styling_field_description_size": "Tamanho da fonte da descrição",
|
|
||||||
"advanced_styling_field_description_size_description": "Ajusta o tamanho do texto da descrição.",
|
|
||||||
"advanced_styling_field_description_weight": "Peso da fonte da descrição",
|
|
||||||
"advanced_styling_field_description_weight_description": "Torna o texto da descrição mais leve ou mais negrito.",
|
|
||||||
"advanced_styling_field_font_size": "Tamanho da fonte",
|
|
||||||
"advanced_styling_field_font_weight": "Peso da fonte",
|
|
||||||
"advanced_styling_field_headline_color": "Cor do título",
|
|
||||||
"advanced_styling_field_headline_color_description": "Colore o texto principal da pergunta.",
|
|
||||||
"advanced_styling_field_headline_size": "Tamanho da fonte do título",
|
|
||||||
"advanced_styling_field_headline_size_description": "Ajusta o tamanho do texto do título.",
|
|
||||||
"advanced_styling_field_headline_weight": "Peso da fonte do título",
|
|
||||||
"advanced_styling_field_headline_weight_description": "Torna o texto do título mais leve ou mais negrito.",
|
|
||||||
"advanced_styling_field_height": "Altura mínima",
|
|
||||||
"advanced_styling_field_indicator_bg": "Fundo do indicador",
|
|
||||||
"advanced_styling_field_indicator_bg_description": "Colore a porção preenchida da barra.",
|
|
||||||
"advanced_styling_field_input_border_radius_description": "Arredonda os cantos do campo.",
|
|
||||||
"advanced_styling_field_input_font_size_description": "Ajusta o tamanho do texto digitado nos campos.",
|
|
||||||
"advanced_styling_field_input_height_description": "Controla a altura mínima do campo de entrada.",
|
|
||||||
"advanced_styling_field_input_padding_x_description": "Adiciona espaço à esquerda e à direita.",
|
|
||||||
"advanced_styling_field_input_padding_y_description": "Adiciona espaço na parte superior e inferior.",
|
|
||||||
"advanced_styling_field_input_placeholder_opacity_description": "Esmaece o texto de dica do placeholder.",
|
|
||||||
"advanced_styling_field_input_shadow_description": "Adiciona uma sombra ao redor dos campos de entrada.",
|
|
||||||
"advanced_styling_field_input_text": "Texto de entrada",
|
|
||||||
"advanced_styling_field_input_text_description": "Colore o texto digitado nos campos de entrada.",
|
|
||||||
"advanced_styling_field_option_bg": "Fundo",
|
|
||||||
"advanced_styling_field_option_bg_description": "Preenche os itens de opção.",
|
|
||||||
"advanced_styling_field_option_border_radius_description": "Arredonda os cantos das opções.",
|
|
||||||
"advanced_styling_field_option_font_size_description": "Ajusta o tamanho do texto do rótulo da opção.",
|
|
||||||
"advanced_styling_field_option_label": "Cor do rótulo",
|
|
||||||
"advanced_styling_field_option_label_description": "Colore o texto do rótulo da opção.",
|
|
||||||
"advanced_styling_field_option_padding_x_description": "Adiciona espaço à esquerda e à direita.",
|
|
||||||
"advanced_styling_field_option_padding_y_description": "Adiciona espaço na parte superior e inferior.",
|
|
||||||
"advanced_styling_field_padding_x": "Espaçamento X",
|
|
||||||
"advanced_styling_field_padding_y": "Espaçamento Y",
|
|
||||||
"advanced_styling_field_placeholder_opacity": "Opacidade do placeholder",
|
|
||||||
"advanced_styling_field_shadow": "Sombra",
|
|
||||||
"advanced_styling_field_track_bg": "Fundo da trilha",
|
|
||||||
"advanced_styling_field_track_bg_description": "Colore a porção não preenchida da barra.",
|
|
||||||
"advanced_styling_field_track_height": "Altura da trilha",
|
|
||||||
"advanced_styling_field_track_height_description": "Controla a espessura da barra de progresso.",
|
|
||||||
"advanced_styling_field_upper_label_color": "Cor do rótulo do título",
|
|
||||||
"advanced_styling_field_upper_label_color_description": "Colore o pequeno rótulo acima dos campos de entrada.",
|
|
||||||
"advanced_styling_field_upper_label_size": "Tamanho da fonte do rótulo do título",
|
|
||||||
"advanced_styling_field_upper_label_size_description": "Ajusta o tamanho do pequeno rótulo acima dos campos de entrada.",
|
|
||||||
"advanced_styling_field_upper_label_weight": "Peso da fonte do rótulo do título",
|
|
||||||
"advanced_styling_field_upper_label_weight_description": "Torna o rótulo mais leve ou mais negrito.",
|
|
||||||
"advanced_styling_section_buttons": "Botões",
|
|
||||||
"advanced_styling_section_headlines": "Títulos e descrições",
|
|
||||||
"advanced_styling_section_inputs": "Campos de entrada",
|
|
||||||
"advanced_styling_section_options": "Opções (rádio/caixa de seleção)",
|
|
||||||
"app_survey_placement": "Posicionamento da pesquisa de app",
|
"app_survey_placement": "Posicionamento da pesquisa de app",
|
||||||
"app_survey_placement_settings_description": "Altere onde as pesquisas serão exibidas em seu aplicativo web ou site.",
|
"app_survey_placement_settings_description": "Altere onde as pesquisas serão exibidas em seu aplicativo web ou site.",
|
||||||
|
"centered_modal_overlay_color": "Cor de sobreposição modal centralizada",
|
||||||
"email_customization": "Personalização de e-mail",
|
"email_customization": "Personalização de e-mail",
|
||||||
"email_customization_description": "Altere a aparência dos e-mails que o Formbricks envia em seu nome.",
|
"email_customization_description": "Altere a aparência dos e-mails que o Formbricks envia em seu nome.",
|
||||||
"enable_custom_styling": "Habilitar estilização personalizada",
|
"enable_custom_styling": "Habilitar estilização personalizada",
|
||||||
@@ -2215,9 +2068,6 @@
|
|||||||
"formbricks_branding_hidden": "A marca Formbricks está oculta.",
|
"formbricks_branding_hidden": "A marca Formbricks está oculta.",
|
||||||
"formbricks_branding_settings_description": "Adoramos seu apoio, mas entendemos se você desativar.",
|
"formbricks_branding_settings_description": "Adoramos seu apoio, mas entendemos se você desativar.",
|
||||||
"formbricks_branding_shown": "A marca Formbricks está visível.",
|
"formbricks_branding_shown": "A marca Formbricks está visível.",
|
||||||
"generate_theme_btn": "Gerar",
|
|
||||||
"generate_theme_confirmation": "Gostaria de gerar um tema de cores correspondente baseado na cor da sua marca? Isso substituirá suas configurações de cores atuais.",
|
|
||||||
"generate_theme_header": "Gerar tema de cores?",
|
|
||||||
"logo_removed_successfully": "Logo removido com sucesso",
|
"logo_removed_successfully": "Logo removido com sucesso",
|
||||||
"logo_settings_description": "Faça upload do logo da sua empresa para personalizar pesquisas e pré-visualizações de links.",
|
"logo_settings_description": "Faça upload do logo da sua empresa para personalizar pesquisas e pré-visualizações de links.",
|
||||||
"logo_updated_successfully": "Logo atualizado com sucesso",
|
"logo_updated_successfully": "Logo atualizado com sucesso",
|
||||||
@@ -2232,8 +2082,6 @@
|
|||||||
"show_formbricks_branding_in": "Mostrar marca Formbricks em pesquisas {type}",
|
"show_formbricks_branding_in": "Mostrar marca Formbricks em pesquisas {type}",
|
||||||
"show_powered_by_formbricks": "Mostrar assinatura 'Powered by Formbricks'",
|
"show_powered_by_formbricks": "Mostrar assinatura 'Powered by Formbricks'",
|
||||||
"styling_updated_successfully": "Estilo atualizado com sucesso",
|
"styling_updated_successfully": "Estilo atualizado com sucesso",
|
||||||
"suggest_colors": "Sugerir cores",
|
|
||||||
"suggested_colors_applied_please_save": "Cores sugeridas geradas com sucesso. Pressione \"Salvar\" para manter as alterações.",
|
|
||||||
"theme": "Tema",
|
"theme": "Tema",
|
||||||
"theme_settings_description": "Crie um tema de estilo para todas as pesquisas. Você pode ativar estilo personalizado para cada pesquisa."
|
"theme_settings_description": "Crie um tema de estilo para todas as pesquisas. Você pode ativar estilo personalizado para cada pesquisa."
|
||||||
},
|
},
|
||||||
@@ -2997,7 +2845,6 @@
|
|||||||
"preview_survey_question_2_choice_1_label": "Sim, me mantenha informado.",
|
"preview_survey_question_2_choice_1_label": "Sim, me mantenha informado.",
|
||||||
"preview_survey_question_2_choice_2_label": "Não, obrigado!",
|
"preview_survey_question_2_choice_2_label": "Não, obrigado!",
|
||||||
"preview_survey_question_2_headline": "Quer ficar por dentro?",
|
"preview_survey_question_2_headline": "Quer ficar por dentro?",
|
||||||
"preview_survey_question_2_subheader": "Este é um exemplo de descrição.",
|
|
||||||
"preview_survey_welcome_card_headline": "Bem-vindo!",
|
"preview_survey_welcome_card_headline": "Bem-vindo!",
|
||||||
"prioritize_features_description": "Identifique os recursos que seus usuários mais e menos precisam.",
|
"prioritize_features_description": "Identifique os recursos que seus usuários mais e menos precisam.",
|
||||||
"prioritize_features_name": "Priorizar Funcionalidades",
|
"prioritize_features_name": "Priorizar Funcionalidades",
|
||||||
|
|||||||
+23
-176
@@ -188,7 +188,6 @@
|
|||||||
"customer_success": "Sucesso do Cliente",
|
"customer_success": "Sucesso do Cliente",
|
||||||
"dark_overlay": "Sobreposição escura",
|
"dark_overlay": "Sobreposição escura",
|
||||||
"date": "Data",
|
"date": "Data",
|
||||||
"days": "dias",
|
|
||||||
"default": "Padrão",
|
"default": "Padrão",
|
||||||
"delete": "Eliminar",
|
"delete": "Eliminar",
|
||||||
"description": "Descrição",
|
"description": "Descrição",
|
||||||
@@ -218,7 +217,6 @@
|
|||||||
"error": "Erro",
|
"error": "Erro",
|
||||||
"error_component_description": "Este recurso não existe ou não tem os direitos necessários para aceder a ele.",
|
"error_component_description": "Este recurso não existe ou não tem os direitos necessários para aceder a ele.",
|
||||||
"error_component_title": "Erro ao carregar recursos",
|
"error_component_title": "Erro ao carregar recursos",
|
||||||
"error_loading_data": "Erro ao carregar dados",
|
|
||||||
"error_rate_limit_description": "Número máximo de pedidos alcançado. Por favor, tente novamente mais tarde.",
|
"error_rate_limit_description": "Número máximo de pedidos alcançado. Por favor, tente novamente mais tarde.",
|
||||||
"error_rate_limit_title": "Limite de Taxa Excedido",
|
"error_rate_limit_title": "Limite de Taxa Excedido",
|
||||||
"expand_rows": "Expandir linhas",
|
"expand_rows": "Expandir linhas",
|
||||||
@@ -277,7 +275,6 @@
|
|||||||
"mobile_overlay_app_works_best_on_desktop": "Formbricks funciona melhor num ecrã maior. Para gerir ou criar inquéritos, mude de dispositivo.",
|
"mobile_overlay_app_works_best_on_desktop": "Formbricks funciona melhor num ecrã maior. Para gerir ou criar inquéritos, mude de dispositivo.",
|
||||||
"mobile_overlay_surveys_look_good": "Não se preocupe – os seus inquéritos têm uma ótima aparência em todos os dispositivos e tamanhos de ecrã!",
|
"mobile_overlay_surveys_look_good": "Não se preocupe – os seus inquéritos têm uma ótima aparência em todos os dispositivos e tamanhos de ecrã!",
|
||||||
"mobile_overlay_title": "Oops, ecrã pequeno detectado!",
|
"mobile_overlay_title": "Oops, ecrã pequeno detectado!",
|
||||||
"months": "meses",
|
|
||||||
"move_down": "Mover para baixo",
|
"move_down": "Mover para baixo",
|
||||||
"move_up": "Mover para cima",
|
"move_up": "Mover para cima",
|
||||||
"multiple_languages": "Várias línguas",
|
"multiple_languages": "Várias línguas",
|
||||||
@@ -288,7 +285,6 @@
|
|||||||
"no_background_image_found": "Nenhuma imagem de fundo encontrada.",
|
"no_background_image_found": "Nenhuma imagem de fundo encontrada.",
|
||||||
"no_code": "Sem código",
|
"no_code": "Sem código",
|
||||||
"no_files_uploaded": "Nenhum ficheiro foi carregado",
|
"no_files_uploaded": "Nenhum ficheiro foi carregado",
|
||||||
"no_overlay": "Sem sobreposição",
|
|
||||||
"no_quotas_found": "Nenhum quota encontrado",
|
"no_quotas_found": "Nenhum quota encontrado",
|
||||||
"no_result_found": "Nenhum resultado encontrado",
|
"no_result_found": "Nenhum resultado encontrado",
|
||||||
"no_results": "Nenhum resultado",
|
"no_results": "Nenhum resultado",
|
||||||
@@ -315,7 +311,6 @@
|
|||||||
"organization_teams_not_found": "Equipas da organização não encontradas",
|
"organization_teams_not_found": "Equipas da organização não encontradas",
|
||||||
"other": "Outro",
|
"other": "Outro",
|
||||||
"others": "Outros",
|
"others": "Outros",
|
||||||
"overlay_color": "Cor da sobreposição",
|
|
||||||
"overview": "Visão geral",
|
"overview": "Visão geral",
|
||||||
"password": "Palavra-passe",
|
"password": "Palavra-passe",
|
||||||
"paused": "Em pausa",
|
"paused": "Em pausa",
|
||||||
@@ -355,7 +350,6 @@
|
|||||||
"request_trial_license": "Solicitar licença de teste",
|
"request_trial_license": "Solicitar licença de teste",
|
||||||
"reset_to_default": "Repor para o padrão",
|
"reset_to_default": "Repor para o padrão",
|
||||||
"response": "Resposta",
|
"response": "Resposta",
|
||||||
"response_id": "ID de resposta",
|
|
||||||
"responses": "Respostas",
|
"responses": "Respostas",
|
||||||
"restart": "Reiniciar",
|
"restart": "Reiniciar",
|
||||||
"role": "Função",
|
"role": "Função",
|
||||||
@@ -396,7 +390,6 @@
|
|||||||
"status": "Estado",
|
"status": "Estado",
|
||||||
"step_by_step_manual": "Manual passo a passo",
|
"step_by_step_manual": "Manual passo a passo",
|
||||||
"storage_not_configured": "Armazenamento de ficheiros não configurado, uploads provavelmente falharão",
|
"storage_not_configured": "Armazenamento de ficheiros não configurado, uploads provavelmente falharão",
|
||||||
"string": "Texto",
|
|
||||||
"styling": "Estilo",
|
"styling": "Estilo",
|
||||||
"submit": "Submeter",
|
"submit": "Submeter",
|
||||||
"summary": "Resumo",
|
"summary": "Resumo",
|
||||||
@@ -429,7 +422,6 @@
|
|||||||
"top_right": "Superior Direito",
|
"top_right": "Superior Direito",
|
||||||
"try_again": "Tente novamente",
|
"try_again": "Tente novamente",
|
||||||
"type": "Tipo",
|
"type": "Tipo",
|
||||||
"unknown_survey": "Inquérito desconhecido",
|
|
||||||
"unlock_more_workspaces_with_a_higher_plan": "Desbloqueie mais projetos com um plano superior.",
|
"unlock_more_workspaces_with_a_higher_plan": "Desbloqueie mais projetos com um plano superior.",
|
||||||
"update": "Atualizar",
|
"update": "Atualizar",
|
||||||
"updated": "Atualizado",
|
"updated": "Atualizado",
|
||||||
@@ -453,7 +445,6 @@
|
|||||||
"website_and_app_connection": "Ligação de Website e Aplicação",
|
"website_and_app_connection": "Ligação de Website e Aplicação",
|
||||||
"website_app_survey": "Inquérito do Website e da Aplicação",
|
"website_app_survey": "Inquérito do Website e da Aplicação",
|
||||||
"website_survey": "Inquérito do Website",
|
"website_survey": "Inquérito do Website",
|
||||||
"weeks": "semanas",
|
|
||||||
"welcome_card": "Cartão de boas-vindas",
|
"welcome_card": "Cartão de boas-vindas",
|
||||||
"workspace_configuration": "Configuração do projeto",
|
"workspace_configuration": "Configuração do projeto",
|
||||||
"workspace_created_successfully": "Projeto criado com sucesso",
|
"workspace_created_successfully": "Projeto criado com sucesso",
|
||||||
@@ -464,7 +455,6 @@
|
|||||||
"workspace_not_found": "Projeto não encontrado",
|
"workspace_not_found": "Projeto não encontrado",
|
||||||
"workspace_permission_not_found": "Permissão do projeto não encontrada",
|
"workspace_permission_not_found": "Permissão do projeto não encontrada",
|
||||||
"workspaces": "Projetos",
|
"workspaces": "Projetos",
|
||||||
"years": "anos",
|
|
||||||
"you": "Você",
|
"you": "Você",
|
||||||
"you_are_downgraded_to_the_community_edition": "Foi rebaixado para a Edição Comunitária.",
|
"you_are_downgraded_to_the_community_edition": "Foi rebaixado para a Edição Comunitária.",
|
||||||
"you_are_not_authorized_to_perform_this_action": "Não está autorizado a realizar esta ação.",
|
"you_are_not_authorized_to_perform_this_action": "Não está autorizado a realizar esta ação.",
|
||||||
@@ -625,7 +615,7 @@
|
|||||||
"attribute_created_successfully": "Atributo criado com sucesso",
|
"attribute_created_successfully": "Atributo criado com sucesso",
|
||||||
"attribute_description": "Descrição",
|
"attribute_description": "Descrição",
|
||||||
"attribute_description_placeholder": "Descrição breve",
|
"attribute_description_placeholder": "Descrição breve",
|
||||||
"attribute_key": "Chave",
|
"attribute_key": "Atributo",
|
||||||
"attribute_key_cannot_be_changed": "A chave não pode ser alterada após a criação",
|
"attribute_key_cannot_be_changed": "A chave não pode ser alterada após a criação",
|
||||||
"attribute_key_hint": "Apenas letras minúsculas, números e sublinhados. Deve começar com uma letra.",
|
"attribute_key_hint": "Apenas letras minúsculas, números e sublinhados. Deve começar com uma letra.",
|
||||||
"attribute_key_placeholder": "ex. data_de_nascimento",
|
"attribute_key_placeholder": "ex. data_de_nascimento",
|
||||||
@@ -636,45 +626,28 @@
|
|||||||
"attribute_updated_successfully": "Atributo atualizado com sucesso",
|
"attribute_updated_successfully": "Atributo atualizado com sucesso",
|
||||||
"attribute_value": "Valor",
|
"attribute_value": "Valor",
|
||||||
"attribute_value_placeholder": "Valor do atributo",
|
"attribute_value_placeholder": "Valor do atributo",
|
||||||
"attributes_msg_attribute_limit_exceeded": "Não foi possível criar {count} novo(s) atributo(s), pois excederia o limite máximo de {limit} classes de atributos. Os atributos existentes foram atualizados com sucesso.",
|
|
||||||
"attributes_msg_attribute_type_validation_error": "{error} (o atributo '{key}' tem dataType: {dataType})",
|
|
||||||
"attributes_msg_email_already_exists": "O email já existe para este ambiente e não foi atualizado.",
|
|
||||||
"attributes_msg_email_or_userid_required": "É necessário email ou userId. Os valores existentes foram preservados.",
|
|
||||||
"attributes_msg_new_attribute_created": "Criado novo atributo '{key}' com tipo '{dataType}'",
|
|
||||||
"attributes_msg_userid_already_exists": "O userId já existe para este ambiente e não foi atualizado.",
|
|
||||||
"contact_deleted_successfully": "Contacto eliminado com sucesso",
|
"contact_deleted_successfully": "Contacto eliminado com sucesso",
|
||||||
"contact_not_found": "Nenhum contacto encontrado",
|
"contact_not_found": "Nenhum contacto encontrado",
|
||||||
"contacts_table_refresh": "Atualizar contactos",
|
"contacts_table_refresh": "Atualizar contactos",
|
||||||
"contacts_table_refresh_success": "Contactos atualizados com sucesso",
|
"contacts_table_refresh_success": "Contactos atualizados com sucesso",
|
||||||
"create_attribute": "Criar atributo",
|
"create_attribute": "Criar atributo",
|
||||||
|
"create_key": "Criar chave",
|
||||||
"create_new_attribute": "Criar novo atributo",
|
"create_new_attribute": "Criar novo atributo",
|
||||||
"create_new_attribute_description": "Crie um novo atributo para fins de segmentação.",
|
"create_new_attribute_description": "Crie um novo atributo para fins de segmentação.",
|
||||||
"custom_attributes": "Atributos personalizados",
|
|
||||||
"data_type": "Tipo de dados",
|
|
||||||
"data_type_cannot_be_changed": "O tipo de dados não pode ser alterado após a criação",
|
|
||||||
"data_type_description": "Escolhe como este atributo deve ser armazenado e filtrado",
|
|
||||||
"date_value_required": "O valor da data é obrigatório. Usa o botão eliminar para remover este atributo se não quiseres definir uma data.",
|
|
||||||
"delete_attribute_confirmation": "{value, plural, one {Isto irá eliminar o atributo selecionado. Todos os dados de contacto associados a este atributo serão perdidos.} other {Isto irá eliminar os atributos selecionados. Todos os dados de contacto associados a estes atributos serão perdidos.}}",
|
"delete_attribute_confirmation": "{value, plural, one {Isto irá eliminar o atributo selecionado. Todos os dados de contacto associados a este atributo serão perdidos.} other {Isto irá eliminar os atributos selecionados. Todos os dados de contacto associados a estes atributos serão perdidos.}}",
|
||||||
"delete_contact_confirmation": "Isto irá eliminar todas as respostas das pesquisas e os atributos de contato associados a este contato. Qualquer direcionamento e personalização baseados nos dados deste contato serão perdidos.",
|
"delete_contact_confirmation": "Isto irá eliminar todas as respostas das pesquisas e os atributos de contato associados a este contato. Qualquer direcionamento e personalização baseados nos dados deste contato serão perdidos.",
|
||||||
"delete_contact_confirmation_with_quotas": "{value, plural, other {Isto irá eliminar todas as respostas das pesquisas e os atributos de contacto associados a este contacto. Qualquer segmentação e personalização baseados nos dados deste contacto serão perdidos. Se este contacto tiver respostas que contribuam para as quotas das pesquisas, as contagens de quotas serão reduzidas, mas os limites das quotas permanecerão inalterados.}}",
|
"delete_contact_confirmation_with_quotas": "{value, plural, other {Isto irá eliminar todas as respostas das pesquisas e os atributos de contacto associados a este contacto. Qualquer segmentação e personalização baseados nos dados deste contacto serão perdidos. Se este contacto tiver respostas que contribuam para as quotas das pesquisas, as contagens de quotas serão reduzidas, mas os limites das quotas permanecerão inalterados.}}",
|
||||||
"displays": "Visualizações",
|
|
||||||
"edit_attribute": "Editar atributo",
|
"edit_attribute": "Editar atributo",
|
||||||
"edit_attribute_description": "Atualize a etiqueta e a descrição deste atributo.",
|
"edit_attribute_description": "Atualize a etiqueta e a descrição deste atributo.",
|
||||||
"edit_attribute_values": "Editar atributos",
|
"edit_attribute_values": "Editar atributos",
|
||||||
"edit_attribute_values_description": "Altere os valores de atributos específicos para este contacto.",
|
"edit_attribute_values_description": "Altere os valores de atributos específicos para este contacto.",
|
||||||
"edit_attributes": "Editar atributos",
|
|
||||||
"edit_attributes_success": "Atributos do contacto atualizados com sucesso",
|
"edit_attributes_success": "Atributos do contacto atualizados com sucesso",
|
||||||
"generate_personal_link": "Gerar Link Pessoal",
|
"generate_personal_link": "Gerar Link Pessoal",
|
||||||
"generate_personal_link_description": "Selecione um inquérito publicado para gerar um link personalizado para este contacto.",
|
"generate_personal_link_description": "Selecione um inquérito publicado para gerar um link personalizado para este contacto.",
|
||||||
"invalid_csv_column_names": "Nome(s) de coluna CSV inválido(s): {columns}. Os nomes de colunas que se tornarão novos atributos devem conter apenas letras minúsculas, números e underscores, e devem começar com uma letra.",
|
|
||||||
"invalid_date_format": "Formato de data inválido. Por favor, usa uma data válida.",
|
|
||||||
"invalid_number_format": "Formato de número inválido. Por favor, introduz um número válido.",
|
|
||||||
"no_activity_yet": "Ainda sem atividade",
|
|
||||||
"no_published_link_surveys_available": "Não existem inquéritos de link publicados disponíveis. Por favor, publique primeiro um inquérito de link.",
|
"no_published_link_surveys_available": "Não existem inquéritos de link publicados disponíveis. Por favor, publique primeiro um inquérito de link.",
|
||||||
"no_published_surveys": "Sem inquéritos publicados",
|
"no_published_surveys": "Sem inquéritos publicados",
|
||||||
"no_responses_found": "Nenhuma resposta encontrada",
|
"no_responses_found": "Nenhuma resposta encontrada",
|
||||||
"not_provided": "Não fornecido",
|
"not_provided": "Não fornecido",
|
||||||
"number_value_required": "O valor numérico é obrigatório. Usa o botão eliminar para remover este atributo.",
|
|
||||||
"personal_link_generated": "Link pessoal gerado com sucesso",
|
"personal_link_generated": "Link pessoal gerado com sucesso",
|
||||||
"personal_link_generated_but_clipboard_failed": "Link pessoal gerado mas falha ao copiar para a área de transferência: {url}",
|
"personal_link_generated_but_clipboard_failed": "Link pessoal gerado mas falha ao copiar para a área de transferência: {url}",
|
||||||
"personal_survey_link": "Link do inquérito pessoal",
|
"personal_survey_link": "Link do inquérito pessoal",
|
||||||
@@ -683,24 +656,13 @@
|
|||||||
"search_contact": "Procurar contacto",
|
"search_contact": "Procurar contacto",
|
||||||
"select_a_survey": "Selecione um inquérito",
|
"select_a_survey": "Selecione um inquérito",
|
||||||
"select_attribute": "Selecionar Atributo",
|
"select_attribute": "Selecionar Atributo",
|
||||||
"select_attribute_key": "Selecionar chave de atributo",
|
|
||||||
"survey_viewed": "Inquérito visualizado",
|
|
||||||
"survey_viewed_at": "Visualizado em",
|
|
||||||
"system_attributes": "Atributos do sistema",
|
|
||||||
"unlock_contacts_description": "Gerir contactos e enviar inquéritos direcionados",
|
"unlock_contacts_description": "Gerir contactos e enviar inquéritos direcionados",
|
||||||
"unlock_contacts_title": "Desbloqueie os contactos com um plano superior",
|
"unlock_contacts_title": "Desbloqueie os contactos com um plano superior",
|
||||||
"upload_contacts_error_attribute_type_mismatch": "O atributo \"{key}\" está definido como \"{dataType}\", mas o CSV contém valores inválidos: {values}",
|
|
||||||
"upload_contacts_error_duplicate_mappings": "Foram encontrados mapeamentos duplicados para os seguintes atributos: {attributes}",
|
|
||||||
"upload_contacts_error_file_too_large": "O tamanho do ficheiro excede o limite máximo de 800KB",
|
|
||||||
"upload_contacts_error_generic": "Ocorreu um erro ao carregar os contactos. Por favor, tenta novamente mais tarde.",
|
|
||||||
"upload_contacts_error_invalid_file_type": "Por favor, carrega um ficheiro CSV",
|
|
||||||
"upload_contacts_error_no_valid_contacts": "O ficheiro CSV carregado não contém contactos válidos, por favor consulta o ficheiro CSV de exemplo para o formato correto.",
|
|
||||||
"upload_contacts_modal_attribute_header": "Atributo Formbricks",
|
|
||||||
"upload_contacts_modal_attributes_description": "Mapeie as colunas no seu CSV para os atributos no Formbricks.",
|
"upload_contacts_modal_attributes_description": "Mapeie as colunas no seu CSV para os atributos no Formbricks.",
|
||||||
"upload_contacts_modal_attributes_new": "Novo atributo",
|
"upload_contacts_modal_attributes_new": "Novo atributo",
|
||||||
"upload_contacts_modal_attributes_search_or_add": "Pesquisar ou adicionar atributo",
|
"upload_contacts_modal_attributes_search_or_add": "Pesquisar ou adicionar atributo",
|
||||||
|
"upload_contacts_modal_attributes_should_be_mapped_to": "deve ser mapeado para",
|
||||||
"upload_contacts_modal_attributes_title": "Atributos",
|
"upload_contacts_modal_attributes_title": "Atributos",
|
||||||
"upload_contacts_modal_csv_column_header": "Coluna CSV",
|
|
||||||
"upload_contacts_modal_description": "Carregue um ficheiro CSV para importar rapidamente contactos com atributos",
|
"upload_contacts_modal_description": "Carregue um ficheiro CSV para importar rapidamente contactos com atributos",
|
||||||
"upload_contacts_modal_download_example_csv": "Descarregar exemplo de CSV",
|
"upload_contacts_modal_download_example_csv": "Descarregar exemplo de CSV",
|
||||||
"upload_contacts_modal_duplicates_description": "Como devemos proceder se um contacto já existir nos seus contactos?",
|
"upload_contacts_modal_duplicates_description": "Como devemos proceder se um contacto já existir nos seus contactos?",
|
||||||
@@ -757,12 +719,7 @@
|
|||||||
"link_google_sheet": "Ligar Folha do Google",
|
"link_google_sheet": "Ligar Folha do Google",
|
||||||
"link_new_sheet": "Ligar nova Folha",
|
"link_new_sheet": "Ligar nova Folha",
|
||||||
"no_integrations_yet": "As suas integrações com o Google Sheets aparecerão aqui assim que as adicionar. ⏲️",
|
"no_integrations_yet": "As suas integrações com o Google Sheets aparecerão aqui assim que as adicionar. ⏲️",
|
||||||
"reconnect_button": "Reconectar",
|
"spreadsheet_url": "URL da folha de cálculo"
|
||||||
"reconnect_button_description": "A tua ligação ao Google Sheets expirou. Por favor, reconecta para continuar a sincronizar respostas. As tuas ligações de folhas de cálculo e dados existentes serão preservados.",
|
|
||||||
"reconnect_button_tooltip": "Reconecta a integração para atualizar o teu acesso. As tuas ligações de folhas de cálculo e dados existentes serão preservados.",
|
|
||||||
"spreadsheet_permission_error": "Não tens permissão para aceder a esta folha de cálculo. Por favor, certifica-te de que a folha de cálculo está partilhada com a tua conta Google e que tens acesso de escrita à folha de cálculo.",
|
|
||||||
"spreadsheet_url": "URL da folha de cálculo",
|
|
||||||
"token_expired_error": "O token de atualização do Google Sheets expirou ou foi revogado. Por favor, reconecta a integração."
|
|
||||||
},
|
},
|
||||||
"include_created_at": "Incluir Criado Em",
|
"include_created_at": "Incluir Criado Em",
|
||||||
"include_hidden_fields": "Incluir Campos Ocultos",
|
"include_hidden_fields": "Incluir Campos Ocultos",
|
||||||
@@ -886,40 +843,6 @@
|
|||||||
"no_attributes_yet": "Ainda não há atributos!",
|
"no_attributes_yet": "Ainda não há atributos!",
|
||||||
"no_filters_yet": "Ainda não há filtros!",
|
"no_filters_yet": "Ainda não há filtros!",
|
||||||
"no_segments_yet": "Atualmente, não tem segmentos guardados.",
|
"no_segments_yet": "Atualmente, não tem segmentos guardados.",
|
||||||
"operator_contains": "contém",
|
|
||||||
"operator_does_not_contain": "não contém",
|
|
||||||
"operator_ends_with": "termina com",
|
|
||||||
"operator_is_after": "é depois",
|
|
||||||
"operator_is_before": "é antes",
|
|
||||||
"operator_is_between": "está entre",
|
|
||||||
"operator_is_newer_than": "é mais recente que",
|
|
||||||
"operator_is_not_set": "não está definido",
|
|
||||||
"operator_is_older_than": "é mais antigo que",
|
|
||||||
"operator_is_same_day": "é no mesmo dia",
|
|
||||||
"operator_is_set": "está definido",
|
|
||||||
"operator_starts_with": "começa com",
|
|
||||||
"operator_title_contains": "Contém",
|
|
||||||
"operator_title_does_not_contain": "Não contém",
|
|
||||||
"operator_title_ends_with": "Termina com",
|
|
||||||
"operator_title_equals": "Igual",
|
|
||||||
"operator_title_greater_equal": "Maior ou igual a",
|
|
||||||
"operator_title_greater_than": "Maior que",
|
|
||||||
"operator_title_is_after": "É depois",
|
|
||||||
"operator_title_is_before": "É antes",
|
|
||||||
"operator_title_is_between": "Está entre",
|
|
||||||
"operator_title_is_newer_than": "É mais recente que",
|
|
||||||
"operator_title_is_not_set": "Não está definido",
|
|
||||||
"operator_title_is_older_than": "É mais antigo que",
|
|
||||||
"operator_title_is_same_day": "É no mesmo dia",
|
|
||||||
"operator_title_is_set": "Está definido",
|
|
||||||
"operator_title_less_equal": "Menor ou igual a",
|
|
||||||
"operator_title_less_than": "Menor que",
|
|
||||||
"operator_title_not_equals": "Diferente de",
|
|
||||||
"operator_title_starts_with": "Começa com",
|
|
||||||
"operator_title_user_is_in": "O utilizador está em",
|
|
||||||
"operator_title_user_is_not_in": "O utilizador não está em",
|
|
||||||
"operator_user_is_in": "O utilizador está em",
|
|
||||||
"operator_user_is_not_in": "O utilizador não está em",
|
|
||||||
"person_and_attributes": "Pessoa e Atributos",
|
"person_and_attributes": "Pessoa e Atributos",
|
||||||
"phone": "Telefone",
|
"phone": "Telefone",
|
||||||
"please_remove_the_segment_from_these_surveys_in_order_to_delete_it": "Por favor, remova o segmento destes questionários para o eliminar.",
|
"please_remove_the_segment_from_these_surveys_in_order_to_delete_it": "Por favor, remova o segmento destes questionários para o eliminar.",
|
||||||
@@ -944,7 +867,6 @@
|
|||||||
"user_targeting_is_currently_only_available_when": "A segmentação de utilizadores está atualmente disponível apenas quando",
|
"user_targeting_is_currently_only_available_when": "A segmentação de utilizadores está atualmente disponível apenas quando",
|
||||||
"value_cannot_be_empty": "O valor não pode estar vazio.",
|
"value_cannot_be_empty": "O valor não pode estar vazio.",
|
||||||
"value_must_be_a_number": "O valor deve ser um número.",
|
"value_must_be_a_number": "O valor deve ser um número.",
|
||||||
"value_must_be_positive": "O valor deve ser um número positivo.",
|
|
||||||
"view_filters": "Ver filtros",
|
"view_filters": "Ver filtros",
|
||||||
"where": "Onde",
|
"where": "Onde",
|
||||||
"with_the_formbricks_sdk": "com o SDK Formbricks"
|
"with_the_formbricks_sdk": "com o SDK Formbricks"
|
||||||
@@ -1031,32 +953,19 @@
|
|||||||
"enterprise_features": "Funcionalidades da Empresa",
|
"enterprise_features": "Funcionalidades da Empresa",
|
||||||
"get_an_enterprise_license_to_get_access_to_all_features": "Obtenha uma licença Enterprise para ter acesso a todas as funcionalidades.",
|
"get_an_enterprise_license_to_get_access_to_all_features": "Obtenha uma licença Enterprise para ter acesso a todas as funcionalidades.",
|
||||||
"keep_full_control_over_your_data_privacy_and_security": "Mantenha controlo total sobre a privacidade e segurança dos seus dados.",
|
"keep_full_control_over_your_data_privacy_and_security": "Mantenha controlo total sobre a privacidade e segurança dos seus dados.",
|
||||||
"license_invalid_description": "A chave de licença na sua variável de ambiente ENTERPRISE_LICENSE_KEY não é válida. Por favor, verifique se existem erros de digitação ou solicite uma nova chave.",
|
|
||||||
"license_status": "Estado da licença",
|
|
||||||
"license_status_active": "Ativa",
|
|
||||||
"license_status_description": "Estado da sua licença empresarial.",
|
|
||||||
"license_status_expired": "Expirada",
|
|
||||||
"license_status_invalid": "Licença inválida",
|
|
||||||
"license_status_unreachable": "Inacessível",
|
|
||||||
"license_unreachable_grace_period": "Não é possível contactar o servidor de licenças. As suas funcionalidades empresariais permanecem ativas durante um período de tolerância de 3 dias que termina a {gracePeriodEnd}.",
|
|
||||||
"no_call_needed_no_strings_attached_request_a_free_30_day_trial_license_to_test_all_features_by_filling_out_this_form": "Sem necessidade de chamada, sem compromissos: Solicite uma licença de teste gratuita de 30 dias para testar todas as funcionalidades preenchendo este formulário:",
|
"no_call_needed_no_strings_attached_request_a_free_30_day_trial_license_to_test_all_features_by_filling_out_this_form": "Sem necessidade de chamada, sem compromissos: Solicite uma licença de teste gratuita de 30 dias para testar todas as funcionalidades preenchendo este formulário:",
|
||||||
"no_credit_card_no_sales_call_just_test_it": "Sem cartão de crédito. Sem chamada de vendas. Apenas teste :)",
|
"no_credit_card_no_sales_call_just_test_it": "Sem cartão de crédito. Sem chamada de vendas. Apenas teste :)",
|
||||||
"on_request": "A pedido",
|
"on_request": "A pedido",
|
||||||
"organization_roles": "Funções da Organização (Administrador, Editor, Programador, etc.)",
|
"organization_roles": "Funções da Organização (Administrador, Editor, Programador, etc.)",
|
||||||
"questions_please_reach_out_to": "Questões? Por favor entre em contacto com",
|
"questions_please_reach_out_to": "Questões? Por favor entre em contacto com",
|
||||||
"recheck_license": "Verificar licença novamente",
|
|
||||||
"recheck_license_failed": "A verificação da licença falhou. O servidor de licenças pode estar inacessível.",
|
|
||||||
"recheck_license_invalid": "A chave de licença é inválida. Por favor, verifique a sua ENTERPRISE_LICENSE_KEY.",
|
|
||||||
"recheck_license_success": "Verificação da licença bem-sucedida",
|
|
||||||
"recheck_license_unreachable": "O servidor de licenças está inacessível. Por favor, tenta novamente mais tarde.",
|
|
||||||
"rechecking": "A verificar novamente...",
|
|
||||||
"request_30_day_trial_license": "Solicitar Licença de Teste de 30 Dias",
|
"request_30_day_trial_license": "Solicitar Licença de Teste de 30 Dias",
|
||||||
"saml_sso": "SSO SAML",
|
"saml_sso": "SSO SAML",
|
||||||
"service_level_agreement": "Acordo de Nível de Serviço",
|
"service_level_agreement": "Acordo de Nível de Serviço",
|
||||||
"soc2_hipaa_iso_27001_compliance_check": "Verificação de conformidade SOC2, HIPAA, ISO 27001",
|
"soc2_hipaa_iso_27001_compliance_check": "Verificação de conformidade SOC2, HIPAA, ISO 27001",
|
||||||
"sso": "SSO (Google, Microsoft, OpenID Connect)",
|
"sso": "SSO (Google, Microsoft, OpenID Connect)",
|
||||||
"teams": "Equipas e Funções de Acesso (Ler, Ler e Escrever, Gerir)",
|
"teams": "Equipas e Funções de Acesso (Ler, Ler e Escrever, Gerir)",
|
||||||
"unlock_the_full_power_of_formbricks_free_for_30_days": "Desbloqueie todo o poder do Formbricks. Grátis por 30 dias."
|
"unlock_the_full_power_of_formbricks_free_for_30_days": "Desbloqueie todo o poder do Formbricks. Grátis por 30 dias.",
|
||||||
|
"your_enterprise_license_is_active_all_features_unlocked": "A sua licença Enterprise está ativa. Todas as funcionalidades desbloqueadas."
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"bulk_invite_warning_description": "No plano gratuito, todos os membros da organização são sempre atribuídos ao papel de \"Proprietário\".",
|
"bulk_invite_warning_description": "No plano gratuito, todos os membros da organização são sempre atribuídos ao papel de \"Proprietário\".",
|
||||||
@@ -1080,7 +989,7 @@
|
|||||||
"from_your_organization": "da sua organização",
|
"from_your_organization": "da sua organização",
|
||||||
"invitation_sent_once_more": "Convite enviado mais uma vez.",
|
"invitation_sent_once_more": "Convite enviado mais uma vez.",
|
||||||
"invite_deleted_successfully": "Convite eliminado com sucesso",
|
"invite_deleted_successfully": "Convite eliminado com sucesso",
|
||||||
"invite_expires_on": "O convite expira em {date}",
|
"invited_on": "Convidado em {date}",
|
||||||
"invites_failed": "Convites falharam",
|
"invites_failed": "Convites falharam",
|
||||||
"leave_organization": "Sair da organização",
|
"leave_organization": "Sair da organização",
|
||||||
"leave_organization_description": "Vai sair desta organização e perder o acesso a todos os inquéritos e respostas. Só pode voltar a juntar-se se for convidado novamente.",
|
"leave_organization_description": "Vai sair desta organização e perder o acesso a todos os inquéritos e respostas. Só pode voltar a juntar-se se for convidado novamente.",
|
||||||
@@ -1193,6 +1102,8 @@
|
|||||||
"please_fill_all_workspace_fields": "Preencha todos os campos para adicionar um novo espaço de trabalho.",
|
"please_fill_all_workspace_fields": "Preencha todos os campos para adicionar um novo espaço de trabalho.",
|
||||||
"read": "Ler",
|
"read": "Ler",
|
||||||
"read_write": "Ler e Escrever",
|
"read_write": "Ler e Escrever",
|
||||||
|
"select_member": "Selecionar membro",
|
||||||
|
"select_workspace": "Selecionar espaço de trabalho",
|
||||||
"team_admin": "Administrador da Equipa",
|
"team_admin": "Administrador da Equipa",
|
||||||
"team_created_successfully": "Equipa criada com sucesso.",
|
"team_created_successfully": "Equipa criada com sucesso.",
|
||||||
"team_deleted_successfully": "Equipa eliminada com sucesso.",
|
"team_deleted_successfully": "Equipa eliminada com sucesso.",
|
||||||
@@ -1242,6 +1153,7 @@
|
|||||||
"add_fallback_placeholder": "Adicionar um espaço reservado para mostrar se não houver valor para recordar.",
|
"add_fallback_placeholder": "Adicionar um espaço reservado para mostrar se não houver valor para recordar.",
|
||||||
"add_hidden_field_id": "Adicionar ID do campo oculto",
|
"add_hidden_field_id": "Adicionar ID do campo oculto",
|
||||||
"add_highlight_border": "Adicionar borda de destaque",
|
"add_highlight_border": "Adicionar borda de destaque",
|
||||||
|
"add_highlight_border_description": "Adicione uma borda externa ao seu cartão de inquérito.",
|
||||||
"add_logic": "Adicionar lógica",
|
"add_logic": "Adicionar lógica",
|
||||||
"add_none_of_the_above": "Adicionar \"Nenhuma das Opções Acima\"",
|
"add_none_of_the_above": "Adicionar \"Nenhuma das Opções Acima\"",
|
||||||
"add_option": "Adicionar opção",
|
"add_option": "Adicionar opção",
|
||||||
@@ -1280,7 +1192,6 @@
|
|||||||
"block_duplicated": "Bloco duplicado.",
|
"block_duplicated": "Bloco duplicado.",
|
||||||
"bold": "Negrito",
|
"bold": "Negrito",
|
||||||
"brand_color": "Cor da marca",
|
"brand_color": "Cor da marca",
|
||||||
"brand_color_description": "Aplicado a botões, links e destaques.",
|
|
||||||
"brightness": "Brilho",
|
"brightness": "Brilho",
|
||||||
"bulk_edit": "Edição em massa",
|
"bulk_edit": "Edição em massa",
|
||||||
"bulk_edit_description": "Edite todas as opções abaixo, uma por linha. Linhas vazias serão ignoradas e duplicados removidos.",
|
"bulk_edit_description": "Edite todas as opções abaixo, uma por linha. Linhas vazias serão ignoradas e duplicados removidos.",
|
||||||
@@ -1298,9 +1209,7 @@
|
|||||||
"capture_new_action": "Capturar nova ação",
|
"capture_new_action": "Capturar nova ação",
|
||||||
"card_arrangement_for_survey_type_derived": "Arranjo de Cartões para Inquéritos {surveyTypeDerived}",
|
"card_arrangement_for_survey_type_derived": "Arranjo de Cartões para Inquéritos {surveyTypeDerived}",
|
||||||
"card_background_color": "Cor de fundo do cartão",
|
"card_background_color": "Cor de fundo do cartão",
|
||||||
"card_background_color_description": "Preenche a área do cartão do inquérito.",
|
|
||||||
"card_border_color": "Cor da borda do cartão",
|
"card_border_color": "Cor da borda do cartão",
|
||||||
"card_border_color_description": "Contorna o cartão do inquérito.",
|
|
||||||
"card_styling": "Estilo de cartão",
|
"card_styling": "Estilo de cartão",
|
||||||
"casual": "Casual",
|
"casual": "Casual",
|
||||||
"caution_edit_duplicate": "Duplicar e editar",
|
"caution_edit_duplicate": "Duplicar e editar",
|
||||||
@@ -1311,12 +1220,20 @@
|
|||||||
"caution_explanation_responses_are_safe": "As respostas mais antigas e mais recentes se misturam, o que pode levar a resumos de dados enganosos.",
|
"caution_explanation_responses_are_safe": "As respostas mais antigas e mais recentes se misturam, o que pode levar a resumos de dados enganosos.",
|
||||||
"caution_recommendation": "Isso pode causar inconsistências de dados no resumo do inquérito. Recomendamos duplicar o inquérito em vez disso.",
|
"caution_recommendation": "Isso pode causar inconsistências de dados no resumo do inquérito. Recomendamos duplicar o inquérito em vez disso.",
|
||||||
"caution_text": "As alterações levarão a inconsistências",
|
"caution_text": "As alterações levarão a inconsistências",
|
||||||
|
"centered_modal_overlay_color": "Cor da sobreposição modal centralizada",
|
||||||
"change_anyway": "Alterar mesmo assim",
|
"change_anyway": "Alterar mesmo assim",
|
||||||
"change_background": "Alterar fundo",
|
"change_background": "Alterar fundo",
|
||||||
"change_question_type": "Alterar tipo de pergunta",
|
"change_question_type": "Alterar tipo de pergunta",
|
||||||
"change_survey_type": "Alterar o tipo de inquérito afeta o acesso existente",
|
"change_survey_type": "Alterar o tipo de inquérito afeta o acesso existente",
|
||||||
|
"change_the_background_color_of_the_card": "Alterar a cor de fundo do cartão",
|
||||||
|
"change_the_background_color_of_the_input_fields": "Alterar a cor de fundo dos campos de entrada",
|
||||||
"change_the_background_to_a_color_image_or_animation": "Altere o fundo para uma cor, imagem ou animação",
|
"change_the_background_to_a_color_image_or_animation": "Altere o fundo para uma cor, imagem ou animação",
|
||||||
|
"change_the_border_color_of_the_card": "Alterar a cor da borda do cartão.",
|
||||||
|
"change_the_border_color_of_the_input_fields": "Alterar a cor da borda dos campos de entrada",
|
||||||
|
"change_the_border_radius_of_the_card_and_the_inputs": "Alterar o raio da borda do cartão e dos campos de entrada",
|
||||||
|
"change_the_brand_color_of_the_survey": "Alterar a cor da marca do inquérito",
|
||||||
"change_the_placement_of_this_survey": "Alterar a colocação deste inquérito.",
|
"change_the_placement_of_this_survey": "Alterar a colocação deste inquérito.",
|
||||||
|
"change_the_question_color_of_the_survey": "Alterar a cor da pergunta do inquérito",
|
||||||
"changes_saved": "Alterações guardadas.",
|
"changes_saved": "Alterações guardadas.",
|
||||||
"changing_survey_type_will_remove_existing_distribution_channels": "Alterar o tipo de inquérito afetará como ele pode ser partilhado. Se os respondentes já tiverem links de acesso para o tipo atual, podem perder o acesso após a mudança.",
|
"changing_survey_type_will_remove_existing_distribution_channels": "Alterar o tipo de inquérito afetará como ele pode ser partilhado. Se os respondentes já tiverem links de acesso para o tipo atual, podem perder o acesso após a mudança.",
|
||||||
"checkbox_label": "Rótulo da Caixa de Seleção",
|
"checkbox_label": "Rótulo da Caixa de Seleção",
|
||||||
@@ -1456,6 +1373,7 @@
|
|||||||
"hide_progress_bar": "Ocultar barra de progresso",
|
"hide_progress_bar": "Ocultar barra de progresso",
|
||||||
"hide_question_settings": "Ocultar definições da pergunta",
|
"hide_question_settings": "Ocultar definições da pergunta",
|
||||||
"hostname": "Nome do host",
|
"hostname": "Nome do host",
|
||||||
|
"how_funky_do_you_want_your_cards_in_survey_type_derived_surveys": "Quão extravagantes quer os seus cartões em Inquéritos {surveyTypeDerived}",
|
||||||
"if_you_need_more_please": "Se precisar de mais, por favor",
|
"if_you_need_more_please": "Se precisar de mais, por favor",
|
||||||
"if_you_really_want_that_answer_ask_until_you_get_it": "Continuar a mostrar sempre que acionado até que uma resposta seja submetida.",
|
"if_you_really_want_that_answer_ask_until_you_get_it": "Continuar a mostrar sempre que acionado até que uma resposta seja submetida.",
|
||||||
"ignore_global_waiting_time": "Ignorar período de espera",
|
"ignore_global_waiting_time": "Ignorar período de espera",
|
||||||
@@ -1466,9 +1384,7 @@
|
|||||||
"initial_value": "Valor inicial",
|
"initial_value": "Valor inicial",
|
||||||
"inner_text": "Texto Interno",
|
"inner_text": "Texto Interno",
|
||||||
"input_border_color": "Cor da borda do campo de entrada",
|
"input_border_color": "Cor da borda do campo de entrada",
|
||||||
"input_border_color_description": "Contorna campos de texto e áreas de texto.",
|
|
||||||
"input_color": "Cor do campo de entrada",
|
"input_color": "Cor do campo de entrada",
|
||||||
"input_color_description": "Preenche o interior dos campos de texto.",
|
|
||||||
"insert_link": "Inserir ligação",
|
"insert_link": "Inserir ligação",
|
||||||
"invalid_targeting": "Segmentação inválida: Por favor, verifique os seus filtros de audiência",
|
"invalid_targeting": "Segmentação inválida: Por favor, verifique os seus filtros de audiência",
|
||||||
"invalid_video_url_warning": "Por favor, insira um URL válido do YouTube, Vimeo ou Loom. Atualmente, não suportamos outros fornecedores de hospedagem de vídeo.",
|
"invalid_video_url_warning": "Por favor, insira um URL válido do YouTube, Vimeo ou Loom. Atualmente, não suportamos outros fornecedores de hospedagem de vídeo.",
|
||||||
@@ -1552,6 +1468,7 @@
|
|||||||
"protect_survey_with_pin_description": "Apenas utilizadores com o PIN podem aceder ao inquérito.",
|
"protect_survey_with_pin_description": "Apenas utilizadores com o PIN podem aceder ao inquérito.",
|
||||||
"publish": "Publicar",
|
"publish": "Publicar",
|
||||||
"question": "Pergunta",
|
"question": "Pergunta",
|
||||||
|
"question_color": "Cor da pergunta",
|
||||||
"question_deleted": "Pergunta eliminada.",
|
"question_deleted": "Pergunta eliminada.",
|
||||||
"question_duplicated": "Pergunta duplicada.",
|
"question_duplicated": "Pergunta duplicada.",
|
||||||
"question_id_updated": "ID da pergunta atualizado",
|
"question_id_updated": "ID da pergunta atualizado",
|
||||||
@@ -1613,7 +1530,6 @@
|
|||||||
"response_limits_redirections_and_more": "Limites de resposta, redirecionamentos e mais.",
|
"response_limits_redirections_and_more": "Limites de resposta, redirecionamentos e mais.",
|
||||||
"response_options": "Opções de Resposta",
|
"response_options": "Opções de Resposta",
|
||||||
"roundness": "Arredondamento",
|
"roundness": "Arredondamento",
|
||||||
"roundness_description": "Controla o arredondamento dos cantos do cartão.",
|
|
||||||
"row_used_in_logic_error": "Esta linha é usada na lógica da pergunta {questionIndex}. Por favor, remova-a da lógica primeiro.",
|
"row_used_in_logic_error": "Esta linha é usada na lógica da pergunta {questionIndex}. Por favor, remova-a da lógica primeiro.",
|
||||||
"rows": "Linhas",
|
"rows": "Linhas",
|
||||||
"save_and_close": "Guardar e Fechar",
|
"save_and_close": "Guardar e Fechar",
|
||||||
@@ -1655,6 +1571,7 @@
|
|||||||
"styling_set_to_theme_styles": "Estilo definido para estilos do tema",
|
"styling_set_to_theme_styles": "Estilo definido para estilos do tema",
|
||||||
"subheading": "Subtítulo",
|
"subheading": "Subtítulo",
|
||||||
"subtract": "Subtrair -",
|
"subtract": "Subtrair -",
|
||||||
|
"suggest_colors": "Sugerir cores",
|
||||||
"survey_completed_heading": "Inquérito Concluído",
|
"survey_completed_heading": "Inquérito Concluído",
|
||||||
"survey_completed_subheading": "Este inquérito gratuito e de código aberto foi encerrado",
|
"survey_completed_subheading": "Este inquérito gratuito e de código aberto foi encerrado",
|
||||||
"survey_display_settings": "Configurações de Exibição do Inquérito",
|
"survey_display_settings": "Configurações de Exibição do Inquérito",
|
||||||
@@ -1731,7 +1648,7 @@
|
|||||||
"validation_rules": "Regras de validação",
|
"validation_rules": "Regras de validação",
|
||||||
"validation_rules_description": "Aceitar apenas respostas que cumpram os seguintes critérios",
|
"validation_rules_description": "Aceitar apenas respostas que cumpram os seguintes critérios",
|
||||||
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} é usada na lógica da pergunta {questionIndex}. Por favor, remova-a da lógica primeiro.",
|
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} é usada na lógica da pergunta {questionIndex}. Por favor, remova-a da lógica primeiro.",
|
||||||
"variable_is_used_in_quota_please_remove_it_from_quota_first": "A variável \"{variableName}\" está a ser usada na quota \"{quotaName}\"",
|
"variable_is_used_in_quota_please_remove_it_from_quota_first": "Variável \"{variableName}\" está a ser utilizada na quota \"{quotaName}\"",
|
||||||
"variable_name_conflicts_with_hidden_field": "O nome da variável está em conflito com um ID de campo oculto existente.",
|
"variable_name_conflicts_with_hidden_field": "O nome da variável está em conflito com um ID de campo oculto existente.",
|
||||||
"variable_name_is_already_taken_please_choose_another": "O nome da variável já está em uso, por favor escolha outro.",
|
"variable_name_is_already_taken_please_choose_another": "O nome da variável já está em uso, por favor escolha outro.",
|
||||||
"variable_name_must_start_with_a_letter": "O nome da variável deve começar com uma letra.",
|
"variable_name_must_start_with_a_letter": "O nome da variável deve começar com uma letra.",
|
||||||
@@ -1957,7 +1874,6 @@
|
|||||||
"filtered_responses_excel": "Respostas filtradas (Excel)",
|
"filtered_responses_excel": "Respostas filtradas (Excel)",
|
||||||
"generating_qr_code": "A gerar código QR",
|
"generating_qr_code": "A gerar código QR",
|
||||||
"impressions": "Impressões",
|
"impressions": "Impressões",
|
||||||
"impressions_identified_only": "A mostrar apenas impressões de contactos identificados",
|
|
||||||
"impressions_tooltip": "Número de vezes que o inquérito foi visualizado.",
|
"impressions_tooltip": "Número de vezes que o inquérito foi visualizado.",
|
||||||
"in_app": {
|
"in_app": {
|
||||||
"connection_description": "O questionário será exibido aos utilizadores do seu website que correspondam aos critérios listados abaixo",
|
"connection_description": "O questionário será exibido aos utilizadores do seu website que correspondam aos critérios listados abaixo",
|
||||||
@@ -2000,7 +1916,6 @@
|
|||||||
"last_quarter": "Último trimestre",
|
"last_quarter": "Último trimestre",
|
||||||
"last_year": "Ano passado",
|
"last_year": "Ano passado",
|
||||||
"limit": "Limite",
|
"limit": "Limite",
|
||||||
"no_identified_impressions": "Sem impressões de contactos identificados",
|
|
||||||
"no_responses_found": "Nenhuma resposta encontrada",
|
"no_responses_found": "Nenhuma resposta encontrada",
|
||||||
"other_values_found": "Outros valores encontrados",
|
"other_values_found": "Outros valores encontrados",
|
||||||
"overall": "Geral",
|
"overall": "Geral",
|
||||||
@@ -2140,71 +2055,9 @@
|
|||||||
"look": {
|
"look": {
|
||||||
"add_background_color": "Adicionar cor de fundo",
|
"add_background_color": "Adicionar cor de fundo",
|
||||||
"add_background_color_description": "Adicione uma cor de fundo ao contentor do logótipo.",
|
"add_background_color_description": "Adicione uma cor de fundo ao contentor do logótipo.",
|
||||||
"advanced_styling_field_border_radius": "Raio da borda",
|
|
||||||
"advanced_styling_field_button_bg": "Fundo do botão",
|
|
||||||
"advanced_styling_field_button_bg_description": "Preenche o botão Seguinte / Submeter.",
|
|
||||||
"advanced_styling_field_button_border_radius_description": "Arredonda os cantos do botão.",
|
|
||||||
"advanced_styling_field_button_font_size_description": "Ajusta o tamanho do texto da etiqueta do botão.",
|
|
||||||
"advanced_styling_field_button_font_weight_description": "Torna o texto do botão mais leve ou mais negrito.",
|
|
||||||
"advanced_styling_field_button_height_description": "Controla a altura do botão.",
|
|
||||||
"advanced_styling_field_button_padding_x_description": "Adiciona espaço à esquerda e à direita.",
|
|
||||||
"advanced_styling_field_button_padding_y_description": "Adiciona espaço no topo e na base.",
|
|
||||||
"advanced_styling_field_button_text": "Texto do botão",
|
|
||||||
"advanced_styling_field_button_text_description": "Colore a etiqueta dentro dos botões.",
|
|
||||||
"advanced_styling_field_description_color": "Cor da descrição",
|
|
||||||
"advanced_styling_field_description_color_description": "Colore o texto abaixo de cada título.",
|
|
||||||
"advanced_styling_field_description_size": "Tamanho da fonte da descrição",
|
|
||||||
"advanced_styling_field_description_size_description": "Ajusta o tamanho do texto da descrição.",
|
|
||||||
"advanced_styling_field_description_weight": "Peso da fonte da descrição",
|
|
||||||
"advanced_styling_field_description_weight_description": "Torna o texto da descrição mais leve ou mais negrito.",
|
|
||||||
"advanced_styling_field_font_size": "Tamanho da fonte",
|
|
||||||
"advanced_styling_field_font_weight": "Peso da fonte",
|
|
||||||
"advanced_styling_field_headline_color": "Cor do título",
|
|
||||||
"advanced_styling_field_headline_color_description": "Colore o texto principal da pergunta.",
|
|
||||||
"advanced_styling_field_headline_size": "Tamanho da fonte do título",
|
|
||||||
"advanced_styling_field_headline_size_description": "Ajusta o tamanho do texto do título.",
|
|
||||||
"advanced_styling_field_headline_weight": "Peso da fonte do título",
|
|
||||||
"advanced_styling_field_headline_weight_description": "Torna o texto do título mais leve ou mais negrito.",
|
|
||||||
"advanced_styling_field_height": "Altura mínima",
|
|
||||||
"advanced_styling_field_indicator_bg": "Fundo do indicador",
|
|
||||||
"advanced_styling_field_indicator_bg_description": "Colore a porção preenchida da barra.",
|
|
||||||
"advanced_styling_field_input_border_radius_description": "Arredonda os cantos do campo.",
|
|
||||||
"advanced_styling_field_input_font_size_description": "Ajusta o tamanho do texto digitado nos campos.",
|
|
||||||
"advanced_styling_field_input_height_description": "Controla a altura mínima do campo de entrada.",
|
|
||||||
"advanced_styling_field_input_padding_x_description": "Adiciona espaço à esquerda e à direita.",
|
|
||||||
"advanced_styling_field_input_padding_y_description": "Adiciona espaço no topo e na base.",
|
|
||||||
"advanced_styling_field_input_placeholder_opacity_description": "Atenua o texto de sugestão do placeholder.",
|
|
||||||
"advanced_styling_field_input_shadow_description": "Adiciona uma sombra ao redor dos campos de entrada.",
|
|
||||||
"advanced_styling_field_input_text": "Texto de entrada",
|
|
||||||
"advanced_styling_field_input_text_description": "Colore o texto digitado nos campos de entrada.",
|
|
||||||
"advanced_styling_field_option_bg": "Fundo",
|
|
||||||
"advanced_styling_field_option_bg_description": "Preenche os itens de opção.",
|
|
||||||
"advanced_styling_field_option_border_radius_description": "Arredonda os cantos das opções.",
|
|
||||||
"advanced_styling_field_option_font_size_description": "Ajusta o tamanho do texto da etiqueta da opção.",
|
|
||||||
"advanced_styling_field_option_label": "Cor da etiqueta",
|
|
||||||
"advanced_styling_field_option_label_description": "Colore o texto da etiqueta da opção.",
|
|
||||||
"advanced_styling_field_option_padding_x_description": "Adiciona espaço à esquerda e à direita.",
|
|
||||||
"advanced_styling_field_option_padding_y_description": "Adiciona espaço no topo e na base.",
|
|
||||||
"advanced_styling_field_padding_x": "Espaçamento X",
|
|
||||||
"advanced_styling_field_padding_y": "Espaçamento Y",
|
|
||||||
"advanced_styling_field_placeholder_opacity": "Opacidade do marcador de posição",
|
|
||||||
"advanced_styling_field_shadow": "Sombra",
|
|
||||||
"advanced_styling_field_track_bg": "Fundo da faixa",
|
|
||||||
"advanced_styling_field_track_bg_description": "Colore a porção não preenchida da barra.",
|
|
||||||
"advanced_styling_field_track_height": "Altura da faixa",
|
|
||||||
"advanced_styling_field_track_height_description": "Controla a espessura da barra de progresso.",
|
|
||||||
"advanced_styling_field_upper_label_color": "Cor da etiqueta do título",
|
|
||||||
"advanced_styling_field_upper_label_color_description": "Colore a pequena etiqueta acima dos campos de entrada.",
|
|
||||||
"advanced_styling_field_upper_label_size": "Tamanho da fonte da etiqueta do título",
|
|
||||||
"advanced_styling_field_upper_label_size_description": "Ajusta o tamanho da pequena etiqueta acima dos campos de entrada.",
|
|
||||||
"advanced_styling_field_upper_label_weight": "Peso da fonte da etiqueta do título",
|
|
||||||
"advanced_styling_field_upper_label_weight_description": "Torna a etiqueta mais leve ou mais negrito.",
|
|
||||||
"advanced_styling_section_buttons": "Botões",
|
|
||||||
"advanced_styling_section_headlines": "Títulos e descrições",
|
|
||||||
"advanced_styling_section_inputs": "Campos de entrada",
|
|
||||||
"advanced_styling_section_options": "Opções (rádio/caixa de seleção)",
|
|
||||||
"app_survey_placement": "Colocação do inquérito (app)",
|
"app_survey_placement": "Colocação do inquérito (app)",
|
||||||
"app_survey_placement_settings_description": "Altere onde os inquéritos serão apresentados na sua aplicação web ou website.",
|
"app_survey_placement_settings_description": "Altere onde os inquéritos serão apresentados na sua aplicação web ou website.",
|
||||||
|
"centered_modal_overlay_color": "Cor da sobreposição modal centralizada",
|
||||||
"email_customization": "Personalização de e-mail",
|
"email_customization": "Personalização de e-mail",
|
||||||
"email_customization_description": "Altere a aparência dos e-mails que a Formbricks envia em seu nome.",
|
"email_customization_description": "Altere a aparência dos e-mails que a Formbricks envia em seu nome.",
|
||||||
"enable_custom_styling": "Ativar estilização personalizada",
|
"enable_custom_styling": "Ativar estilização personalizada",
|
||||||
@@ -2215,9 +2068,6 @@
|
|||||||
"formbricks_branding_hidden": "A marca Formbricks está oculta.",
|
"formbricks_branding_hidden": "A marca Formbricks está oculta.",
|
||||||
"formbricks_branding_settings_description": "Adoramos o seu apoio, mas compreendemos se preferir desativar.",
|
"formbricks_branding_settings_description": "Adoramos o seu apoio, mas compreendemos se preferir desativar.",
|
||||||
"formbricks_branding_shown": "A marca Formbricks está visível.",
|
"formbricks_branding_shown": "A marca Formbricks está visível.",
|
||||||
"generate_theme_btn": "Gerar",
|
|
||||||
"generate_theme_confirmation": "Gostarias de gerar um tema de cores correspondente com base na cor da tua marca? Isto irá substituir as tuas definições de cor atuais.",
|
|
||||||
"generate_theme_header": "Gerar tema de cores?",
|
|
||||||
"logo_removed_successfully": "Logótipo removido com sucesso",
|
"logo_removed_successfully": "Logótipo removido com sucesso",
|
||||||
"logo_settings_description": "Carregue o logótipo da sua empresa para personalizar inquéritos e pré-visualizações de links.",
|
"logo_settings_description": "Carregue o logótipo da sua empresa para personalizar inquéritos e pré-visualizações de links.",
|
||||||
"logo_updated_successfully": "Logótipo atualizado com sucesso",
|
"logo_updated_successfully": "Logótipo atualizado com sucesso",
|
||||||
@@ -2232,8 +2082,6 @@
|
|||||||
"show_formbricks_branding_in": "Mostrar marca Formbricks em inquéritos {type}",
|
"show_formbricks_branding_in": "Mostrar marca Formbricks em inquéritos {type}",
|
||||||
"show_powered_by_formbricks": "Mostrar assinatura 'Powered by Formbricks'",
|
"show_powered_by_formbricks": "Mostrar assinatura 'Powered by Formbricks'",
|
||||||
"styling_updated_successfully": "Estilo atualizado com sucesso",
|
"styling_updated_successfully": "Estilo atualizado com sucesso",
|
||||||
"suggest_colors": "Sugerir cores",
|
|
||||||
"suggested_colors_applied_please_save": "Cores sugeridas geradas com sucesso. Pressiona \"Guardar\" para manter as alterações.",
|
|
||||||
"theme": "Tema",
|
"theme": "Tema",
|
||||||
"theme_settings_description": "Crie um tema de estilo para todos os inquéritos. Pode ativar estilos personalizados para cada inquérito."
|
"theme_settings_description": "Crie um tema de estilo para todos os inquéritos. Pode ativar estilos personalizados para cada inquérito."
|
||||||
},
|
},
|
||||||
@@ -2997,7 +2845,6 @@
|
|||||||
"preview_survey_question_2_choice_1_label": "Sim, mantenha-me informado.",
|
"preview_survey_question_2_choice_1_label": "Sim, mantenha-me informado.",
|
||||||
"preview_survey_question_2_choice_2_label": "Não, obrigado!",
|
"preview_survey_question_2_choice_2_label": "Não, obrigado!",
|
||||||
"preview_survey_question_2_headline": "Quer manter-se atualizado?",
|
"preview_survey_question_2_headline": "Quer manter-se atualizado?",
|
||||||
"preview_survey_question_2_subheader": "Este é um exemplo de descrição.",
|
|
||||||
"preview_survey_welcome_card_headline": "Bem-vindo!",
|
"preview_survey_welcome_card_headline": "Bem-vindo!",
|
||||||
"prioritize_features_description": "Identifique as funcionalidades que os seus utilizadores precisam mais e menos.",
|
"prioritize_features_description": "Identifique as funcionalidades que os seus utilizadores precisam mais e menos.",
|
||||||
"prioritize_features_name": "Priorizar Funcionalidades",
|
"prioritize_features_name": "Priorizar Funcionalidades",
|
||||||
|
|||||||
+24
-177
@@ -188,7 +188,6 @@
|
|||||||
"customer_success": "Succesul Clientului",
|
"customer_success": "Succesul Clientului",
|
||||||
"dark_overlay": "Suprapunere întunecată",
|
"dark_overlay": "Suprapunere întunecată",
|
||||||
"date": "Dată",
|
"date": "Dată",
|
||||||
"days": "zile",
|
|
||||||
"default": "Implicit",
|
"default": "Implicit",
|
||||||
"delete": "Șterge",
|
"delete": "Șterge",
|
||||||
"description": "Descriere",
|
"description": "Descriere",
|
||||||
@@ -218,7 +217,6 @@
|
|||||||
"error": "Eroare",
|
"error": "Eroare",
|
||||||
"error_component_description": "Această resursă nu există sau nu aveți drepturile necesare pentru a o accesa.",
|
"error_component_description": "Această resursă nu există sau nu aveți drepturile necesare pentru a o accesa.",
|
||||||
"error_component_title": "Eroare la încărcarea resurselor",
|
"error_component_title": "Eroare la încărcarea resurselor",
|
||||||
"error_loading_data": "Eroare la încărcarea datelor",
|
|
||||||
"error_rate_limit_description": "Numărul maxim de cereri atins. Vă rugăm să încercați din nou mai târziu.",
|
"error_rate_limit_description": "Numărul maxim de cereri atins. Vă rugăm să încercați din nou mai târziu.",
|
||||||
"error_rate_limit_title": "Limită de cereri depășită",
|
"error_rate_limit_title": "Limită de cereri depășită",
|
||||||
"expand_rows": "Extinde rândurile",
|
"expand_rows": "Extinde rândurile",
|
||||||
@@ -277,7 +275,6 @@
|
|||||||
"mobile_overlay_app_works_best_on_desktop": "Formbricks funcționează cel mai bine pe un ecran mai mare. Pentru a gestiona sau crea chestionare, treceți la un alt dispozitiv.",
|
"mobile_overlay_app_works_best_on_desktop": "Formbricks funcționează cel mai bine pe un ecran mai mare. Pentru a gestiona sau crea chestionare, treceți la un alt dispozitiv.",
|
||||||
"mobile_overlay_surveys_look_good": "Nu vă faceți griji – chestionarele dumneavoastră arată grozav pe orice dispozitiv și dimensiune a ecranului!",
|
"mobile_overlay_surveys_look_good": "Nu vă faceți griji – chestionarele dumneavoastră arată grozav pe orice dispozitiv și dimensiune a ecranului!",
|
||||||
"mobile_overlay_title": "Ups, ecran mic detectat!",
|
"mobile_overlay_title": "Ups, ecran mic detectat!",
|
||||||
"months": "luni",
|
|
||||||
"move_down": "Mută în jos",
|
"move_down": "Mută în jos",
|
||||||
"move_up": "Mută sus",
|
"move_up": "Mută sus",
|
||||||
"multiple_languages": "Mai multe limbi",
|
"multiple_languages": "Mai multe limbi",
|
||||||
@@ -288,7 +285,6 @@
|
|||||||
"no_background_image_found": "Nu a fost găsită nicio imagine de fundal.",
|
"no_background_image_found": "Nu a fost găsită nicio imagine de fundal.",
|
||||||
"no_code": "Fără Cod",
|
"no_code": "Fără Cod",
|
||||||
"no_files_uploaded": "Nu au fost încărcate fișiere",
|
"no_files_uploaded": "Nu au fost încărcate fișiere",
|
||||||
"no_overlay": "Fără overlay",
|
|
||||||
"no_quotas_found": "Nicio cotă găsită",
|
"no_quotas_found": "Nicio cotă găsită",
|
||||||
"no_result_found": "Niciun rezultat găsit",
|
"no_result_found": "Niciun rezultat găsit",
|
||||||
"no_results": "Nicio rezultat",
|
"no_results": "Nicio rezultat",
|
||||||
@@ -315,7 +311,6 @@
|
|||||||
"organization_teams_not_found": "Echipele organizației nu au fost găsite",
|
"organization_teams_not_found": "Echipele organizației nu au fost găsite",
|
||||||
"other": "Altele",
|
"other": "Altele",
|
||||||
"others": "Altele",
|
"others": "Altele",
|
||||||
"overlay_color": "Culoare overlay",
|
|
||||||
"overview": "Prezentare generală",
|
"overview": "Prezentare generală",
|
||||||
"password": "Parolă",
|
"password": "Parolă",
|
||||||
"paused": "Pauză",
|
"paused": "Pauză",
|
||||||
@@ -355,7 +350,6 @@
|
|||||||
"request_trial_license": "Solicitați o licență de încercare",
|
"request_trial_license": "Solicitați o licență de încercare",
|
||||||
"reset_to_default": "Revino la implicit",
|
"reset_to_default": "Revino la implicit",
|
||||||
"response": "Răspuns",
|
"response": "Răspuns",
|
||||||
"response_id": "ID răspuns",
|
|
||||||
"responses": "Răspunsuri",
|
"responses": "Răspunsuri",
|
||||||
"restart": "Repornește",
|
"restart": "Repornește",
|
||||||
"role": "Rolul",
|
"role": "Rolul",
|
||||||
@@ -396,7 +390,6 @@
|
|||||||
"status": "Stare",
|
"status": "Stare",
|
||||||
"step_by_step_manual": "Manual pas cu pas",
|
"step_by_step_manual": "Manual pas cu pas",
|
||||||
"storage_not_configured": "Stocarea fișierelor neconfigurată, upload-urile vor eșua probabil",
|
"storage_not_configured": "Stocarea fișierelor neconfigurată, upload-urile vor eșua probabil",
|
||||||
"string": "Text",
|
|
||||||
"styling": "Stilizare",
|
"styling": "Stilizare",
|
||||||
"submit": "Trimite",
|
"submit": "Trimite",
|
||||||
"summary": "Sumar",
|
"summary": "Sumar",
|
||||||
@@ -429,7 +422,6 @@
|
|||||||
"top_right": "Dreapta Sus",
|
"top_right": "Dreapta Sus",
|
||||||
"try_again": "Încearcă din nou",
|
"try_again": "Încearcă din nou",
|
||||||
"type": "Tip",
|
"type": "Tip",
|
||||||
"unknown_survey": "Chestionar necunoscut",
|
|
||||||
"unlock_more_workspaces_with_a_higher_plan": "Deblochează mai multe workspaces cu un plan superior.",
|
"unlock_more_workspaces_with_a_higher_plan": "Deblochează mai multe workspaces cu un plan superior.",
|
||||||
"update": "Actualizare",
|
"update": "Actualizare",
|
||||||
"updated": "Actualizat",
|
"updated": "Actualizat",
|
||||||
@@ -453,7 +445,6 @@
|
|||||||
"website_and_app_connection": "Conectare site web și aplicație",
|
"website_and_app_connection": "Conectare site web și aplicație",
|
||||||
"website_app_survey": "Chestionar pentru site și aplicație",
|
"website_app_survey": "Chestionar pentru site și aplicație",
|
||||||
"website_survey": "Chestionar despre site",
|
"website_survey": "Chestionar despre site",
|
||||||
"weeks": "săptămâni",
|
|
||||||
"welcome_card": "Card de bun venit",
|
"welcome_card": "Card de bun venit",
|
||||||
"workspace_configuration": "Configurare workspace",
|
"workspace_configuration": "Configurare workspace",
|
||||||
"workspace_created_successfully": "Spațiul de lucru a fost creat cu succes",
|
"workspace_created_successfully": "Spațiul de lucru a fost creat cu succes",
|
||||||
@@ -464,7 +455,6 @@
|
|||||||
"workspace_not_found": "Workspace-ul nu a fost găsit",
|
"workspace_not_found": "Workspace-ul nu a fost găsit",
|
||||||
"workspace_permission_not_found": "Permisiunea pentru workspace nu a fost găsită",
|
"workspace_permission_not_found": "Permisiunea pentru workspace nu a fost găsită",
|
||||||
"workspaces": "Workspaces",
|
"workspaces": "Workspaces",
|
||||||
"years": "ani",
|
|
||||||
"you": "Tu",
|
"you": "Tu",
|
||||||
"you_are_downgraded_to_the_community_edition": "Ai fost retrogradat la ediția Community.",
|
"you_are_downgraded_to_the_community_edition": "Ai fost retrogradat la ediția Community.",
|
||||||
"you_are_not_authorized_to_perform_this_action": "Nu sunteți autorizat să efectuați această acțiune.",
|
"you_are_not_authorized_to_perform_this_action": "Nu sunteți autorizat să efectuați această acțiune.",
|
||||||
@@ -625,7 +615,7 @@
|
|||||||
"attribute_created_successfully": "Atribut creat cu succes",
|
"attribute_created_successfully": "Atribut creat cu succes",
|
||||||
"attribute_description": "Descriere",
|
"attribute_description": "Descriere",
|
||||||
"attribute_description_placeholder": "Descriere scurtă",
|
"attribute_description_placeholder": "Descriere scurtă",
|
||||||
"attribute_key": "Cheie",
|
"attribute_key": "Atribut",
|
||||||
"attribute_key_cannot_be_changed": "Cheia nu poate fi modificată după creare",
|
"attribute_key_cannot_be_changed": "Cheia nu poate fi modificată după creare",
|
||||||
"attribute_key_hint": "Doar litere mici, cifre și caractere de subliniere. Trebuie să înceapă cu o literă.",
|
"attribute_key_hint": "Doar litere mici, cifre și caractere de subliniere. Trebuie să înceapă cu o literă.",
|
||||||
"attribute_key_placeholder": "ex: date_of_birth",
|
"attribute_key_placeholder": "ex: date_of_birth",
|
||||||
@@ -636,45 +626,28 @@
|
|||||||
"attribute_updated_successfully": "Atribut actualizat cu succes",
|
"attribute_updated_successfully": "Atribut actualizat cu succes",
|
||||||
"attribute_value": "Valoare",
|
"attribute_value": "Valoare",
|
||||||
"attribute_value_placeholder": "Valoare atribut",
|
"attribute_value_placeholder": "Valoare atribut",
|
||||||
"attributes_msg_attribute_limit_exceeded": "Nu s-au putut crea {count, plural, one {1 atribut nou} few {# atribute noi} other {# de atribute noi}} deoarece s-ar depăși limita maximă de {limit, plural, one {1 clasă de atribute} few {# clase de atribute} other {# de clase de atribute}}. Atributele existente au fost actualizate cu succes.",
|
|
||||||
"attributes_msg_attribute_type_validation_error": "{error} (atributul „{key}” are dataType: {dataType})",
|
|
||||||
"attributes_msg_email_already_exists": "Emailul există deja pentru acest mediu și nu a fost actualizat.",
|
|
||||||
"attributes_msg_email_or_userid_required": "Este necesar fie un email, fie un userId. Valorile existente au fost păstrate.",
|
|
||||||
"attributes_msg_new_attribute_created": "A fost creat atributul nou „{key}” cu tipul „{dataType}”",
|
|
||||||
"attributes_msg_userid_already_exists": "UserId-ul există deja pentru acest mediu și nu a fost actualizat.",
|
|
||||||
"contact_deleted_successfully": "Contact șters cu succes",
|
"contact_deleted_successfully": "Contact șters cu succes",
|
||||||
"contact_not_found": "Nu a fost găsit niciun contact",
|
"contact_not_found": "Nu a fost găsit niciun contact",
|
||||||
"contacts_table_refresh": "Reîmprospătare contacte",
|
"contacts_table_refresh": "Reîmprospătare contacte",
|
||||||
"contacts_table_refresh_success": "Contactele au fost actualizate cu succes",
|
"contacts_table_refresh_success": "Contactele au fost actualizate cu succes",
|
||||||
"create_attribute": "Creează atribut",
|
"create_attribute": "Creează atribut",
|
||||||
|
"create_key": "Creează cheie",
|
||||||
"create_new_attribute": "Creează atribut nou",
|
"create_new_attribute": "Creează atribut nou",
|
||||||
"create_new_attribute_description": "Creează un atribut nou pentru segmentare.",
|
"create_new_attribute_description": "Creează un atribut nou pentru segmentare.",
|
||||||
"custom_attributes": "Atribute personalizate",
|
|
||||||
"data_type": "Tip de date",
|
|
||||||
"data_type_cannot_be_changed": "Tipul de date nu poate fi schimbat după creare",
|
|
||||||
"data_type_description": "Alege cum să fie stocat și filtrat acest atribut",
|
|
||||||
"date_value_required": "Valoarea pentru dată este obligatorie. Folosește butonul de ștergere dacă nu vrei să setezi o dată.",
|
|
||||||
"delete_attribute_confirmation": "{value, plural, one {Acest lucru va șterge atributul selectat. Orice date de contact asociate cu acest atribut vor fi pierdute.} few {Acest lucru va șterge atributele selectate. Orice date de contact asociate cu aceste atribute vor fi pierdute.} other {Acest lucru va șterge atributele selectate. Orice date de contact asociate cu aceste atribute vor fi pierdute.}}",
|
"delete_attribute_confirmation": "{value, plural, one {Acest lucru va șterge atributul selectat. Orice date de contact asociate cu acest atribut vor fi pierdute.} few {Acest lucru va șterge atributele selectate. Orice date de contact asociate cu aceste atribute vor fi pierdute.} other {Acest lucru va șterge atributele selectate. Orice date de contact asociate cu aceste atribute vor fi pierdute.}}",
|
||||||
"delete_contact_confirmation": "Acest lucru va șterge toate răspunsurile la sondaj și atributele de contact asociate cu acest contact. Orice țintire și personalizare bazată pe datele acestui contact vor fi pierdute.",
|
"delete_contact_confirmation": "Acest lucru va șterge toate răspunsurile la sondaj și atributele de contact asociate cu acest contact. Orice țintire și personalizare bazată pe datele acestui contact vor fi pierdute.",
|
||||||
"delete_contact_confirmation_with_quotas": "{value, plural, one {Această acțiune va șterge toate răspunsurile chestionarului și atributele de contact asociate cu acest contact. Orice țintire și personalizare bazată pe datele acestui contact vor fi pierdute. Dacă acest contact are răspunsuri care contează pentru cotele chestionarului, numărul cotelor va fi redus, dar limitele cotelor vor rămâne neschimbate.} other {Aceste acțiuni vor șterge toate răspunsurile chestionarului și atributele de contact asociate cu acești contacți. Orice țintire și personalizare bazată pe datele acestor contacți vor fi pierdute. Dacă acești contacți au răspunsuri care contează pentru cotele chestionarului, numărul cotelor va fi redus, dar limitele cotelor vor rămâne neschimbate.} }",
|
"delete_contact_confirmation_with_quotas": "{value, plural, one {Această acțiune va șterge toate răspunsurile chestionarului și atributele de contact asociate cu acest contact. Orice țintire și personalizare bazată pe datele acestui contact vor fi pierdute. Dacă acest contact are răspunsuri care contează pentru cotele chestionarului, numărul cotelor va fi redus, dar limitele cotelor vor rămâne neschimbate.} other {Aceste acțiuni vor șterge toate răspunsurile chestionarului și atributele de contact asociate cu acești contacți. Orice țintire și personalizare bazată pe datele acestor contacți vor fi pierdute. Dacă acești contacți au răspunsuri care contează pentru cotele chestionarului, numărul cotelor va fi redus, dar limitele cotelor vor rămâne neschimbate.} }",
|
||||||
"displays": "Afișări",
|
|
||||||
"edit_attribute": "Editează atributul",
|
"edit_attribute": "Editează atributul",
|
||||||
"edit_attribute_description": "Actualizează eticheta și descrierea acestui atribut.",
|
"edit_attribute_description": "Actualizează eticheta și descrierea acestui atribut.",
|
||||||
"edit_attribute_values": "Editează atributele",
|
"edit_attribute_values": "Editează atributele",
|
||||||
"edit_attribute_values_description": "Modifică valorile anumitor atribute pentru acest contact.",
|
"edit_attribute_values_description": "Modifică valorile anumitor atribute pentru acest contact.",
|
||||||
"edit_attributes": "Editează atributele",
|
|
||||||
"edit_attributes_success": "Atributele contactului au fost actualizate cu succes",
|
"edit_attributes_success": "Atributele contactului au fost actualizate cu succes",
|
||||||
"generate_personal_link": "Generează link personal",
|
"generate_personal_link": "Generează link personal",
|
||||||
"generate_personal_link_description": "Selectați un sondaj publicat pentru a genera un link personalizat pentru acest contact.",
|
"generate_personal_link_description": "Selectați un sondaj publicat pentru a genera un link personalizat pentru acest contact.",
|
||||||
"invalid_csv_column_names": "Nume de coloană CSV nevalide: {columns}. Numele coloanelor care vor deveni atribute noi trebuie să conțină doar litere mici, cifre și caractere de subliniere și trebuie să înceapă cu o literă.",
|
|
||||||
"invalid_date_format": "Format de dată invalid. Te rugăm să folosești o dată validă.",
|
|
||||||
"invalid_number_format": "Format de număr invalid. Te rugăm să introduci un număr valid.",
|
|
||||||
"no_activity_yet": "Nicio activitate încă",
|
|
||||||
"no_published_link_surveys_available": "Nu există sondaje publicate pentru linkuri disponibile. Vă rugăm să publicați mai întâi un sondaj pentru linkuri.",
|
"no_published_link_surveys_available": "Nu există sondaje publicate pentru linkuri disponibile. Vă rugăm să publicați mai întâi un sondaj pentru linkuri.",
|
||||||
"no_published_surveys": "Nu există sondaje publicate",
|
"no_published_surveys": "Nu există sondaje publicate",
|
||||||
"no_responses_found": "Nu s-au găsit răspunsuri",
|
"no_responses_found": "Nu s-au găsit răspunsuri",
|
||||||
"not_provided": "Nu a fost furnizat",
|
"not_provided": "Nu a fost furnizat",
|
||||||
"number_value_required": "Valoarea numerică este obligatorie. Folosește butonul de ștergere pentru a elimina acest atribut.",
|
|
||||||
"personal_link_generated": "Linkul personal a fost generat cu succes",
|
"personal_link_generated": "Linkul personal a fost generat cu succes",
|
||||||
"personal_link_generated_but_clipboard_failed": "Linkul personal a fost generat, dar nu s-a reușit copierea în clipboard: {url}",
|
"personal_link_generated_but_clipboard_failed": "Linkul personal a fost generat, dar nu s-a reușit copierea în clipboard: {url}",
|
||||||
"personal_survey_link": "Link către sondajul personal",
|
"personal_survey_link": "Link către sondajul personal",
|
||||||
@@ -683,24 +656,13 @@
|
|||||||
"search_contact": "Căutați contact",
|
"search_contact": "Căutați contact",
|
||||||
"select_a_survey": "Selectați un sondaj",
|
"select_a_survey": "Selectați un sondaj",
|
||||||
"select_attribute": "Selectează atributul",
|
"select_attribute": "Selectează atributul",
|
||||||
"select_attribute_key": "Selectează cheia atributului",
|
|
||||||
"survey_viewed": "Chestionar vizualizat",
|
|
||||||
"survey_viewed_at": "Vizualizat la",
|
|
||||||
"system_attributes": "Atribute de sistem",
|
|
||||||
"unlock_contacts_description": "Gestionează contactele și trimite sondaje țintite",
|
"unlock_contacts_description": "Gestionează contactele și trimite sondaje țintite",
|
||||||
"unlock_contacts_title": "Deblocați contactele cu un plan superior.",
|
"unlock_contacts_title": "Deblocați contactele cu un plan superior.",
|
||||||
"upload_contacts_error_attribute_type_mismatch": "Atributul „{key}” este de tipul „{dataType}”, dar CSV-ul conține valori invalide: {values}",
|
|
||||||
"upload_contacts_error_duplicate_mappings": "Au fost găsite mapări duplicate pentru următoarele atribute: {attributes}",
|
|
||||||
"upload_contacts_error_file_too_large": "Dimensiunea fișierului depășește limita maximă de 800KB",
|
|
||||||
"upload_contacts_error_generic": "A apărut o eroare la încărcarea contactelor. Te rugăm să încerci din nou mai târziu.",
|
|
||||||
"upload_contacts_error_invalid_file_type": "Te rugăm să încarci un fișier CSV",
|
|
||||||
"upload_contacts_error_no_valid_contacts": "Fișierul CSV încărcat nu conține contacte valide. Consultă fișierul CSV de exemplu pentru formatul corect.",
|
|
||||||
"upload_contacts_modal_attribute_header": "Atribut Formbricks",
|
|
||||||
"upload_contacts_modal_attributes_description": "Mapează coloanele din CSV-ul tău la atributele din Formbricks.",
|
"upload_contacts_modal_attributes_description": "Mapează coloanele din CSV-ul tău la atributele din Formbricks.",
|
||||||
"upload_contacts_modal_attributes_new": "Atribut nou",
|
"upload_contacts_modal_attributes_new": "Atribut nou",
|
||||||
"upload_contacts_modal_attributes_search_or_add": "Căutați sau adăugați atribut",
|
"upload_contacts_modal_attributes_search_or_add": "Căutați sau adăugați atribut",
|
||||||
|
"upload_contacts_modal_attributes_should_be_mapped_to": "ar trebui să fie mapat către",
|
||||||
"upload_contacts_modal_attributes_title": "Atribute",
|
"upload_contacts_modal_attributes_title": "Atribute",
|
||||||
"upload_contacts_modal_csv_column_header": "Coloană CSV",
|
|
||||||
"upload_contacts_modal_description": "Încărcați un fișier CSV pentru a importa rapid contactele cu atribute.",
|
"upload_contacts_modal_description": "Încărcați un fișier CSV pentru a importa rapid contactele cu atribute.",
|
||||||
"upload_contacts_modal_download_example_csv": "Descărcați exemplul CSV",
|
"upload_contacts_modal_download_example_csv": "Descărcați exemplul CSV",
|
||||||
"upload_contacts_modal_duplicates_description": "Cum ar trebui să procedăm dacă un contact există deja în agenda dumneavoastră?",
|
"upload_contacts_modal_duplicates_description": "Cum ar trebui să procedăm dacă un contact există deja în agenda dumneavoastră?",
|
||||||
@@ -757,12 +719,7 @@
|
|||||||
"link_google_sheet": "Leagă Google Sheet",
|
"link_google_sheet": "Leagă Google Sheet",
|
||||||
"link_new_sheet": "Leagă un nou Sheet",
|
"link_new_sheet": "Leagă un nou Sheet",
|
||||||
"no_integrations_yet": "Integrațiile tale Google Sheet vor apărea aici de îndată ce le vei adăuga. ⏲️",
|
"no_integrations_yet": "Integrațiile tale Google Sheet vor apărea aici de îndată ce le vei adăuga. ⏲️",
|
||||||
"reconnect_button": "Reconectează",
|
"spreadsheet_url": "URL foaie de calcul"
|
||||||
"reconnect_button_description": "Conexiunea ta cu Google Sheets a expirat. Te rugăm să te reconectezi pentru a continua sincronizarea răspunsurilor. Linkurile și datele existente din foile de calcul vor fi păstrate.",
|
|
||||||
"reconnect_button_tooltip": "Reconectează integrarea pentru a-ți reîmprospăta accesul. Linkurile și datele existente din foile de calcul vor fi păstrate.",
|
|
||||||
"spreadsheet_permission_error": "Nu ai permisiunea de a accesa această foaie de calcul. Asigură-te că foaia de calcul este partajată cu contul tău Google și că ai acces de scriere la aceasta.",
|
|
||||||
"spreadsheet_url": "URL foaie de calcul",
|
|
||||||
"token_expired_error": "Tokenul de reîmprospătare Google Sheets a expirat sau a fost revocat. Te rugăm să reconectezi integrarea."
|
|
||||||
},
|
},
|
||||||
"include_created_at": "Include data creării",
|
"include_created_at": "Include data creării",
|
||||||
"include_hidden_fields": "Include câmpuri ascunse",
|
"include_hidden_fields": "Include câmpuri ascunse",
|
||||||
@@ -886,40 +843,6 @@
|
|||||||
"no_attributes_yet": "Niciun atribut încă!",
|
"no_attributes_yet": "Niciun atribut încă!",
|
||||||
"no_filters_yet": "Nu există filtre încă!",
|
"no_filters_yet": "Nu există filtre încă!",
|
||||||
"no_segments_yet": "În prezent nu aveți segmente salvate.",
|
"no_segments_yet": "În prezent nu aveți segmente salvate.",
|
||||||
"operator_contains": "conține",
|
|
||||||
"operator_does_not_contain": "nu conține",
|
|
||||||
"operator_ends_with": "se termină cu",
|
|
||||||
"operator_is_after": "este după",
|
|
||||||
"operator_is_before": "este înainte",
|
|
||||||
"operator_is_between": "este între",
|
|
||||||
"operator_is_newer_than": "este mai nou decât",
|
|
||||||
"operator_is_not_set": "nu este setat",
|
|
||||||
"operator_is_older_than": "este mai vechi decât",
|
|
||||||
"operator_is_same_day": "este în aceeași zi",
|
|
||||||
"operator_is_set": "este setat",
|
|
||||||
"operator_starts_with": "începe cu",
|
|
||||||
"operator_title_contains": "Conține",
|
|
||||||
"operator_title_does_not_contain": "Nu conține",
|
|
||||||
"operator_title_ends_with": "Se termină cu",
|
|
||||||
"operator_title_equals": "Egal",
|
|
||||||
"operator_title_greater_equal": "Mai mare sau egal cu",
|
|
||||||
"operator_title_greater_than": "Mai mare decât",
|
|
||||||
"operator_title_is_after": "Este după",
|
|
||||||
"operator_title_is_before": "Este înainte",
|
|
||||||
"operator_title_is_between": "Este între",
|
|
||||||
"operator_title_is_newer_than": "Este mai nou decât",
|
|
||||||
"operator_title_is_not_set": "Nu este setat",
|
|
||||||
"operator_title_is_older_than": "Este mai vechi decât",
|
|
||||||
"operator_title_is_same_day": "Este în aceeași zi",
|
|
||||||
"operator_title_is_set": "Este setat",
|
|
||||||
"operator_title_less_equal": "Mai mic sau egal cu",
|
|
||||||
"operator_title_less_than": "Mai mic decât",
|
|
||||||
"operator_title_not_equals": "Nu este egal cu",
|
|
||||||
"operator_title_starts_with": "Începe cu",
|
|
||||||
"operator_title_user_is_in": "Utilizatorul este în",
|
|
||||||
"operator_title_user_is_not_in": "Utilizatorul nu este în",
|
|
||||||
"operator_user_is_in": "Utilizatorul este în",
|
|
||||||
"operator_user_is_not_in": "Utilizatorul nu este în",
|
|
||||||
"person_and_attributes": "Persoană & Atribute",
|
"person_and_attributes": "Persoană & Atribute",
|
||||||
"phone": "Telefon",
|
"phone": "Telefon",
|
||||||
"please_remove_the_segment_from_these_surveys_in_order_to_delete_it": "Vă rugăm să eliminați segmentul din aceste chestionare pentru a-l șterge.",
|
"please_remove_the_segment_from_these_surveys_in_order_to_delete_it": "Vă rugăm să eliminați segmentul din aceste chestionare pentru a-l șterge.",
|
||||||
@@ -944,7 +867,6 @@
|
|||||||
"user_targeting_is_currently_only_available_when": "Targetarea utilizatorilor este disponibilă în prezent doar atunci când",
|
"user_targeting_is_currently_only_available_when": "Targetarea utilizatorilor este disponibilă în prezent doar atunci când",
|
||||||
"value_cannot_be_empty": "Valoarea nu poate fi goală.",
|
"value_cannot_be_empty": "Valoarea nu poate fi goală.",
|
||||||
"value_must_be_a_number": "Valoarea trebuie să fie un număr.",
|
"value_must_be_a_number": "Valoarea trebuie să fie un număr.",
|
||||||
"value_must_be_positive": "Valoarea trebuie să fie un număr pozitiv.",
|
|
||||||
"view_filters": "Vizualizați filtrele",
|
"view_filters": "Vizualizați filtrele",
|
||||||
"where": "Unde",
|
"where": "Unde",
|
||||||
"with_the_formbricks_sdk": "cu SDK Formbricks"
|
"with_the_formbricks_sdk": "cu SDK Formbricks"
|
||||||
@@ -1031,32 +953,19 @@
|
|||||||
"enterprise_features": "Funcții Enterprise",
|
"enterprise_features": "Funcții Enterprise",
|
||||||
"get_an_enterprise_license_to_get_access_to_all_features": "Obțineți o licență Enterprise pentru a avea acces la toate funcționalitățile.",
|
"get_an_enterprise_license_to_get_access_to_all_features": "Obțineți o licență Enterprise pentru a avea acces la toate funcționalitățile.",
|
||||||
"keep_full_control_over_your_data_privacy_and_security": "Mențineți controlul complet asupra confidențialității și securității datelor dumneavoastră.",
|
"keep_full_control_over_your_data_privacy_and_security": "Mențineți controlul complet asupra confidențialității și securității datelor dumneavoastră.",
|
||||||
"license_invalid_description": "Cheia de licență din variabila de mediu ENTERPRISE_LICENSE_KEY nu este validă. Te rugăm să verifici dacă există greșeli de scriere sau să soliciți o cheie nouă.",
|
|
||||||
"license_status": "Stare licență",
|
|
||||||
"license_status_active": "Activă",
|
|
||||||
"license_status_description": "Starea licenței tale enterprise.",
|
|
||||||
"license_status_expired": "Expirată",
|
|
||||||
"license_status_invalid": "Licență invalidă",
|
|
||||||
"license_status_unreachable": "Indisponibilă",
|
|
||||||
"license_unreachable_grace_period": "Serverul de licențe nu poate fi contactat. Funcționalitățile enterprise rămân active timp de 3 zile, până la data de {gracePeriodEnd}.",
|
|
||||||
"no_call_needed_no_strings_attached_request_a_free_30_day_trial_license_to_test_all_features_by_filling_out_this_form": "Nicio apel necesar, fără obligații: Solicitați o licență de probă gratuită de 30 de zile pentru a testa toate funcțiile prin completarea acestui formular:",
|
"no_call_needed_no_strings_attached_request_a_free_30_day_trial_license_to_test_all_features_by_filling_out_this_form": "Nicio apel necesar, fără obligații: Solicitați o licență de probă gratuită de 30 de zile pentru a testa toate funcțiile prin completarea acestui formular:",
|
||||||
"no_credit_card_no_sales_call_just_test_it": "Nu este nevoie de card de credit. Fără apeluri de vânzări. Doar testează-l :)",
|
"no_credit_card_no_sales_call_just_test_it": "Nu este nevoie de card de credit. Fără apeluri de vânzări. Doar testează-l :)",
|
||||||
"on_request": "La cerere",
|
"on_request": "La cerere",
|
||||||
"organization_roles": "Roluri organizaționale (Administrator, Editor, Dezvoltator, etc.)",
|
"organization_roles": "Roluri organizaționale (Administrator, Editor, Dezvoltator, etc.)",
|
||||||
"questions_please_reach_out_to": "Întrebări? Vă rugăm să trimiteți mesaj către",
|
"questions_please_reach_out_to": "Întrebări? Vă rugăm să trimiteți mesaj către",
|
||||||
"recheck_license": "Verifică din nou licența",
|
|
||||||
"recheck_license_failed": "Verificarea licenței a eșuat. Serverul de licențe poate fi indisponibil.",
|
|
||||||
"recheck_license_invalid": "Cheia de licență este invalidă. Te rugăm să verifici variabila ENTERPRISE_LICENSE_KEY.",
|
|
||||||
"recheck_license_success": "Licența a fost verificată cu succes",
|
|
||||||
"recheck_license_unreachable": "Serverul de licențe este indisponibil. Te rugăm să încerci din nou mai târziu.",
|
|
||||||
"rechecking": "Se verifică din nou...",
|
|
||||||
"request_30_day_trial_license": "Solicitați o licență de încercare de 30 de zile",
|
"request_30_day_trial_license": "Solicitați o licență de încercare de 30 de zile",
|
||||||
"saml_sso": "SAML SSO",
|
"saml_sso": "SAML SSO",
|
||||||
"service_level_agreement": "Acord privind nivelul de servicii",
|
"service_level_agreement": "Acord privind nivelul de servicii",
|
||||||
"soc2_hipaa_iso_27001_compliance_check": "Verificare conformitate SOC2, HIPAA, ISO 27001",
|
"soc2_hipaa_iso_27001_compliance_check": "Verificare conformitate SOC2, HIPAA, ISO 27001",
|
||||||
"sso": "SSO (Google, Microsoft, OpenID Connect)",
|
"sso": "SSO (Google, Microsoft, OpenID Connect)",
|
||||||
"teams": "Echipe & Roluri de Acces (Citiți, Citiți și Scrieți, Gestionați)",
|
"teams": "Echipe & Roluri de Acces (Citiți, Citiți și Scrieți, Gestionați)",
|
||||||
"unlock_the_full_power_of_formbricks_free_for_30_days": "Deblocați puterea completă a Formbricks. Gratuit timp de 30 de zile."
|
"unlock_the_full_power_of_formbricks_free_for_30_days": "Deblocați puterea completă a Formbricks. Gratuit timp de 30 de zile.",
|
||||||
|
"your_enterprise_license_is_active_all_features_unlocked": "Licența dvs. Enterprise este activă. Toate funcțiile sunt deblocate."
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"bulk_invite_warning_description": "În planul gratuit, toți membrii organizației sunt întotdeauna alocați rolului „Proprietar”.",
|
"bulk_invite_warning_description": "În planul gratuit, toți membrii organizației sunt întotdeauna alocați rolului „Proprietar”.",
|
||||||
@@ -1080,7 +989,7 @@
|
|||||||
"from_your_organization": "din organizația ta",
|
"from_your_organization": "din organizația ta",
|
||||||
"invitation_sent_once_more": "Invitație trimisă din nou.",
|
"invitation_sent_once_more": "Invitație trimisă din nou.",
|
||||||
"invite_deleted_successfully": "Invitație ștearsă cu succes",
|
"invite_deleted_successfully": "Invitație ștearsă cu succes",
|
||||||
"invite_expires_on": "Invitația expiră pe {date}",
|
"invited_on": "Invitat pe {date}",
|
||||||
"invites_failed": "Invitații eșuate",
|
"invites_failed": "Invitații eșuate",
|
||||||
"leave_organization": "Părăsește organizația",
|
"leave_organization": "Părăsește organizația",
|
||||||
"leave_organization_description": "Vei părăsi această organizație și vei pierde accesul la toate sondajele și răspunsurile. Poți să te alături din nou doar dacă ești invitat.",
|
"leave_organization_description": "Vei părăsi această organizație și vei pierde accesul la toate sondajele și răspunsurile. Poți să te alături din nou doar dacă ești invitat.",
|
||||||
@@ -1193,6 +1102,8 @@
|
|||||||
"please_fill_all_workspace_fields": "Vă rugăm să completați toate câmpurile pentru a adăuga un nou spațiu de lucru.",
|
"please_fill_all_workspace_fields": "Vă rugăm să completați toate câmpurile pentru a adăuga un nou spațiu de lucru.",
|
||||||
"read": "Citește",
|
"read": "Citește",
|
||||||
"read_write": "Citire & Scriere",
|
"read_write": "Citire & Scriere",
|
||||||
|
"select_member": "Selectează membrul",
|
||||||
|
"select_workspace": "Selectați spațiul de lucru",
|
||||||
"team_admin": "Administrator Echipe",
|
"team_admin": "Administrator Echipe",
|
||||||
"team_created_successfully": "Echipă creată cu succes",
|
"team_created_successfully": "Echipă creată cu succes",
|
||||||
"team_deleted_successfully": "Echipă ștearsă cu succes.",
|
"team_deleted_successfully": "Echipă ștearsă cu succes.",
|
||||||
@@ -1242,6 +1153,7 @@
|
|||||||
"add_fallback_placeholder": "Adaugă un placeholder pentru a afișa dacă nu există valoare de reamintit",
|
"add_fallback_placeholder": "Adaugă un placeholder pentru a afișa dacă nu există valoare de reamintit",
|
||||||
"add_hidden_field_id": "Adăugați ID câmp ascuns",
|
"add_hidden_field_id": "Adăugați ID câmp ascuns",
|
||||||
"add_highlight_border": "Adaugă bordură evidențiată",
|
"add_highlight_border": "Adaugă bordură evidențiată",
|
||||||
|
"add_highlight_border_description": "Adaugă o margine exterioară cardului tău de sondaj.",
|
||||||
"add_logic": "Adaugă logică",
|
"add_logic": "Adaugă logică",
|
||||||
"add_none_of_the_above": "Adăugați \"Niciuna dintre cele de mai sus\"",
|
"add_none_of_the_above": "Adăugați \"Niciuna dintre cele de mai sus\"",
|
||||||
"add_option": "Adăugați opțiune",
|
"add_option": "Adăugați opțiune",
|
||||||
@@ -1280,7 +1192,6 @@
|
|||||||
"block_duplicated": "Bloc duplicat.",
|
"block_duplicated": "Bloc duplicat.",
|
||||||
"bold": "Îngroșat",
|
"bold": "Îngroșat",
|
||||||
"brand_color": "Culoarea brandului",
|
"brand_color": "Culoarea brandului",
|
||||||
"brand_color_description": "Se aplică pe butoane, linkuri și evidențieri.",
|
|
||||||
"brightness": "Luminozitate",
|
"brightness": "Luminozitate",
|
||||||
"bulk_edit": "Editare în bloc",
|
"bulk_edit": "Editare în bloc",
|
||||||
"bulk_edit_description": "Editați toate opțiunile de mai jos, câte una pe linie. Liniile goale vor fi omise, iar duplicatele vor fi eliminate.",
|
"bulk_edit_description": "Editați toate opțiunile de mai jos, câte una pe linie. Liniile goale vor fi omise, iar duplicatele vor fi eliminate.",
|
||||||
@@ -1298,9 +1209,7 @@
|
|||||||
"capture_new_action": "Capturați acțiune nouă",
|
"capture_new_action": "Capturați acțiune nouă",
|
||||||
"card_arrangement_for_survey_type_derived": "Aranjament de carduri pentru sondaje de tip {surveyTypeDerived}",
|
"card_arrangement_for_survey_type_derived": "Aranjament de carduri pentru sondaje de tip {surveyTypeDerived}",
|
||||||
"card_background_color": "Culoarea de fundal a cardului",
|
"card_background_color": "Culoarea de fundal a cardului",
|
||||||
"card_background_color_description": "Umple zona cardului de sondaj.",
|
|
||||||
"card_border_color": "Culoarea bordurii cardului",
|
"card_border_color": "Culoarea bordurii cardului",
|
||||||
"card_border_color_description": "Conturează cardul sondajului.",
|
|
||||||
"card_styling": "Stilizare card",
|
"card_styling": "Stilizare card",
|
||||||
"casual": "Casual",
|
"casual": "Casual",
|
||||||
"caution_edit_duplicate": "Duplică & editează",
|
"caution_edit_duplicate": "Duplică & editează",
|
||||||
@@ -1311,12 +1220,20 @@
|
|||||||
"caution_explanation_responses_are_safe": "Răspunsurile mai vechi și mai noi se amestecă, ceea ce poate duce la rezumate de date înșelătoare.",
|
"caution_explanation_responses_are_safe": "Răspunsurile mai vechi și mai noi se amestecă, ceea ce poate duce la rezumate de date înșelătoare.",
|
||||||
"caution_recommendation": "Aceasta poate cauza inconsistențe de date în rezultatul sondajului. Vă recomandăm să duplicați sondajul în schimb.",
|
"caution_recommendation": "Aceasta poate cauza inconsistențe de date în rezultatul sondajului. Vă recomandăm să duplicați sondajul în schimb.",
|
||||||
"caution_text": "Schimbările vor duce la inconsecvențe",
|
"caution_text": "Schimbările vor duce la inconsecvențe",
|
||||||
|
"centered_modal_overlay_color": "Culoare suprapunere modală centralizată",
|
||||||
"change_anyway": "Schimbă oricum",
|
"change_anyway": "Schimbă oricum",
|
||||||
"change_background": "Schimbați fundalul",
|
"change_background": "Schimbați fundalul",
|
||||||
"change_question_type": "Schimbă tipul întrebării",
|
"change_question_type": "Schimbă tipul întrebării",
|
||||||
"change_survey_type": "Schimbarea tipului chestionarului afectează accesul existent",
|
"change_survey_type": "Schimbarea tipului chestionarului afectează accesul existent",
|
||||||
|
"change_the_background_color_of_the_card": "Schimbați culoarea de fundal a cardului.",
|
||||||
|
"change_the_background_color_of_the_input_fields": "Schimbați culoarea de fundal a câmpurilor de introducere.",
|
||||||
"change_the_background_to_a_color_image_or_animation": "Schimbați fundalul cu o culoare, imagine sau animație.",
|
"change_the_background_to_a_color_image_or_animation": "Schimbați fundalul cu o culoare, imagine sau animație.",
|
||||||
|
"change_the_border_color_of_the_card": "Schimbați culoarea bordurii cardului.",
|
||||||
|
"change_the_border_color_of_the_input_fields": "Schimbați culoarea bordurii câmpurilor de introducere.",
|
||||||
|
"change_the_border_radius_of_the_card_and_the_inputs": "Schimbați raza de rotunjire a cardului și a câmpurilor de introducere.",
|
||||||
|
"change_the_brand_color_of_the_survey": "Schimbați culoarea brandului chestionarului",
|
||||||
"change_the_placement_of_this_survey": "Schimbă amplasarea acestui sondaj.",
|
"change_the_placement_of_this_survey": "Schimbă amplasarea acestui sondaj.",
|
||||||
|
"change_the_question_color_of_the_survey": "Schimbați culoarea întrebării chestionarului.",
|
||||||
"changes_saved": "Modificările au fost salvate",
|
"changes_saved": "Modificările au fost salvate",
|
||||||
"changing_survey_type_will_remove_existing_distribution_channels": "Schimbarea tipului chestionarului va afecta modul în care acesta poate fi distribuit. Dacă respondenții au deja linkuri de acces pentru tipul curent, aceștia ar putea pierde accesul după schimbare.",
|
"changing_survey_type_will_remove_existing_distribution_channels": "Schimbarea tipului chestionarului va afecta modul în care acesta poate fi distribuit. Dacă respondenții au deja linkuri de acces pentru tipul curent, aceștia ar putea pierde accesul după schimbare.",
|
||||||
"checkbox_label": "Etichetă casetă de selectare",
|
"checkbox_label": "Etichetă casetă de selectare",
|
||||||
@@ -1456,6 +1373,7 @@
|
|||||||
"hide_progress_bar": "Ascunde bara de progres",
|
"hide_progress_bar": "Ascunde bara de progres",
|
||||||
"hide_question_settings": "Ascunde setările întrebării",
|
"hide_question_settings": "Ascunde setările întrebării",
|
||||||
"hostname": "Nume gazdă",
|
"hostname": "Nume gazdă",
|
||||||
|
"how_funky_do_you_want_your_cards_in_survey_type_derived_surveys": "Cât de funky doriți să fie cardurile dumneavoastră în sondajele de tip {surveyTypeDerived}",
|
||||||
"if_you_need_more_please": "Dacă aveți nevoie de mai mult, vă rugăm",
|
"if_you_need_more_please": "Dacă aveți nevoie de mai mult, vă rugăm",
|
||||||
"if_you_really_want_that_answer_ask_until_you_get_it": "Continuă afișarea ori de câte ori este declanșat până când se trimite un răspuns.",
|
"if_you_really_want_that_answer_ask_until_you_get_it": "Continuă afișarea ori de câte ori este declanșat până când se trimite un răspuns.",
|
||||||
"ignore_global_waiting_time": "Ignoră perioada de răcire",
|
"ignore_global_waiting_time": "Ignoră perioada de răcire",
|
||||||
@@ -1466,9 +1384,7 @@
|
|||||||
"initial_value": "Valoare inițială",
|
"initial_value": "Valoare inițială",
|
||||||
"inner_text": "Text Interior",
|
"inner_text": "Text Interior",
|
||||||
"input_border_color": "Culoarea graniței câmpului de introducere",
|
"input_border_color": "Culoarea graniței câmpului de introducere",
|
||||||
"input_border_color_description": "Conturează câmpurile de text și zonele de text.",
|
|
||||||
"input_color": "Culoarea câmpului de introducere",
|
"input_color": "Culoarea câmpului de introducere",
|
||||||
"input_color_description": "Umple interiorul câmpurilor de text.",
|
|
||||||
"insert_link": "Inserează link",
|
"insert_link": "Inserează link",
|
||||||
"invalid_targeting": "\"Targetare nevalidă: Vă rugăm să verificați filtrele pentru audiență\"",
|
"invalid_targeting": "\"Targetare nevalidă: Vă rugăm să verificați filtrele pentru audiență\"",
|
||||||
"invalid_video_url_warning": "Vă rugăm să introduceți un URL valid de YouTube, Vimeo sau Loom. În prezent nu susținem alți furnizori de găzduire video.",
|
"invalid_video_url_warning": "Vă rugăm să introduceți un URL valid de YouTube, Vimeo sau Loom. În prezent nu susținem alți furnizori de găzduire video.",
|
||||||
@@ -1552,12 +1468,13 @@
|
|||||||
"protect_survey_with_pin_description": "Doar utilizatorii care cunosc PIN-ul pot accesa sondajul.",
|
"protect_survey_with_pin_description": "Doar utilizatorii care cunosc PIN-ul pot accesa sondajul.",
|
||||||
"publish": "Publică",
|
"publish": "Publică",
|
||||||
"question": "Întrebare",
|
"question": "Întrebare",
|
||||||
|
"question_color": "Culoarea întrebării",
|
||||||
"question_deleted": "Întrebare ștearsă.",
|
"question_deleted": "Întrebare ștearsă.",
|
||||||
"question_duplicated": "Întrebare duplicată.",
|
"question_duplicated": "Întrebare duplicată.",
|
||||||
"question_id_updated": "ID întrebare actualizat",
|
"question_id_updated": "ID întrebare actualizat",
|
||||||
"question_used_in_logic_warning_text": "Elemente din acest bloc sunt folosite într-o regulă de logică. Sigur doriți să îl ștergeți?",
|
"question_used_in_logic_warning_text": "Elemente din acest bloc sunt folosite într-o regulă de logică. Sigur doriți să îl ștergeți?",
|
||||||
"question_used_in_logic_warning_title": "Inconsistență logică",
|
"question_used_in_logic_warning_title": "Inconsistență logică",
|
||||||
"question_used_in_quota": "Întrebarea aceasta este folosită în cota „{quotaName}”",
|
"question_used_in_quota": "Întrebarea aceasta este folosită în cota \"{quotaName}\"",
|
||||||
"question_used_in_recall": "Această întrebare este reamintită în întrebarea {questionIndex}.",
|
"question_used_in_recall": "Această întrebare este reamintită în întrebarea {questionIndex}.",
|
||||||
"question_used_in_recall_ending_card": "Această întrebare este reamintită în Cardul de Încheiere.",
|
"question_used_in_recall_ending_card": "Această întrebare este reamintită în Cardul de Încheiere.",
|
||||||
"quotas": {
|
"quotas": {
|
||||||
@@ -1613,7 +1530,6 @@
|
|||||||
"response_limits_redirections_and_more": "Limite de răspunsuri, redirecționări și altele.",
|
"response_limits_redirections_and_more": "Limite de răspunsuri, redirecționări și altele.",
|
||||||
"response_options": "Opțiuni răspuns",
|
"response_options": "Opțiuni răspuns",
|
||||||
"roundness": "Rotunjire",
|
"roundness": "Rotunjire",
|
||||||
"roundness_description": "Controlează cât de rotunjite sunt colțurile cardului.",
|
|
||||||
"row_used_in_logic_error": "Această linie este folosită în logica întrebării {questionIndex}. Vă rugăm să-l eliminați din logică mai întâi.",
|
"row_used_in_logic_error": "Această linie este folosită în logica întrebării {questionIndex}. Vă rugăm să-l eliminați din logică mai întâi.",
|
||||||
"rows": "Rânduri",
|
"rows": "Rânduri",
|
||||||
"save_and_close": "Salvează & Închide",
|
"save_and_close": "Salvează & Închide",
|
||||||
@@ -1655,6 +1571,7 @@
|
|||||||
"styling_set_to_theme_styles": "Stilizare setată la stilurile temei",
|
"styling_set_to_theme_styles": "Stilizare setată la stilurile temei",
|
||||||
"subheading": "Subtitlu",
|
"subheading": "Subtitlu",
|
||||||
"subtract": "Scade -",
|
"subtract": "Scade -",
|
||||||
|
"suggest_colors": "Sugerați culori",
|
||||||
"survey_completed_heading": "Sondaj Completat",
|
"survey_completed_heading": "Sondaj Completat",
|
||||||
"survey_completed_subheading": "Acest sondaj gratuit și open-source a fost închis",
|
"survey_completed_subheading": "Acest sondaj gratuit și open-source a fost închis",
|
||||||
"survey_display_settings": "Setări de afișare a sondajului",
|
"survey_display_settings": "Setări de afișare a sondajului",
|
||||||
@@ -1731,7 +1648,7 @@
|
|||||||
"validation_rules": "Reguli de validare",
|
"validation_rules": "Reguli de validare",
|
||||||
"validation_rules_description": "Acceptă doar răspunsurile care îndeplinesc următoarele criterii",
|
"validation_rules_description": "Acceptă doar răspunsurile care îndeplinesc următoarele criterii",
|
||||||
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} este folosit în logica întrebării {questionIndex}. Vă rugăm să-l eliminați din logică mai întâi.",
|
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} este folosit în logica întrebării {questionIndex}. Vă rugăm să-l eliminați din logică mai întâi.",
|
||||||
"variable_is_used_in_quota_please_remove_it_from_quota_first": "Variabila „{variableName}” este folosită în cota „{quotaName}”. Vă rugăm să o eliminați mai întâi din cotă",
|
"variable_is_used_in_quota_please_remove_it_from_quota_first": "Variabila \"{variableName}\" este folosită în cota \"{quotaName}\"",
|
||||||
"variable_name_conflicts_with_hidden_field": "Numele variabilei intră în conflict cu un ID de câmp ascuns existent.",
|
"variable_name_conflicts_with_hidden_field": "Numele variabilei intră în conflict cu un ID de câmp ascuns existent.",
|
||||||
"variable_name_is_already_taken_please_choose_another": "Numele variabilei este deja utilizat, vă rugăm să alegeți altul.",
|
"variable_name_is_already_taken_please_choose_another": "Numele variabilei este deja utilizat, vă rugăm să alegeți altul.",
|
||||||
"variable_name_must_start_with_a_letter": "Numele variabilei trebuie să înceapă cu o literă.",
|
"variable_name_must_start_with_a_letter": "Numele variabilei trebuie să înceapă cu o literă.",
|
||||||
@@ -1957,7 +1874,6 @@
|
|||||||
"filtered_responses_excel": "Răspunsuri filtrate (Excel)",
|
"filtered_responses_excel": "Răspunsuri filtrate (Excel)",
|
||||||
"generating_qr_code": "Se generează codul QR",
|
"generating_qr_code": "Se generează codul QR",
|
||||||
"impressions": "Impresii",
|
"impressions": "Impresii",
|
||||||
"impressions_identified_only": "Se afișează doar impresiile de la contactele identificate",
|
|
||||||
"impressions_tooltip": "Număr de ori când sondajul a fost vizualizat.",
|
"impressions_tooltip": "Număr de ori când sondajul a fost vizualizat.",
|
||||||
"in_app": {
|
"in_app": {
|
||||||
"connection_description": "Sondajul va fi afișat utilizatorilor site-ului dvs. web, care îndeplinesc criteriile enumerate mai jos",
|
"connection_description": "Sondajul va fi afișat utilizatorilor site-ului dvs. web, care îndeplinesc criteriile enumerate mai jos",
|
||||||
@@ -2000,7 +1916,6 @@
|
|||||||
"last_quarter": "Ultimul trimestru",
|
"last_quarter": "Ultimul trimestru",
|
||||||
"last_year": "Anul trecut",
|
"last_year": "Anul trecut",
|
||||||
"limit": "Limită",
|
"limit": "Limită",
|
||||||
"no_identified_impressions": "Nicio impresie de la contactele identificate",
|
|
||||||
"no_responses_found": "Nu s-au găsit răspunsuri",
|
"no_responses_found": "Nu s-au găsit răspunsuri",
|
||||||
"other_values_found": "Alte valori găsite",
|
"other_values_found": "Alte valori găsite",
|
||||||
"overall": "General",
|
"overall": "General",
|
||||||
@@ -2140,71 +2055,9 @@
|
|||||||
"look": {
|
"look": {
|
||||||
"add_background_color": "Adăugați culoare de fundal",
|
"add_background_color": "Adăugați culoare de fundal",
|
||||||
"add_background_color_description": "Adăugați o culoare de fundal la containerul siglei.",
|
"add_background_color_description": "Adăugați o culoare de fundal la containerul siglei.",
|
||||||
"advanced_styling_field_border_radius": "Raza colțurilor",
|
|
||||||
"advanced_styling_field_button_bg": "Fundal buton",
|
|
||||||
"advanced_styling_field_button_bg_description": "Umple butonul Următor / Trimite.",
|
|
||||||
"advanced_styling_field_button_border_radius_description": "Rotunjește colțurile butonului.",
|
|
||||||
"advanced_styling_field_button_font_size_description": "Scalează textul etichetei butonului.",
|
|
||||||
"advanced_styling_field_button_font_weight_description": "Face textul butonului mai subțire sau mai îngroșat.",
|
|
||||||
"advanced_styling_field_button_height_description": "Controlează înălțimea butonului.",
|
|
||||||
"advanced_styling_field_button_padding_x_description": "Adaugă spațiu la stânga și la dreapta.",
|
|
||||||
"advanced_styling_field_button_padding_y_description": "Adaugă spațiu sus și jos.",
|
|
||||||
"advanced_styling_field_button_text": "Text buton",
|
|
||||||
"advanced_styling_field_button_text_description": "Colorează eticheta din interiorul butoanelor.",
|
|
||||||
"advanced_styling_field_description_color": "Culoare descriere",
|
|
||||||
"advanced_styling_field_description_color_description": "Colorează textul de sub fiecare titlu.",
|
|
||||||
"advanced_styling_field_description_size": "Mărime font descriere",
|
|
||||||
"advanced_styling_field_description_size_description": "Scalează textul descrierii.",
|
|
||||||
"advanced_styling_field_description_weight": "Grosime font descriere",
|
|
||||||
"advanced_styling_field_description_weight_description": "Face textul descrierii mai subțire sau mai îngroșat.",
|
|
||||||
"advanced_styling_field_font_size": "Mărime font",
|
|
||||||
"advanced_styling_field_font_weight": "Grosime font",
|
|
||||||
"advanced_styling_field_headline_color": "Culoare titlu",
|
|
||||||
"advanced_styling_field_headline_color_description": "Colorează textul principal al întrebării.",
|
|
||||||
"advanced_styling_field_headline_size": "Mărime font titlu",
|
|
||||||
"advanced_styling_field_headline_size_description": "Scalează textul titlului.",
|
|
||||||
"advanced_styling_field_headline_weight": "Grosime font titlu",
|
|
||||||
"advanced_styling_field_headline_weight_description": "Face textul titlului mai subțire sau mai îngroșat.",
|
|
||||||
"advanced_styling_field_height": "Înălțime minimă",
|
|
||||||
"advanced_styling_field_indicator_bg": "Fundal indicator",
|
|
||||||
"advanced_styling_field_indicator_bg_description": "Colorează partea umplută a barei.",
|
|
||||||
"advanced_styling_field_input_border_radius_description": "Rotunjește colțurile câmpurilor de introducere.",
|
|
||||||
"advanced_styling_field_input_font_size_description": "Scalează textul introdus în câmpuri.",
|
|
||||||
"advanced_styling_field_input_height_description": "Controlează înălțimea minimă a câmpului de introducere.",
|
|
||||||
"advanced_styling_field_input_padding_x_description": "Adaugă spațiu la stânga și la dreapta.",
|
|
||||||
"advanced_styling_field_input_padding_y_description": "Adaugă spațiu deasupra și dedesubt.",
|
|
||||||
"advanced_styling_field_input_placeholder_opacity_description": "Estompează textul de sugestie din placeholder.",
|
|
||||||
"advanced_styling_field_input_shadow_description": "Adaugă o umbră în jurul câmpurilor de introducere.",
|
|
||||||
"advanced_styling_field_input_text": "Text câmp",
|
|
||||||
"advanced_styling_field_input_text_description": "Colorează textul introdus în câmpuri.",
|
|
||||||
"advanced_styling_field_option_bg": "Fundal",
|
|
||||||
"advanced_styling_field_option_bg_description": "Umple elementele de opțiune.",
|
|
||||||
"advanced_styling_field_option_border_radius_description": "Rotunjește colțurile opțiunilor.",
|
|
||||||
"advanced_styling_field_option_font_size_description": "Redimensionează textul etichetei opțiunii.",
|
|
||||||
"advanced_styling_field_option_label": "Culoare etichetă",
|
|
||||||
"advanced_styling_field_option_label_description": "Colorează textul etichetei opțiunii.",
|
|
||||||
"advanced_styling_field_option_padding_x_description": "Adaugă spațiu în stânga și în dreapta.",
|
|
||||||
"advanced_styling_field_option_padding_y_description": "Adaugă spațiu deasupra și dedesubt.",
|
|
||||||
"advanced_styling_field_padding_x": "Spațiere X",
|
|
||||||
"advanced_styling_field_padding_y": "Spațiere Y",
|
|
||||||
"advanced_styling_field_placeholder_opacity": "Opacitate placeholder",
|
|
||||||
"advanced_styling_field_shadow": "Umbră",
|
|
||||||
"advanced_styling_field_track_bg": "Fundal track",
|
|
||||||
"advanced_styling_field_track_bg_description": "Colorează partea necompletată a barei.",
|
|
||||||
"advanced_styling_field_track_height": "Înălțime track",
|
|
||||||
"advanced_styling_field_track_height_description": "Controlează grosimea barei de progres.",
|
|
||||||
"advanced_styling_field_upper_label_color": "Culoare etichetă titlu",
|
|
||||||
"advanced_styling_field_upper_label_color_description": "Colorează eticheta mică de deasupra câmpurilor.",
|
|
||||||
"advanced_styling_field_upper_label_size": "Mărime font etichetă titlu",
|
|
||||||
"advanced_styling_field_upper_label_size_description": "Redimensionează eticheta mică de deasupra câmpurilor.",
|
|
||||||
"advanced_styling_field_upper_label_weight": "Grosime font etichetă titlu",
|
|
||||||
"advanced_styling_field_upper_label_weight_description": "Face eticheta mai subțire sau mai îngroșată.",
|
|
||||||
"advanced_styling_section_buttons": "Butoane",
|
|
||||||
"advanced_styling_section_headlines": "Titluri și descrieri",
|
|
||||||
"advanced_styling_section_inputs": "Inputuri",
|
|
||||||
"advanced_styling_section_options": "Opțiuni (Radio/Checkbox)",
|
|
||||||
"app_survey_placement": "Amplasarea sondajului în aplicație",
|
"app_survey_placement": "Amplasarea sondajului în aplicație",
|
||||||
"app_survey_placement_settings_description": "Schimbați unde vor fi afișate sondajele în aplicația sau site-ul dvs. web.",
|
"app_survey_placement_settings_description": "Schimbați unde vor fi afișate sondajele în aplicația sau site-ul dvs. web.",
|
||||||
|
"centered_modal_overlay_color": "Culoare suprapunere modală centralizată",
|
||||||
"email_customization": "Personalizare email",
|
"email_customization": "Personalizare email",
|
||||||
"email_customization_description": "Schimbați aspectul și stilul emailurilor trimise de Formbricks în numele dvs.",
|
"email_customization_description": "Schimbați aspectul și stilul emailurilor trimise de Formbricks în numele dvs.",
|
||||||
"enable_custom_styling": "Activați stilizarea personalizată",
|
"enable_custom_styling": "Activați stilizarea personalizată",
|
||||||
@@ -2215,9 +2068,6 @@
|
|||||||
"formbricks_branding_hidden": "Brandingul Formbricks este ascuns.",
|
"formbricks_branding_hidden": "Brandingul Formbricks este ascuns.",
|
||||||
"formbricks_branding_settings_description": "Ne bucurăm de susținerea ta, dar înțelegem dacă vrei să dezactivezi această opțiune.",
|
"formbricks_branding_settings_description": "Ne bucurăm de susținerea ta, dar înțelegem dacă vrei să dezactivezi această opțiune.",
|
||||||
"formbricks_branding_shown": "Brandingul Formbricks este afișat.",
|
"formbricks_branding_shown": "Brandingul Formbricks este afișat.",
|
||||||
"generate_theme_btn": "Generează",
|
|
||||||
"generate_theme_confirmation": "Vrei să generezi o temă de culori potrivită pe baza culorii brandului tău? Aceasta va suprascrie setările actuale de culoare.",
|
|
||||||
"generate_theme_header": "Generezi temă de culori?",
|
|
||||||
"logo_removed_successfully": "Sigla a fost eliminată cu succes",
|
"logo_removed_successfully": "Sigla a fost eliminată cu succes",
|
||||||
"logo_settings_description": "Încarcă sigla companiei pentru a personaliza sondajele și previzualizările de linkuri.",
|
"logo_settings_description": "Încarcă sigla companiei pentru a personaliza sondajele și previzualizările de linkuri.",
|
||||||
"logo_updated_successfully": "Sigla a fost actualizată cu succes",
|
"logo_updated_successfully": "Sigla a fost actualizată cu succes",
|
||||||
@@ -2232,8 +2082,6 @@
|
|||||||
"show_formbricks_branding_in": "Afișează brandingul Formbricks în sondajele de tip {type}",
|
"show_formbricks_branding_in": "Afișează brandingul Formbricks în sondajele de tip {type}",
|
||||||
"show_powered_by_formbricks": "Afișează semnătura „Powered by Formbricks”",
|
"show_powered_by_formbricks": "Afișează semnătura „Powered by Formbricks”",
|
||||||
"styling_updated_successfully": "Stilizarea a fost actualizată cu succes",
|
"styling_updated_successfully": "Stilizarea a fost actualizată cu succes",
|
||||||
"suggest_colors": "Sugerează culori",
|
|
||||||
"suggested_colors_applied_please_save": "Culorile sugerate au fost generate cu succes. Apasă pe „Salvează” pentru a păstra modificările.",
|
|
||||||
"theme": "Temă",
|
"theme": "Temă",
|
||||||
"theme_settings_description": "Creează o temă de stil pentru toate sondajele. Poți activa stilizare personalizată pentru fiecare sondaj."
|
"theme_settings_description": "Creează o temă de stil pentru toate sondajele. Poți activa stilizare personalizată pentru fiecare sondaj."
|
||||||
},
|
},
|
||||||
@@ -2997,7 +2845,6 @@
|
|||||||
"preview_survey_question_2_choice_1_label": "Da, ține-mă informat.",
|
"preview_survey_question_2_choice_1_label": "Da, ține-mă informat.",
|
||||||
"preview_survey_question_2_choice_2_label": "Nu, mulţumesc!",
|
"preview_survey_question_2_choice_2_label": "Nu, mulţumesc!",
|
||||||
"preview_survey_question_2_headline": "Vrei să fii în temă?",
|
"preview_survey_question_2_headline": "Vrei să fii în temă?",
|
||||||
"preview_survey_question_2_subheader": "Aceasta este o descriere exemplu.",
|
|
||||||
"preview_survey_welcome_card_headline": "Bun venit!",
|
"preview_survey_welcome_card_headline": "Bun venit!",
|
||||||
"prioritize_features_description": "Identificați caracteristicile de care utilizatorii dumneavoastră au cel mai mult și cel mai puțin nevoie.",
|
"prioritize_features_description": "Identificați caracteristicile de care utilizatorii dumneavoastră au cel mai mult și cel mai puțin nevoie.",
|
||||||
"prioritize_features_name": "Prioritizați caracteristicile",
|
"prioritize_features_name": "Prioritizați caracteristicile",
|
||||||
|
|||||||
+24
-177
@@ -188,7 +188,6 @@
|
|||||||
"customer_success": "Customer Success",
|
"customer_success": "Customer Success",
|
||||||
"dark_overlay": "Тёмный оверлей",
|
"dark_overlay": "Тёмный оверлей",
|
||||||
"date": "Дата",
|
"date": "Дата",
|
||||||
"days": "дни",
|
|
||||||
"default": "По умолчанию",
|
"default": "По умолчанию",
|
||||||
"delete": "Удалить",
|
"delete": "Удалить",
|
||||||
"description": "Описание",
|
"description": "Описание",
|
||||||
@@ -218,7 +217,6 @@
|
|||||||
"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": "Развернуть строки",
|
||||||
@@ -277,7 +275,6 @@
|
|||||||
"mobile_overlay_app_works_best_on_desktop": "Formbricks лучше всего работает на большом экране. Для управления или создания опросов перейдите на другое устройство.",
|
"mobile_overlay_app_works_best_on_desktop": "Formbricks лучше всего работает на большом экране. Для управления или создания опросов перейдите на другое устройство.",
|
||||||
"mobile_overlay_surveys_look_good": "Не волнуйтесь — ваши опросы отлично выглядят на любом устройстве и экране!",
|
"mobile_overlay_surveys_look_good": "Не волнуйтесь — ваши опросы отлично выглядят на любом устройстве и экране!",
|
||||||
"mobile_overlay_title": "Ой, обнаружен маленький экран!",
|
"mobile_overlay_title": "Ой, обнаружен маленький экран!",
|
||||||
"months": "месяцы",
|
|
||||||
"move_down": "Переместить вниз",
|
"move_down": "Переместить вниз",
|
||||||
"move_up": "Переместить вверх",
|
"move_up": "Переместить вверх",
|
||||||
"multiple_languages": "Несколько языков",
|
"multiple_languages": "Несколько языков",
|
||||||
@@ -288,7 +285,6 @@
|
|||||||
"no_background_image_found": "Фоновое изображение не найдено.",
|
"no_background_image_found": "Фоновое изображение не найдено.",
|
||||||
"no_code": "Нет кода",
|
"no_code": "Нет кода",
|
||||||
"no_files_uploaded": "Файлы не были загружены",
|
"no_files_uploaded": "Файлы не были загружены",
|
||||||
"no_overlay": "Без наложения",
|
|
||||||
"no_quotas_found": "Квоты не найдены",
|
"no_quotas_found": "Квоты не найдены",
|
||||||
"no_result_found": "Результат не найден",
|
"no_result_found": "Результат не найден",
|
||||||
"no_results": "Нет результатов",
|
"no_results": "Нет результатов",
|
||||||
@@ -315,7 +311,6 @@
|
|||||||
"organization_teams_not_found": "Команды организации не найдены",
|
"organization_teams_not_found": "Команды организации не найдены",
|
||||||
"other": "Другое",
|
"other": "Другое",
|
||||||
"others": "Другие",
|
"others": "Другие",
|
||||||
"overlay_color": "Цвет наложения",
|
|
||||||
"overview": "Обзор",
|
"overview": "Обзор",
|
||||||
"password": "Пароль",
|
"password": "Пароль",
|
||||||
"paused": "Приостановлено",
|
"paused": "Приостановлено",
|
||||||
@@ -355,7 +350,6 @@
|
|||||||
"request_trial_license": "Запросить пробную лицензию",
|
"request_trial_license": "Запросить пробную лицензию",
|
||||||
"reset_to_default": "Сбросить по умолчанию",
|
"reset_to_default": "Сбросить по умолчанию",
|
||||||
"response": "Ответ",
|
"response": "Ответ",
|
||||||
"response_id": "ID ответа",
|
|
||||||
"responses": "Ответы",
|
"responses": "Ответы",
|
||||||
"restart": "Перезапустить",
|
"restart": "Перезапустить",
|
||||||
"role": "Роль",
|
"role": "Роль",
|
||||||
@@ -396,7 +390,6 @@
|
|||||||
"status": "Статус",
|
"status": "Статус",
|
||||||
"step_by_step_manual": "Пошаговая инструкция",
|
"step_by_step_manual": "Пошаговая инструкция",
|
||||||
"storage_not_configured": "Хранилище файлов не настроено, загрузка, скорее всего, не удастся",
|
"storage_not_configured": "Хранилище файлов не настроено, загрузка, скорее всего, не удастся",
|
||||||
"string": "Текст",
|
|
||||||
"styling": "Стилизация",
|
"styling": "Стилизация",
|
||||||
"submit": "Отправить",
|
"submit": "Отправить",
|
||||||
"summary": "Сводка",
|
"summary": "Сводка",
|
||||||
@@ -429,7 +422,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": "Обновлено",
|
||||||
@@ -453,7 +445,6 @@
|
|||||||
"website_and_app_connection": "Связь сайта и приложения",
|
"website_and_app_connection": "Связь сайта и приложения",
|
||||||
"website_app_survey": "Опрос сайта и приложения",
|
"website_app_survey": "Опрос сайта и приложения",
|
||||||
"website_survey": "Опрос сайта",
|
"website_survey": "Опрос сайта",
|
||||||
"weeks": "недели",
|
|
||||||
"welcome_card": "Приветственная карточка",
|
"welcome_card": "Приветственная карточка",
|
||||||
"workspace_configuration": "Настройка рабочего пространства",
|
"workspace_configuration": "Настройка рабочего пространства",
|
||||||
"workspace_created_successfully": "Рабочий проект успешно создан",
|
"workspace_created_successfully": "Рабочий проект успешно создан",
|
||||||
@@ -464,7 +455,6 @@
|
|||||||
"workspace_not_found": "Рабочее пространство не найдено",
|
"workspace_not_found": "Рабочее пространство не найдено",
|
||||||
"workspace_permission_not_found": "Разрешение на рабочее пространство не найдено",
|
"workspace_permission_not_found": "Разрешение на рабочее пространство не найдено",
|
||||||
"workspaces": "Рабочие пространства",
|
"workspaces": "Рабочие пространства",
|
||||||
"years": "годы",
|
|
||||||
"you": "Вы",
|
"you": "Вы",
|
||||||
"you_are_downgraded_to_the_community_edition": "Ваша версия понижена до Community Edition.",
|
"you_are_downgraded_to_the_community_edition": "Ваша версия понижена до Community Edition.",
|
||||||
"you_are_not_authorized_to_perform_this_action": "У вас нет прав для выполнения этого действия.",
|
"you_are_not_authorized_to_perform_this_action": "У вас нет прав для выполнения этого действия.",
|
||||||
@@ -625,7 +615,7 @@
|
|||||||
"attribute_created_successfully": "Атрибут успешно создан",
|
"attribute_created_successfully": "Атрибут успешно создан",
|
||||||
"attribute_description": "Описание",
|
"attribute_description": "Описание",
|
||||||
"attribute_description_placeholder": "Краткое описание",
|
"attribute_description_placeholder": "Краткое описание",
|
||||||
"attribute_key": "Ключ",
|
"attribute_key": "Атрибут",
|
||||||
"attribute_key_cannot_be_changed": "Ключ нельзя изменить после создания",
|
"attribute_key_cannot_be_changed": "Ключ нельзя изменить после создания",
|
||||||
"attribute_key_hint": "Только строчные буквы, цифры и символы подчёркивания. Должен начинаться с буквы.",
|
"attribute_key_hint": "Только строчные буквы, цифры и символы подчёркивания. Должен начинаться с буквы.",
|
||||||
"attribute_key_placeholder": "например, date_of_birth",
|
"attribute_key_placeholder": "например, date_of_birth",
|
||||||
@@ -636,45 +626,28 @@
|
|||||||
"attribute_updated_successfully": "Атрибут успешно обновлён",
|
"attribute_updated_successfully": "Атрибут успешно обновлён",
|
||||||
"attribute_value": "Значение",
|
"attribute_value": "Значение",
|
||||||
"attribute_value_placeholder": "Значение атрибута",
|
"attribute_value_placeholder": "Значение атрибута",
|
||||||
"attributes_msg_attribute_limit_exceeded": "Не удалось создать {count} новых атрибута, так как это превысит максимальное количество классов атрибутов: {limit}. Существующие атрибуты были успешно обновлены.",
|
|
||||||
"attributes_msg_attribute_type_validation_error": "{error} (атрибут «{key}» имеет тип данных: {dataType})",
|
|
||||||
"attributes_msg_email_already_exists": "Этот email уже существует в данной среде и не был обновлён.",
|
|
||||||
"attributes_msg_email_or_userid_required": "Требуется указать либо email, либо userId. Существующие значения были сохранены.",
|
|
||||||
"attributes_msg_new_attribute_created": "Создан новый атрибут «{key}» с типом «{dataType}»",
|
|
||||||
"attributes_msg_userid_already_exists": "Этот userId уже существует в данной среде и не был обновлён.",
|
|
||||||
"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": "Пользовательские атрибуты",
|
|
||||||
"data_type": "Тип данных",
|
|
||||||
"data_type_cannot_be_changed": "Тип данных нельзя изменить после создания",
|
|
||||||
"data_type_description": "Выберите, как этот атрибут будет храниться и фильтроваться",
|
|
||||||
"date_value_required": "Требуется значение даты. Используйте кнопку удаления, если не хотите указывать дату.",
|
|
||||||
"delete_attribute_confirmation": "{value, plural, one {Будет удалён выбранный атрибут. Все данные контактов, связанные с этим атрибутом, будут потеряны.} few {Будут удалены выбранные атрибуты. Все данные контактов, связанные с этими атрибутами, будут потеряны.} many {Будут удалены выбранные атрибуты. Все данные контактов, связанные с этими атрибутами, будут потеряны.} other {Будут удалены выбранные атрибуты. Все данные контактов, связанные с этими атрибутами, будут потеряны.}}",
|
"delete_attribute_confirmation": "{value, plural, one {Будет удалён выбранный атрибут. Все данные контактов, связанные с этим атрибутом, будут потеряны.} few {Будут удалены выбранные атрибуты. Все данные контактов, связанные с этими атрибутами, будут потеряны.} many {Будут удалены выбранные атрибуты. Все данные контактов, связанные с этими атрибутами, будут потеряны.} other {Будут удалены выбранные атрибуты. Все данные контактов, связанные с этими атрибутами, будут потеряны.}}",
|
||||||
"delete_contact_confirmation": "Это удалит все ответы на опросы и атрибуты контакта, связанные с этим контактом. Любая таргетинг и персонализация на основе данных этого контакта будут потеряны.",
|
"delete_contact_confirmation": "Это удалит все ответы на опросы и атрибуты контакта, связанные с этим контактом. Любая таргетинг и персонализация на основе данных этого контакта будут потеряны.",
|
||||||
"delete_contact_confirmation_with_quotas": "{value, plural, one {Это удалит все ответы на опросы и атрибуты контакта, связанные с этим контактом. Любая таргетинг и персонализация на основе данных этого контакта будут потеряны. Если у этого контакта есть ответы, которые учитываются в квотах опроса, количество по квотам будет уменьшено, но лимиты квот останутся без изменений.} few {Это удалит все ответы на опросы и атрибуты контактов, связанные с этими контактами. Любая таргетинг и персонализация на основе данных этих контактов будут потеряны. Если у этих контактов есть ответы, которые учитываются в квотах опроса, количество по квотам будет уменьшено, но лимиты квот останутся без изменений.} many {Это удалит все ответы на опросы и атрибуты контактов, связанные с этими контактами. Любая таргетинг и персонализация на основе данных этих контактов будут потеряны. Если у этих контактов есть ответы, которые учитываются в квотах опроса, количество по квотам будет уменьшено, но лимиты квот останутся без изменений.} other {Это удалит все ответы на опросы и атрибуты контактов, связанные с этими контактами. Любая таргетинг и персонализация на основе данных этих контактов будут потеряны. Если у этих контактов есть ответы, которые учитываются в квотах опроса, количество по квотам будет уменьшено, но лимиты квот останутся без изменений.}}",
|
"delete_contact_confirmation_with_quotas": "{value, plural, one {Это удалит все ответы на опросы и атрибуты контакта, связанные с этим контактом. Любая таргетинг и персонализация на основе данных этого контакта будут потеряны. Если у этого контакта есть ответы, которые учитываются в квотах опроса, количество по квотам будет уменьшено, но лимиты квот останутся без изменений.} few {Это удалит все ответы на опросы и атрибуты контактов, связанные с этими контактами. Любая таргетинг и персонализация на основе данных этих контактов будут потеряны. Если у этих контактов есть ответы, которые учитываются в квотах опроса, количество по квотам будет уменьшено, но лимиты квот останутся без изменений.} many {Это удалит все ответы на опросы и атрибуты контактов, связанные с этими контактами. Любая таргетинг и персонализация на основе данных этих контактов будут потеряны. Если у этих контактов есть ответы, которые учитываются в квотах опроса, количество по квотам будет уменьшено, но лимиты квот останутся без изменений.} other {Это удалит все ответы на опросы и атрибуты контактов, связанные с этими контактами. Любая таргетинг и персонализация на основе данных этих контактов будут потеряны. Если у этих контактов есть ответы, которые учитываются в квотах опроса, количество по квотам будет уменьшено, но лимиты квот останутся без изменений.}}",
|
||||||
"displays": "Показы",
|
|
||||||
"edit_attribute": "Редактировать атрибут",
|
"edit_attribute": "Редактировать атрибут",
|
||||||
"edit_attribute_description": "Обновите метку и описание для этого атрибута.",
|
"edit_attribute_description": "Обновите метку и описание для этого атрибута.",
|
||||||
"edit_attribute_values": "Редактировать атрибуты",
|
"edit_attribute_values": "Редактировать атрибуты",
|
||||||
"edit_attribute_values_description": "Измените значения определённых атрибутов для этого контакта.",
|
"edit_attribute_values_description": "Измените значения определённых атрибутов для этого контакта.",
|
||||||
"edit_attributes": "Редактировать атрибуты",
|
|
||||||
"edit_attributes_success": "Атрибуты контакта успешно обновлены",
|
"edit_attributes_success": "Атрибуты контакта успешно обновлены",
|
||||||
"generate_personal_link": "Сгенерировать персональную ссылку",
|
"generate_personal_link": "Сгенерировать персональную ссылку",
|
||||||
"generate_personal_link_description": "Выберите опубликованный опрос, чтобы сгенерировать персональную ссылку для этого контакта.",
|
"generate_personal_link_description": "Выберите опубликованный опрос, чтобы сгенерировать персональную ссылку для этого контакта.",
|
||||||
"invalid_csv_column_names": "Недопустимые имена столбцов в CSV: {columns}. Имена столбцов, которые станут новыми атрибутами, должны содержать только строчные буквы, цифры и подчёркивания, а также начинаться с буквы.",
|
|
||||||
"invalid_date_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": "Ответы не найдены",
|
||||||
"not_provided": "Не указано",
|
"not_provided": "Не указано",
|
||||||
"number_value_required": "Требуется числовое значение. Используй кнопку удаления, чтобы убрать этот атрибут.",
|
|
||||||
"personal_link_generated": "Персональная ссылка успешно сгенерирована",
|
"personal_link_generated": "Персональная ссылка успешно сгенерирована",
|
||||||
"personal_link_generated_but_clipboard_failed": "Персональная ссылка сгенерирована, но не удалось скопировать в буфер обмена: {url}",
|
"personal_link_generated_but_clipboard_failed": "Персональная ссылка сгенерирована, но не удалось скопировать в буфер обмена: {url}",
|
||||||
"personal_survey_link": "Персональная ссылка на опрос",
|
"personal_survey_link": "Персональная ссылка на опрос",
|
||||||
@@ -683,24 +656,13 @@
|
|||||||
"search_contact": "Поиск контакта",
|
"search_contact": "Поиск контакта",
|
||||||
"select_a_survey": "Выберите опрос",
|
"select_a_survey": "Выберите опрос",
|
||||||
"select_attribute": "Выберите атрибут",
|
"select_attribute": "Выберите атрибут",
|
||||||
"select_attribute_key": "Выберите ключ атрибута",
|
|
||||||
"survey_viewed": "Опрос просмотрен",
|
|
||||||
"survey_viewed_at": "Просмотрено",
|
|
||||||
"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_duplicate_mappings": "Обнаружены дублирующиеся сопоставления для следующих атрибутов: {attributes}",
|
|
||||||
"upload_contacts_error_file_too_large": "Размер файла превышает максимальный лимит 800 КБ",
|
|
||||||
"upload_contacts_error_generic": "Произошла ошибка при загрузке контактов. Пожалуйста, попробуй ещё раз позже.",
|
|
||||||
"upload_contacts_error_invalid_file_type": "Пожалуйста, загрузи файл в формате CSV",
|
|
||||||
"upload_contacts_error_no_valid_contacts": "Загруженный CSV-файл не содержит ни одного корректного контакта. Ознакомься с примером CSV-файла для правильного формата.",
|
|
||||||
"upload_contacts_modal_attribute_header": "Атрибут Formbricks",
|
|
||||||
"upload_contacts_modal_attributes_description": "Сопоставьте столбцы в вашем CSV с атрибутами в Formbricks.",
|
"upload_contacts_modal_attributes_description": "Сопоставьте столбцы в вашем CSV с атрибутами в Formbricks.",
|
||||||
"upload_contacts_modal_attributes_new": "Новый атрибут",
|
"upload_contacts_modal_attributes_new": "Новый атрибут",
|
||||||
"upload_contacts_modal_attributes_search_or_add": "Найти или добавить атрибут",
|
"upload_contacts_modal_attributes_search_or_add": "Найти или добавить атрибут",
|
||||||
|
"upload_contacts_modal_attributes_should_be_mapped_to": "должен быть сопоставлен с",
|
||||||
"upload_contacts_modal_attributes_title": "Атрибуты",
|
"upload_contacts_modal_attributes_title": "Атрибуты",
|
||||||
"upload_contacts_modal_csv_column_header": "Столбец CSV",
|
|
||||||
"upload_contacts_modal_description": "Загрузите CSV, чтобы быстро импортировать контакты с атрибутами",
|
"upload_contacts_modal_description": "Загрузите CSV, чтобы быстро импортировать контакты с атрибутами",
|
||||||
"upload_contacts_modal_download_example_csv": "Скачать пример CSV",
|
"upload_contacts_modal_download_example_csv": "Скачать пример CSV",
|
||||||
"upload_contacts_modal_duplicates_description": "Как поступить, если контакт уже существует в вашей базе?",
|
"upload_contacts_modal_duplicates_description": "Как поступить, если контакт уже существует в вашей базе?",
|
||||||
@@ -757,12 +719,7 @@
|
|||||||
"link_google_sheet": "Связать с Google Sheet",
|
"link_google_sheet": "Связать с Google Sheet",
|
||||||
"link_new_sheet": "Связать с новой таблицей",
|
"link_new_sheet": "Связать с новой таблицей",
|
||||||
"no_integrations_yet": "Ваши интеграции с Google Sheet появятся здесь, как только вы их добавите. ⏲️",
|
"no_integrations_yet": "Ваши интеграции с Google Sheet появятся здесь, как только вы их добавите. ⏲️",
|
||||||
"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": "Включить скрытые поля",
|
||||||
@@ -886,40 +843,6 @@
|
|||||||
"no_attributes_yet": "Пока нет атрибутов!",
|
"no_attributes_yet": "Пока нет атрибутов!",
|
||||||
"no_filters_yet": "Пока нет фильтров!",
|
"no_filters_yet": "Пока нет фильтров!",
|
||||||
"no_segments_yet": "У вас пока нет сохранённых сегментов.",
|
"no_segments_yet": "У вас пока нет сохранённых сегментов.",
|
||||||
"operator_contains": "содержит",
|
|
||||||
"operator_does_not_contain": "не содержит",
|
|
||||||
"operator_ends_with": "оканчивается на",
|
|
||||||
"operator_is_after": "после",
|
|
||||||
"operator_is_before": "до",
|
|
||||||
"operator_is_between": "находится между",
|
|
||||||
"operator_is_newer_than": "новее чем",
|
|
||||||
"operator_is_not_set": "не задано",
|
|
||||||
"operator_is_older_than": "старше чем",
|
|
||||||
"operator_is_same_day": "в тот же день",
|
|
||||||
"operator_is_set": "задано",
|
|
||||||
"operator_starts_with": "начинается с",
|
|
||||||
"operator_title_contains": "Содержит",
|
|
||||||
"operator_title_does_not_contain": "Не содержит",
|
|
||||||
"operator_title_ends_with": "Оканчивается на",
|
|
||||||
"operator_title_equals": "Равно",
|
|
||||||
"operator_title_greater_equal": "Больше или равно",
|
|
||||||
"operator_title_greater_than": "Больше чем",
|
|
||||||
"operator_title_is_after": "После",
|
|
||||||
"operator_title_is_before": "До",
|
|
||||||
"operator_title_is_between": "Находится между",
|
|
||||||
"operator_title_is_newer_than": "Новее чем",
|
|
||||||
"operator_title_is_not_set": "Не задано",
|
|
||||||
"operator_title_is_older_than": "Старше чем",
|
|
||||||
"operator_title_is_same_day": "В тот же день",
|
|
||||||
"operator_title_is_set": "Задано",
|
|
||||||
"operator_title_less_equal": "Меньше или равно",
|
|
||||||
"operator_title_less_than": "Меньше чем",
|
|
||||||
"operator_title_not_equals": "Не равно",
|
|
||||||
"operator_title_starts_with": "Начинается с",
|
|
||||||
"operator_title_user_is_in": "Пользователь входит в",
|
|
||||||
"operator_title_user_is_not_in": "Пользователь не входит в",
|
|
||||||
"operator_user_is_in": "Пользователь входит в",
|
|
||||||
"operator_user_is_not_in": "Пользователь не входит в",
|
|
||||||
"person_and_attributes": "Пользователь и атрибуты",
|
"person_and_attributes": "Пользователь и атрибуты",
|
||||||
"phone": "Телефон",
|
"phone": "Телефон",
|
||||||
"please_remove_the_segment_from_these_surveys_in_order_to_delete_it": "Пожалуйста, удалите этот сегмент из указанных опросов, чтобы его удалить.",
|
"please_remove_the_segment_from_these_surveys_in_order_to_delete_it": "Пожалуйста, удалите этот сегмент из указанных опросов, чтобы его удалить.",
|
||||||
@@ -944,7 +867,6 @@
|
|||||||
"user_targeting_is_currently_only_available_when": "Таргетинг пользователей сейчас доступен только когда",
|
"user_targeting_is_currently_only_available_when": "Таргетинг пользователей сейчас доступен только когда",
|
||||||
"value_cannot_be_empty": "Значение не может быть пустым.",
|
"value_cannot_be_empty": "Значение не может быть пустым.",
|
||||||
"value_must_be_a_number": "Значение должно быть числом.",
|
"value_must_be_a_number": "Значение должно быть числом.",
|
||||||
"value_must_be_positive": "Значение должно быть положительным числом.",
|
|
||||||
"view_filters": "Просмотреть фильтры",
|
"view_filters": "Просмотреть фильтры",
|
||||||
"where": "Где",
|
"where": "Где",
|
||||||
"with_the_formbricks_sdk": "с помощью Formbricks SDK"
|
"with_the_formbricks_sdk": "с помощью Formbricks SDK"
|
||||||
@@ -1031,32 +953,19 @@
|
|||||||
"enterprise_features": "Функции для предприятий",
|
"enterprise_features": "Функции для предприятий",
|
||||||
"get_an_enterprise_license_to_get_access_to_all_features": "Получите корпоративную лицензию для доступа ко всем функциям.",
|
"get_an_enterprise_license_to_get_access_to_all_features": "Получите корпоративную лицензию для доступа ко всем функциям.",
|
||||||
"keep_full_control_over_your_data_privacy_and_security": "Полный контроль над конфиденциальностью и безопасностью ваших данных.",
|
"keep_full_control_over_your_data_privacy_and_security": "Полный контроль над конфиденциальностью и безопасностью ваших данных.",
|
||||||
"license_invalid_description": "Ключ лицензии в переменной окружения ENTERPRISE_LICENSE_KEY недействителен. Проверь, нет ли опечаток, или запроси новый ключ.",
|
|
||||||
"license_status": "Статус лицензии",
|
|
||||||
"license_status_active": "Активна",
|
|
||||||
"license_status_description": "Статус вашей корпоративной лицензии.",
|
|
||||||
"license_status_expired": "Срок действия истёк",
|
|
||||||
"license_status_invalid": "Недействительная лицензия",
|
|
||||||
"license_status_unreachable": "Недоступна",
|
|
||||||
"license_unreachable_grace_period": "Не удаётся подключиться к серверу лицензий. Корпоративные функции останутся активными в течение 3-дневного льготного периода, который закончится {gracePeriodEnd}.",
|
|
||||||
"no_call_needed_no_strings_attached_request_a_free_30_day_trial_license_to_test_all_features_by_filling_out_this_form": "Без звонков и обязательств: запросите бесплатную 30-дневную пробную лицензию для тестирования всех функций, заполнив эту форму:",
|
"no_call_needed_no_strings_attached_request_a_free_30_day_trial_license_to_test_all_features_by_filling_out_this_form": "Без звонков и обязательств: запросите бесплатную 30-дневную пробную лицензию для тестирования всех функций, заполнив эту форму:",
|
||||||
"no_credit_card_no_sales_call_just_test_it": "Без кредитной карты. Без звонков от отдела продаж. Просто попробуйте :)",
|
"no_credit_card_no_sales_call_just_test_it": "Без кредитной карты. Без звонков от отдела продаж. Просто попробуйте :)",
|
||||||
"on_request": "По запросу",
|
"on_request": "По запросу",
|
||||||
"organization_roles": "Роли в организации (администратор, редактор, разработчик и др.)",
|
"organization_roles": "Роли в организации (администратор, редактор, разработчик и др.)",
|
||||||
"questions_please_reach_out_to": "Вопросы? Свяжитесь с",
|
"questions_please_reach_out_to": "Вопросы? Свяжитесь с",
|
||||||
"recheck_license": "Проверить лицензию ещё раз",
|
|
||||||
"recheck_license_failed": "Не удалось проверить лицензию. Сервер лицензий может быть недоступен.",
|
|
||||||
"recheck_license_invalid": "Ключ лицензии недействителен. Пожалуйста, проверь свою переменную ENTERPRISE_LICENSE_KEY.",
|
|
||||||
"recheck_license_success": "Проверка лицензии прошла успешно",
|
|
||||||
"recheck_license_unreachable": "Сервер лицензий недоступен. Пожалуйста, попробуй позже.",
|
|
||||||
"rechecking": "Проверка...",
|
|
||||||
"request_30_day_trial_license": "Запросить 30-дневную пробную лицензию",
|
"request_30_day_trial_license": "Запросить 30-дневную пробную лицензию",
|
||||||
"saml_sso": "SAML SSO",
|
"saml_sso": "SAML SSO",
|
||||||
"service_level_agreement": "Соглашение об уровне обслуживания (SLA)",
|
"service_level_agreement": "Соглашение об уровне обслуживания (SLA)",
|
||||||
"soc2_hipaa_iso_27001_compliance_check": "Проверка соответствия SOC2, HIPAA, ISO 27001",
|
"soc2_hipaa_iso_27001_compliance_check": "Проверка соответствия SOC2, HIPAA, ISO 27001",
|
||||||
"sso": "SSO (Google, Microsoft, OpenID Connect)",
|
"sso": "SSO (Google, Microsoft, OpenID Connect)",
|
||||||
"teams": "Команды и роли доступа (чтение, чтение и запись, управление)",
|
"teams": "Команды и роли доступа (чтение, чтение и запись, управление)",
|
||||||
"unlock_the_full_power_of_formbricks_free_for_30_days": "Откройте все возможности Formbricks. Бесплатно на 30 дней."
|
"unlock_the_full_power_of_formbricks_free_for_30_days": "Откройте все возможности Formbricks. Бесплатно на 30 дней.",
|
||||||
|
"your_enterprise_license_is_active_all_features_unlocked": "Ваша корпоративная лицензия активна. Все функции разблокированы."
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"bulk_invite_warning_description": "В бесплатном тарифе всем участникам организации всегда назначается роль \"Владелец\".",
|
"bulk_invite_warning_description": "В бесплатном тарифе всем участникам организации всегда назначается роль \"Владелец\".",
|
||||||
@@ -1080,7 +989,7 @@
|
|||||||
"from_your_organization": "из вашей организации",
|
"from_your_organization": "из вашей организации",
|
||||||
"invitation_sent_once_more": "Приглашение отправлено ещё раз.",
|
"invitation_sent_once_more": "Приглашение отправлено ещё раз.",
|
||||||
"invite_deleted_successfully": "Приглашение успешно удалено",
|
"invite_deleted_successfully": "Приглашение успешно удалено",
|
||||||
"invite_expires_on": "Приглашение истекает {date}",
|
"invited_on": "Приглашён {date}",
|
||||||
"invites_failed": "Не удалось отправить приглашения",
|
"invites_failed": "Не удалось отправить приглашения",
|
||||||
"leave_organization": "Покинуть организацию",
|
"leave_organization": "Покинуть организацию",
|
||||||
"leave_organization_description": "Вы покинете эту организацию и потеряете доступ ко всем опросам и ответам. Вы сможете вернуться только по новому приглашению.",
|
"leave_organization_description": "Вы покинете эту организацию и потеряете доступ ко всем опросам и ответам. Вы сможете вернуться только по новому приглашению.",
|
||||||
@@ -1193,6 +1102,8 @@
|
|||||||
"please_fill_all_workspace_fields": "Пожалуйста, заполните все поля для добавления нового рабочего пространства.",
|
"please_fill_all_workspace_fields": "Пожалуйста, заполните все поля для добавления нового рабочего пространства.",
|
||||||
"read": "Чтение",
|
"read": "Чтение",
|
||||||
"read_write": "Чтение и запись",
|
"read_write": "Чтение и запись",
|
||||||
|
"select_member": "Выберите участника",
|
||||||
|
"select_workspace": "Выберите рабочее пространство",
|
||||||
"team_admin": "Администратор команды",
|
"team_admin": "Администратор команды",
|
||||||
"team_created_successfully": "Команда успешно создана.",
|
"team_created_successfully": "Команда успешно создана.",
|
||||||
"team_deleted_successfully": "Команда успешно удалена.",
|
"team_deleted_successfully": "Команда успешно удалена.",
|
||||||
@@ -1242,6 +1153,7 @@
|
|||||||
"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": "Добавить вариант",
|
||||||
@@ -1280,7 +1192,6 @@
|
|||||||
"block_duplicated": "Блокировать дубликаты.",
|
"block_duplicated": "Блокировать дубликаты.",
|
||||||
"bold": "Жирный",
|
"bold": "Жирный",
|
||||||
"brand_color": "Фирменный цвет",
|
"brand_color": "Фирменный цвет",
|
||||||
"brand_color_description": "Применяется к кнопкам, ссылкам и выделениям.",
|
|
||||||
"brightness": "Яркость",
|
"brightness": "Яркость",
|
||||||
"bulk_edit": "Массовое редактирование",
|
"bulk_edit": "Массовое редактирование",
|
||||||
"bulk_edit_description": "Отредактируйте все варианты ниже, по одному на строку. Пустые строки будут пропущены, а дубликаты удалены.",
|
"bulk_edit_description": "Отредактируйте все варианты ниже, по одному на строку. Пустые строки будут пропущены, а дубликаты удалены.",
|
||||||
@@ -1298,9 +1209,7 @@
|
|||||||
"capture_new_action": "Захватить новое действие",
|
"capture_new_action": "Захватить новое действие",
|
||||||
"card_arrangement_for_survey_type_derived": "Расположение карточек для опросов типа {surveyTypeDerived}",
|
"card_arrangement_for_survey_type_derived": "Расположение карточек для опросов типа {surveyTypeDerived}",
|
||||||
"card_background_color": "Цвет фона карточки",
|
"card_background_color": "Цвет фона карточки",
|
||||||
"card_background_color_description": "Заполняет область карточки опроса.",
|
|
||||||
"card_border_color": "Цвет рамки карточки",
|
"card_border_color": "Цвет рамки карточки",
|
||||||
"card_border_color_description": "Обводит карточку опроса.",
|
|
||||||
"card_styling": "Оформление карточки",
|
"card_styling": "Оформление карточки",
|
||||||
"casual": "Неформальный",
|
"casual": "Неформальный",
|
||||||
"caution_edit_duplicate": "Дублировать и редактировать",
|
"caution_edit_duplicate": "Дублировать и редактировать",
|
||||||
@@ -1311,12 +1220,20 @@
|
|||||||
"caution_explanation_responses_are_safe": "Старые и новые ответы смешиваются, что может привести к искажённым итоговым данным.",
|
"caution_explanation_responses_are_safe": "Старые и новые ответы смешиваются, что может привести к искажённым итоговым данным.",
|
||||||
"caution_recommendation": "Это может привести к несоответствиям в итогах опроса. Рекомендуем вместо этого дублировать опрос.",
|
"caution_recommendation": "Это может привести к несоответствиям в итогах опроса. Рекомендуем вместо этого дублировать опрос.",
|
||||||
"caution_text": "Изменения приведут к несоответствиям",
|
"caution_text": "Изменения приведут к несоответствиям",
|
||||||
|
"centered_modal_overlay_color": "Цвет оверлея центрированного модального окна",
|
||||||
"change_anyway": "Всё равно изменить",
|
"change_anyway": "Всё равно изменить",
|
||||||
"change_background": "Изменить фон",
|
"change_background": "Изменить фон",
|
||||||
"change_question_type": "Изменить тип вопроса",
|
"change_question_type": "Изменить тип вопроса",
|
||||||
"change_survey_type": "Смена типа опроса влияет на существующий доступ",
|
"change_survey_type": "Смена типа опроса влияет на существующий доступ",
|
||||||
|
"change_the_background_color_of_the_card": "Изменить цвет фона карточки.",
|
||||||
|
"change_the_background_color_of_the_input_fields": "Изменить цвет фона полей ввода.",
|
||||||
"change_the_background_to_a_color_image_or_animation": "Изменить фон на цвет, изображение или анимацию.",
|
"change_the_background_to_a_color_image_or_animation": "Изменить фон на цвет, изображение или анимацию.",
|
||||||
|
"change_the_border_color_of_the_card": "Изменить цвет рамки карточки.",
|
||||||
|
"change_the_border_color_of_the_input_fields": "Изменить цвет рамки полей ввода.",
|
||||||
|
"change_the_border_radius_of_the_card_and_the_inputs": "Изменить скругление углов карточки и полей ввода.",
|
||||||
|
"change_the_brand_color_of_the_survey": "Изменить фирменный цвет опроса.",
|
||||||
"change_the_placement_of_this_survey": "Изменить размещение этого опроса.",
|
"change_the_placement_of_this_survey": "Изменить размещение этого опроса.",
|
||||||
|
"change_the_question_color_of_the_survey": "Изменить цвет вопросов в опросе.",
|
||||||
"changes_saved": "Изменения сохранены.",
|
"changes_saved": "Изменения сохранены.",
|
||||||
"changing_survey_type_will_remove_existing_distribution_channels": "Изменение типа опроса повлияет на способы его распространения. Если у респондентов уже есть ссылки для доступа к текущему типу, после смены они могут потерять доступ.",
|
"changing_survey_type_will_remove_existing_distribution_channels": "Изменение типа опроса повлияет на способы его распространения. Если у респондентов уже есть ссылки для доступа к текущему типу, после смены они могут потерять доступ.",
|
||||||
"checkbox_label": "Метка флажка",
|
"checkbox_label": "Метка флажка",
|
||||||
@@ -1456,6 +1373,7 @@
|
|||||||
"hide_progress_bar": "Скрыть индикатор прогресса",
|
"hide_progress_bar": "Скрыть индикатор прогресса",
|
||||||
"hide_question_settings": "Скрыть настройки вопроса",
|
"hide_question_settings": "Скрыть настройки вопроса",
|
||||||
"hostname": "Имя хоста",
|
"hostname": "Имя хоста",
|
||||||
|
"how_funky_do_you_want_your_cards_in_survey_type_derived_surveys": "Насколько необычными вы хотите сделать карточки в опросах типа {surveyTypeDerived}",
|
||||||
"if_you_need_more_please": "Если вам нужно больше, пожалуйста",
|
"if_you_need_more_please": "Если вам нужно больше, пожалуйста",
|
||||||
"if_you_really_want_that_answer_ask_until_you_get_it": "Показывать каждый раз при срабатывании, пока не будет получен ответ.",
|
"if_you_really_want_that_answer_ask_until_you_get_it": "Показывать каждый раз при срабатывании, пока не будет получен ответ.",
|
||||||
"ignore_global_waiting_time": "Игнорировать период ожидания",
|
"ignore_global_waiting_time": "Игнорировать период ожидания",
|
||||||
@@ -1466,9 +1384,7 @@
|
|||||||
"initial_value": "Начальное значение",
|
"initial_value": "Начальное значение",
|
||||||
"inner_text": "Внутренний текст",
|
"inner_text": "Внутренний текст",
|
||||||
"input_border_color": "Цвет рамки поля ввода",
|
"input_border_color": "Цвет рамки поля ввода",
|
||||||
"input_border_color_description": "Обводит текстовые поля и текстовые области.",
|
|
||||||
"input_color": "Цвет поля ввода",
|
"input_color": "Цвет поля ввода",
|
||||||
"input_color_description": "Заполняет внутреннюю часть текстовых полей.",
|
|
||||||
"insert_link": "Вставить ссылку",
|
"insert_link": "Вставить ссылку",
|
||||||
"invalid_targeting": "Некорректный таргетинг: проверьте фильтры аудитории",
|
"invalid_targeting": "Некорректный таргетинг: проверьте фильтры аудитории",
|
||||||
"invalid_video_url_warning": "Пожалуйста, введите корректную ссылку на YouTube, Vimeo или Loom. В настоящее время другие видеохостинги не поддерживаются.",
|
"invalid_video_url_warning": "Пожалуйста, введите корректную ссылку на YouTube, Vimeo или Loom. В настоящее время другие видеохостинги не поддерживаются.",
|
||||||
@@ -1552,12 +1468,13 @@
|
|||||||
"protect_survey_with_pin_description": "Только пользователи, у которых есть PIN-код, могут получить доступ к опросу.",
|
"protect_survey_with_pin_description": "Только пользователи, у которых есть PIN-код, могут получить доступ к опросу.",
|
||||||
"publish": "Опубликовать",
|
"publish": "Опубликовать",
|
||||||
"question": "Вопрос",
|
"question": "Вопрос",
|
||||||
|
"question_color": "Цвет вопроса",
|
||||||
"question_deleted": "Вопрос удалён.",
|
"question_deleted": "Вопрос удалён.",
|
||||||
"question_duplicated": "Вопрос дублирован.",
|
"question_duplicated": "Вопрос дублирован.",
|
||||||
"question_id_updated": "ID вопроса обновлён",
|
"question_id_updated": "ID вопроса обновлён",
|
||||||
"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}\"",
|
||||||
"question_used_in_recall": "Этот вопрос используется в отзыве в вопросе {questionIndex}.",
|
"question_used_in_recall": "Этот вопрос используется в отзыве в вопросе {questionIndex}.",
|
||||||
"question_used_in_recall_ending_card": "Этот вопрос используется в отзыве на финальной карточке",
|
"question_used_in_recall_ending_card": "Этот вопрос используется в отзыве на финальной карточке",
|
||||||
"quotas": {
|
"quotas": {
|
||||||
@@ -1613,7 +1530,6 @@
|
|||||||
"response_limits_redirections_and_more": "Лимиты ответов, перенаправления и другое.",
|
"response_limits_redirections_and_more": "Лимиты ответов, перенаправления и другое.",
|
||||||
"response_options": "Параметры ответа",
|
"response_options": "Параметры ответа",
|
||||||
"roundness": "Скругление",
|
"roundness": "Скругление",
|
||||||
"roundness_description": "Определяет степень скругления углов карточки.",
|
|
||||||
"row_used_in_logic_error": "Эта строка используется в логике вопроса {questionIndex}. Пожалуйста, сначала удалите её из логики.",
|
"row_used_in_logic_error": "Эта строка используется в логике вопроса {questionIndex}. Пожалуйста, сначала удалите её из логики.",
|
||||||
"rows": "Строки",
|
"rows": "Строки",
|
||||||
"save_and_close": "Сохранить и закрыть",
|
"save_and_close": "Сохранить и закрыть",
|
||||||
@@ -1655,6 +1571,7 @@
|
|||||||
"styling_set_to_theme_styles": "Оформление установлено в соответствии с темой",
|
"styling_set_to_theme_styles": "Оформление установлено в соответствии с темой",
|
||||||
"subheading": "Подзаголовок",
|
"subheading": "Подзаголовок",
|
||||||
"subtract": "Вычесть -",
|
"subtract": "Вычесть -",
|
||||||
|
"suggest_colors": "Предложить цвета",
|
||||||
"survey_completed_heading": "Опрос завершён",
|
"survey_completed_heading": "Опрос завершён",
|
||||||
"survey_completed_subheading": "Этот бесплатный и открытый опрос был закрыт",
|
"survey_completed_subheading": "Этот бесплатный и открытый опрос был закрыт",
|
||||||
"survey_display_settings": "Настройки отображения опроса",
|
"survey_display_settings": "Настройки отображения опроса",
|
||||||
@@ -1731,7 +1648,7 @@
|
|||||||
"validation_rules": "Правила валидации",
|
"validation_rules": "Правила валидации",
|
||||||
"validation_rules_description": "Принимать только ответы, соответствующие следующим критериям",
|
"validation_rules_description": "Принимать только ответы, соответствующие следующим критериям",
|
||||||
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} используется в логике вопроса {questionIndex}. Пожалуйста, сначала удалите его из логики.",
|
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} используется в логике вопроса {questionIndex}. Пожалуйста, сначала удалите его из логики.",
|
||||||
"variable_is_used_in_quota_please_remove_it_from_quota_first": "Переменная «{variableName}» используется в квоте «{quotaName}». Сначала удалите её из квоты.",
|
"variable_is_used_in_quota_please_remove_it_from_quota_first": "Переменная «{variableName}» используется в квоте «{quotaName}»",
|
||||||
"variable_name_conflicts_with_hidden_field": "Имя переменной конфликтует с существующим ID скрытого поля.",
|
"variable_name_conflicts_with_hidden_field": "Имя переменной конфликтует с существующим ID скрытого поля.",
|
||||||
"variable_name_is_already_taken_please_choose_another": "Это имя переменной уже занято, выберите другое.",
|
"variable_name_is_already_taken_please_choose_another": "Это имя переменной уже занято, выберите другое.",
|
||||||
"variable_name_must_start_with_a_letter": "Имя переменной должно начинаться с буквы.",
|
"variable_name_must_start_with_a_letter": "Имя переменной должно начинаться с буквы.",
|
||||||
@@ -1957,7 +1874,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": "Опрос будет показан пользователям вашего сайта, которые соответствуют указанным ниже критериям",
|
||||||
@@ -2000,7 +1916,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": "В целом",
|
||||||
@@ -2140,71 +2055,9 @@
|
|||||||
"look": {
|
"look": {
|
||||||
"add_background_color": "Добавить цвет фона",
|
"add_background_color": "Добавить цвет фона",
|
||||||
"add_background_color_description": "Добавьте цвет фона для контейнера с логотипом.",
|
"add_background_color_description": "Добавьте цвет фона для контейнера с логотипом.",
|
||||||
"advanced_styling_field_border_radius": "Радиус скругления",
|
|
||||||
"advanced_styling_field_button_bg": "Фон кнопки",
|
|
||||||
"advanced_styling_field_button_bg_description": "Заполняет кнопку «Далее» / «Отправить».",
|
|
||||||
"advanced_styling_field_button_border_radius_description": "Скругляет углы кнопки.",
|
|
||||||
"advanced_styling_field_button_font_size_description": "Масштабирует текст на кнопке.",
|
|
||||||
"advanced_styling_field_button_font_weight_description": "Делает текст на кнопке тоньше или жирнее.",
|
|
||||||
"advanced_styling_field_button_height_description": "Определяет высоту кнопки.",
|
|
||||||
"advanced_styling_field_button_padding_x_description": "Добавляет отступы слева и справа.",
|
|
||||||
"advanced_styling_field_button_padding_y_description": "Добавляет отступы сверху и снизу.",
|
|
||||||
"advanced_styling_field_button_text": "Текст кнопки",
|
|
||||||
"advanced_styling_field_button_text_description": "Задаёт цвет текста на кнопках.",
|
|
||||||
"advanced_styling_field_description_color": "Цвет описания",
|
|
||||||
"advanced_styling_field_description_color_description": "Задаёт цвет текста под каждым заголовком.",
|
|
||||||
"advanced_styling_field_description_size": "Размер шрифта описания",
|
|
||||||
"advanced_styling_field_description_size_description": "Масштабирует текст описания.",
|
|
||||||
"advanced_styling_field_description_weight": "Толщина шрифта описания",
|
|
||||||
"advanced_styling_field_description_weight_description": "Делает текст описания тоньше или жирнее.",
|
|
||||||
"advanced_styling_field_font_size": "Размер шрифта",
|
|
||||||
"advanced_styling_field_font_weight": "Толщина шрифта",
|
|
||||||
"advanced_styling_field_headline_color": "Цвет заголовка",
|
|
||||||
"advanced_styling_field_headline_color_description": "Задаёт цвет основного текста вопроса.",
|
|
||||||
"advanced_styling_field_headline_size": "Размер шрифта заголовка",
|
|
||||||
"advanced_styling_field_headline_size_description": "Масштабирует текст заголовка.",
|
|
||||||
"advanced_styling_field_headline_weight": "Толщина шрифта заголовка",
|
|
||||||
"advanced_styling_field_headline_weight_description": "Делает текст заголовка тоньше или жирнее.",
|
|
||||||
"advanced_styling_field_height": "Минимальная высота",
|
|
||||||
"advanced_styling_field_indicator_bg": "Фон индикатора",
|
|
||||||
"advanced_styling_field_indicator_bg_description": "Задаёт цвет заполненной части полосы.",
|
|
||||||
"advanced_styling_field_input_border_radius_description": "Скругляет углы полей ввода.",
|
|
||||||
"advanced_styling_field_input_font_size_description": "Масштабирует введённый текст в полях ввода.",
|
|
||||||
"advanced_styling_field_input_height_description": "Определяет минимальную высоту поля ввода.",
|
|
||||||
"advanced_styling_field_input_padding_x_description": "Добавляет отступы слева и справа.",
|
|
||||||
"advanced_styling_field_input_padding_y_description": "Добавляет пространство сверху и снизу.",
|
|
||||||
"advanced_styling_field_input_placeholder_opacity_description": "Делает текст подсказки менее заметным.",
|
|
||||||
"advanced_styling_field_input_shadow_description": "Добавляет тень вокруг полей ввода.",
|
|
||||||
"advanced_styling_field_input_text": "Текст ввода",
|
|
||||||
"advanced_styling_field_input_text_description": "Задаёт цвет введённого текста в полях.",
|
|
||||||
"advanced_styling_field_option_bg": "Фон",
|
|
||||||
"advanced_styling_field_option_bg_description": "Заливает фон элементов опций.",
|
|
||||||
"advanced_styling_field_option_border_radius_description": "Скругляет углы опций.",
|
|
||||||
"advanced_styling_field_option_font_size_description": "Изменяет размер текста метки опции.",
|
|
||||||
"advanced_styling_field_option_label": "Цвет метки",
|
|
||||||
"advanced_styling_field_option_label_description": "Задаёт цвет текста метки опции.",
|
|
||||||
"advanced_styling_field_option_padding_x_description": "Добавляет пространство слева и справа.",
|
|
||||||
"advanced_styling_field_option_padding_y_description": "Добавляет пространство сверху и снизу.",
|
|
||||||
"advanced_styling_field_padding_x": "Внутренний отступ по X",
|
|
||||||
"advanced_styling_field_padding_y": "Внутренний отступ по Y",
|
|
||||||
"advanced_styling_field_placeholder_opacity": "Прозрачность плейсхолдера",
|
|
||||||
"advanced_styling_field_shadow": "Тень",
|
|
||||||
"advanced_styling_field_track_bg": "Фон трека",
|
|
||||||
"advanced_styling_field_track_bg_description": "Задаёт цвет незаполненной части полосы.",
|
|
||||||
"advanced_styling_field_track_height": "Высота трека",
|
|
||||||
"advanced_styling_field_track_height_description": "Управляет толщиной индикатора прогресса.",
|
|
||||||
"advanced_styling_field_upper_label_color": "Цвет метки заголовка",
|
|
||||||
"advanced_styling_field_upper_label_color_description": "Задаёт цвет маленькой метки над полями ввода.",
|
|
||||||
"advanced_styling_field_upper_label_size": "Размер шрифта метки заголовка",
|
|
||||||
"advanced_styling_field_upper_label_size_description": "Изменяет размер маленькой метки над полями ввода.",
|
|
||||||
"advanced_styling_field_upper_label_weight": "Толщина шрифта метки заголовка",
|
|
||||||
"advanced_styling_field_upper_label_weight_description": "Делает метку тоньше или жирнее.",
|
|
||||||
"advanced_styling_section_buttons": "Кнопки",
|
|
||||||
"advanced_styling_section_headlines": "Заголовки и описания",
|
|
||||||
"advanced_styling_section_inputs": "Поля ввода",
|
|
||||||
"advanced_styling_section_options": "Опции (радио/чекбокс)",
|
|
||||||
"app_survey_placement": "Размещение опроса в приложении",
|
"app_survey_placement": "Размещение опроса в приложении",
|
||||||
"app_survey_placement_settings_description": "Измените, где будут отображаться опросы в вашем веб-приложении или на сайте.",
|
"app_survey_placement_settings_description": "Измените, где будут отображаться опросы в вашем веб-приложении или на сайте.",
|
||||||
|
"centered_modal_overlay_color": "Цвет оверлея центрированного модального окна",
|
||||||
"email_customization": "Настройка email",
|
"email_customization": "Настройка email",
|
||||||
"email_customization_description": "Измените внешний вид писем, которые Formbricks отправляет от вашего имени.",
|
"email_customization_description": "Измените внешний вид писем, которые Formbricks отправляет от вашего имени.",
|
||||||
"enable_custom_styling": "Включить пользовательское оформление",
|
"enable_custom_styling": "Включить пользовательское оформление",
|
||||||
@@ -2215,9 +2068,6 @@
|
|||||||
"formbricks_branding_hidden": "Брендинг Formbricks скрыт.",
|
"formbricks_branding_hidden": "Брендинг Formbricks скрыт.",
|
||||||
"formbricks_branding_settings_description": "Мы ценим вашу поддержку, но понимаем, если вы захотите отключить это.",
|
"formbricks_branding_settings_description": "Мы ценим вашу поддержку, но понимаем, если вы захотите отключить это.",
|
||||||
"formbricks_branding_shown": "Брендинг Formbricks отображается.",
|
"formbricks_branding_shown": "Брендинг Formbricks отображается.",
|
||||||
"generate_theme_btn": "Сгенерировать",
|
|
||||||
"generate_theme_confirmation": "Сгенерировать подходящую цветовую тему на основе цвета твоего бренда? Это перезапишет текущие цветовые настройки.",
|
|
||||||
"generate_theme_header": "Сгенерировать цветовую тему?",
|
|
||||||
"logo_removed_successfully": "Логотип успешно удалён",
|
"logo_removed_successfully": "Логотип успешно удалён",
|
||||||
"logo_settings_description": "Загрузите логотип вашей компании для брендирования опросов и предпросмотра ссылок.",
|
"logo_settings_description": "Загрузите логотип вашей компании для брендирования опросов и предпросмотра ссылок.",
|
||||||
"logo_updated_successfully": "Логотип успешно обновлён",
|
"logo_updated_successfully": "Логотип успешно обновлён",
|
||||||
@@ -2232,8 +2082,6 @@
|
|||||||
"show_formbricks_branding_in": "Показывать брендинг Formbricks в опросах типа {type}",
|
"show_formbricks_branding_in": "Показывать брендинг Formbricks в опросах типа {type}",
|
||||||
"show_powered_by_formbricks": "Показывать подпись «Работает на Formbricks»",
|
"show_powered_by_formbricks": "Показывать подпись «Работает на Formbricks»",
|
||||||
"styling_updated_successfully": "Стили успешно обновлены",
|
"styling_updated_successfully": "Стили успешно обновлены",
|
||||||
"suggest_colors": "Предложить цвета",
|
|
||||||
"suggested_colors_applied_please_save": "Рекомендованные цвета успешно сгенерированы. Нажми «Сохранить», чтобы применить изменения.",
|
|
||||||
"theme": "Тема",
|
"theme": "Тема",
|
||||||
"theme_settings_description": "Создайте стиль для всех опросов. Вы можете включить индивидуальное оформление для каждого опроса."
|
"theme_settings_description": "Создайте стиль для всех опросов. Вы можете включить индивидуальное оформление для каждого опроса."
|
||||||
},
|
},
|
||||||
@@ -2997,7 +2845,6 @@
|
|||||||
"preview_survey_question_2_choice_1_label": "Да, держите меня в курсе.",
|
"preview_survey_question_2_choice_1_label": "Да, держите меня в курсе.",
|
||||||
"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_welcome_card_headline": "Добро пожаловать!",
|
"preview_survey_welcome_card_headline": "Добро пожаловать!",
|
||||||
"prioritize_features_description": "Определите, какие функции наиболее и наименее важны для ваших пользователей.",
|
"prioritize_features_description": "Определите, какие функции наиболее и наименее важны для ваших пользователей.",
|
||||||
"prioritize_features_name": "Приоритизация функций",
|
"prioritize_features_name": "Приоритизация функций",
|
||||||
|
|||||||
+24
-177
@@ -188,7 +188,6 @@
|
|||||||
"customer_success": "Kundframgång",
|
"customer_success": "Kundframgång",
|
||||||
"dark_overlay": "Mörkt överlägg",
|
"dark_overlay": "Mörkt överlägg",
|
||||||
"date": "Datum",
|
"date": "Datum",
|
||||||
"days": "dagar",
|
|
||||||
"default": "Standard",
|
"default": "Standard",
|
||||||
"delete": "Ta bort",
|
"delete": "Ta bort",
|
||||||
"description": "Beskrivning",
|
"description": "Beskrivning",
|
||||||
@@ -218,7 +217,6 @@
|
|||||||
"error": "Fel",
|
"error": "Fel",
|
||||||
"error_component_description": "Denna resurs finns inte eller så har du inte de nödvändiga rättigheterna för att komma åt den.",
|
"error_component_description": "Denna resurs finns inte eller så har du inte de nödvändiga rättigheterna för att komma åt den.",
|
||||||
"error_component_title": "Fel vid laddning av resurser",
|
"error_component_title": "Fel vid laddning av resurser",
|
||||||
"error_loading_data": "Fel vid inläsning av data",
|
|
||||||
"error_rate_limit_description": "Maximalt antal förfrågningar har nåtts. Försök igen senare.",
|
"error_rate_limit_description": "Maximalt antal förfrågningar har nåtts. Försök igen senare.",
|
||||||
"error_rate_limit_title": "Begränsningsgräns överskriden",
|
"error_rate_limit_title": "Begränsningsgräns överskriden",
|
||||||
"expand_rows": "Visa rader",
|
"expand_rows": "Visa rader",
|
||||||
@@ -277,7 +275,6 @@
|
|||||||
"mobile_overlay_app_works_best_on_desktop": "Formbricks fungerar bäst på en större skärm. Byt till en annan enhet för att hantera eller bygga enkäter.",
|
"mobile_overlay_app_works_best_on_desktop": "Formbricks fungerar bäst på en större skärm. Byt till en annan enhet för att hantera eller bygga enkäter.",
|
||||||
"mobile_overlay_surveys_look_good": "Oroa dig inte – dina enkäter ser bra ut på alla enheter och skärmstorlekar!",
|
"mobile_overlay_surveys_look_good": "Oroa dig inte – dina enkäter ser bra ut på alla enheter och skärmstorlekar!",
|
||||||
"mobile_overlay_title": "Hoppsan, liten skärm upptäckt!",
|
"mobile_overlay_title": "Hoppsan, liten skärm upptäckt!",
|
||||||
"months": "månader",
|
|
||||||
"move_down": "Flytta ner",
|
"move_down": "Flytta ner",
|
||||||
"move_up": "Flytta upp",
|
"move_up": "Flytta upp",
|
||||||
"multiple_languages": "Flera språk",
|
"multiple_languages": "Flera språk",
|
||||||
@@ -288,7 +285,6 @@
|
|||||||
"no_background_image_found": "Ingen bakgrundsbild hittades.",
|
"no_background_image_found": "Ingen bakgrundsbild hittades.",
|
||||||
"no_code": "Ingen kod",
|
"no_code": "Ingen kod",
|
||||||
"no_files_uploaded": "Inga filer laddades upp",
|
"no_files_uploaded": "Inga filer laddades upp",
|
||||||
"no_overlay": "Ingen overlay",
|
|
||||||
"no_quotas_found": "Inga kvoter hittades",
|
"no_quotas_found": "Inga kvoter hittades",
|
||||||
"no_result_found": "Inget resultat hittades",
|
"no_result_found": "Inget resultat hittades",
|
||||||
"no_results": "Inga resultat",
|
"no_results": "Inga resultat",
|
||||||
@@ -315,7 +311,6 @@
|
|||||||
"organization_teams_not_found": "Organisationsteam hittades inte",
|
"organization_teams_not_found": "Organisationsteam hittades inte",
|
||||||
"other": "Annat",
|
"other": "Annat",
|
||||||
"others": "Andra",
|
"others": "Andra",
|
||||||
"overlay_color": "Overlay-färg",
|
|
||||||
"overview": "Översikt",
|
"overview": "Översikt",
|
||||||
"password": "Lösenord",
|
"password": "Lösenord",
|
||||||
"paused": "Pausad",
|
"paused": "Pausad",
|
||||||
@@ -355,7 +350,6 @@
|
|||||||
"request_trial_license": "Begär provlicens",
|
"request_trial_license": "Begär provlicens",
|
||||||
"reset_to_default": "Återställ till standard",
|
"reset_to_default": "Återställ till standard",
|
||||||
"response": "Svar",
|
"response": "Svar",
|
||||||
"response_id": "Svar-ID",
|
|
||||||
"responses": "Svar",
|
"responses": "Svar",
|
||||||
"restart": "Starta om",
|
"restart": "Starta om",
|
||||||
"role": "Roll",
|
"role": "Roll",
|
||||||
@@ -396,7 +390,6 @@
|
|||||||
"status": "Status",
|
"status": "Status",
|
||||||
"step_by_step_manual": "Steg-för-steg-manual",
|
"step_by_step_manual": "Steg-för-steg-manual",
|
||||||
"storage_not_configured": "Fillagring är inte konfigurerad, uppladdningar kommer sannolikt att misslyckas",
|
"storage_not_configured": "Fillagring är inte konfigurerad, uppladdningar kommer sannolikt att misslyckas",
|
||||||
"string": "Text",
|
|
||||||
"styling": "Styling",
|
"styling": "Styling",
|
||||||
"submit": "Skicka",
|
"submit": "Skicka",
|
||||||
"summary": "Sammanfattning",
|
"summary": "Sammanfattning",
|
||||||
@@ -429,7 +422,6 @@
|
|||||||
"top_right": "Övre höger",
|
"top_right": "Övre höger",
|
||||||
"try_again": "Försök igen",
|
"try_again": "Försök igen",
|
||||||
"type": "Typ",
|
"type": "Typ",
|
||||||
"unknown_survey": "Okänd enkät",
|
|
||||||
"unlock_more_workspaces_with_a_higher_plan": "Lås upp fler arbetsytor med ett högre abonnemang.",
|
"unlock_more_workspaces_with_a_higher_plan": "Lås upp fler arbetsytor med ett högre abonnemang.",
|
||||||
"update": "Uppdatera",
|
"update": "Uppdatera",
|
||||||
"updated": "Uppdaterad",
|
"updated": "Uppdaterad",
|
||||||
@@ -453,7 +445,6 @@
|
|||||||
"website_and_app_connection": "Webbplats- och appanslutning",
|
"website_and_app_connection": "Webbplats- och appanslutning",
|
||||||
"website_app_survey": "Webbplats- och appenkät",
|
"website_app_survey": "Webbplats- och appenkät",
|
||||||
"website_survey": "Webbplatsenkät",
|
"website_survey": "Webbplatsenkät",
|
||||||
"weeks": "veckor",
|
|
||||||
"welcome_card": "Välkomstkort",
|
"welcome_card": "Välkomstkort",
|
||||||
"workspace_configuration": "Arbetsytans konfiguration",
|
"workspace_configuration": "Arbetsytans konfiguration",
|
||||||
"workspace_created_successfully": "Arbetsytan har skapats",
|
"workspace_created_successfully": "Arbetsytan har skapats",
|
||||||
@@ -464,7 +455,6 @@
|
|||||||
"workspace_not_found": "Arbetsyta hittades inte",
|
"workspace_not_found": "Arbetsyta hittades inte",
|
||||||
"workspace_permission_not_found": "Arbetsytebehörighet hittades inte",
|
"workspace_permission_not_found": "Arbetsytebehörighet hittades inte",
|
||||||
"workspaces": "Arbetsytor",
|
"workspaces": "Arbetsytor",
|
||||||
"years": "år",
|
|
||||||
"you": "Du",
|
"you": "Du",
|
||||||
"you_are_downgraded_to_the_community_edition": "Du har nedgraderats till Community Edition.",
|
"you_are_downgraded_to_the_community_edition": "Du har nedgraderats till Community Edition.",
|
||||||
"you_are_not_authorized_to_perform_this_action": "Du har inte behörighet att utföra denna åtgärd.",
|
"you_are_not_authorized_to_perform_this_action": "Du har inte behörighet att utföra denna åtgärd.",
|
||||||
@@ -625,7 +615,7 @@
|
|||||||
"attribute_created_successfully": "Attributet har skapats",
|
"attribute_created_successfully": "Attributet har skapats",
|
||||||
"attribute_description": "Beskrivning",
|
"attribute_description": "Beskrivning",
|
||||||
"attribute_description_placeholder": "Kort beskrivning",
|
"attribute_description_placeholder": "Kort beskrivning",
|
||||||
"attribute_key": "Nyckel",
|
"attribute_key": "Attribut",
|
||||||
"attribute_key_cannot_be_changed": "Nyckeln kan inte ändras efter skapande",
|
"attribute_key_cannot_be_changed": "Nyckeln kan inte ändras efter skapande",
|
||||||
"attribute_key_hint": "Endast små bokstäver, siffror och understreck. Måste börja med en bokstav.",
|
"attribute_key_hint": "Endast små bokstäver, siffror och understreck. Måste börja med en bokstav.",
|
||||||
"attribute_key_placeholder": "t.ex. date_of_birth",
|
"attribute_key_placeholder": "t.ex. date_of_birth",
|
||||||
@@ -636,45 +626,28 @@
|
|||||||
"attribute_updated_successfully": "Attributet har uppdaterats",
|
"attribute_updated_successfully": "Attributet har uppdaterats",
|
||||||
"attribute_value": "Värde",
|
"attribute_value": "Värde",
|
||||||
"attribute_value_placeholder": "Attributvärde",
|
"attribute_value_placeholder": "Attributvärde",
|
||||||
"attributes_msg_attribute_limit_exceeded": "Kunde inte skapa {count} nya attribut eftersom det skulle överskrida maxgränsen på {limit} attributklasser. Befintliga attribut uppdaterades utan problem.",
|
|
||||||
"attributes_msg_attribute_type_validation_error": "{error} (attributet '{key}' har dataTyp: {dataType})",
|
|
||||||
"attributes_msg_email_already_exists": "E-postadressen finns redan för den här miljön och uppdaterades inte.",
|
|
||||||
"attributes_msg_email_or_userid_required": "Antingen e-post eller userId krävs. De befintliga värdena behölls.",
|
|
||||||
"attributes_msg_new_attribute_created": "Nytt attribut '{key}' med typen '{dataType}' har skapats",
|
|
||||||
"attributes_msg_userid_already_exists": "UserId finns redan för den här miljön och uppdaterades inte.",
|
|
||||||
"contact_deleted_successfully": "Kontakt borttagen",
|
"contact_deleted_successfully": "Kontakt borttagen",
|
||||||
"contact_not_found": "Ingen sådan kontakt hittades",
|
"contact_not_found": "Ingen sådan kontakt hittades",
|
||||||
"contacts_table_refresh": "Uppdatera kontakter",
|
"contacts_table_refresh": "Uppdatera kontakter",
|
||||||
"contacts_table_refresh_success": "Kontakter uppdaterade",
|
"contacts_table_refresh_success": "Kontakter uppdaterade",
|
||||||
"create_attribute": "Skapa attribut",
|
"create_attribute": "Skapa attribut",
|
||||||
|
"create_key": "Skapa nyckel",
|
||||||
"create_new_attribute": "Skapa nytt attribut",
|
"create_new_attribute": "Skapa nytt attribut",
|
||||||
"create_new_attribute_description": "Skapa ett nytt attribut för segmenteringsändamål.",
|
"create_new_attribute_description": "Skapa ett nytt attribut för segmenteringsändamål.",
|
||||||
"custom_attributes": "Anpassade attribut",
|
|
||||||
"data_type": "Datatyp",
|
|
||||||
"data_type_cannot_be_changed": "Datatypen kan inte ändras efter skapande",
|
|
||||||
"data_type_description": "Välj hur detta attribut ska lagras och filtreras",
|
|
||||||
"date_value_required": "Datumvärde krävs. Använd ta bort-knappen om du inte vill ange ett datum.",
|
|
||||||
"delete_attribute_confirmation": "{value, plural, one {Detta kommer att ta bort det valda attributet. All kontaktdata som är kopplad till detta attribut kommer att gå förlorad.} other {Detta kommer att ta bort de valda attributen. All kontaktdata som är kopplad till dessa attribut kommer att gå förlorad.}}",
|
"delete_attribute_confirmation": "{value, plural, one {Detta kommer att ta bort det valda attributet. All kontaktdata som är kopplad till detta attribut kommer att gå förlorad.} other {Detta kommer att ta bort de valda attributen. All kontaktdata som är kopplad till dessa attribut kommer att gå förlorad.}}",
|
||||||
"delete_contact_confirmation": "Detta kommer att ta bort alla enkätsvar och kontaktattribut som är kopplade till denna kontakt. All målgruppsinriktning och personalisering baserad på denna kontakts data kommer att gå förlorad.",
|
"delete_contact_confirmation": "Detta kommer att ta bort alla enkätsvar och kontaktattribut som är kopplade till denna kontakt. All målgruppsinriktning och personalisering baserad på denna kontakts data kommer att gå förlorad.",
|
||||||
"delete_contact_confirmation_with_quotas": "{value, plural, one {Detta kommer att ta bort alla enkätsvar och kontaktattribut som är kopplade till denna kontakt. All målgruppsinriktning och personalisering baserad på denna kontakts data kommer att gå förlorad. Om denna kontakt har svar som räknas mot enkätkvoter, kommer kvotantalet att minskas men kvotgränserna förblir oförändrade.} other {Detta kommer att ta bort alla enkätsvar och kontaktattribut som är kopplade till dessa kontakter. All målgruppsinriktning och personalisering baserad på dessa kontakters data kommer att gå förlorad. Om dessa kontakter har svar som räknas mot enkätkvoter, kommer kvotantalet att minskas men kvotgränserna förblir oförändrade.}}",
|
"delete_contact_confirmation_with_quotas": "{value, plural, one {Detta kommer att ta bort alla enkätsvar och kontaktattribut som är kopplade till denna kontakt. All målgruppsinriktning och personalisering baserad på denna kontakts data kommer att gå förlorad. Om denna kontakt har svar som räknas mot enkätkvoter, kommer kvotantalet att minskas men kvotgränserna förblir oförändrade.} other {Detta kommer att ta bort alla enkätsvar och kontaktattribut som är kopplade till dessa kontakter. All målgruppsinriktning och personalisering baserad på dessa kontakters data kommer att gå förlorad. Om dessa kontakter har svar som räknas mot enkätkvoter, kommer kvotantalet att minskas men kvotgränserna förblir oförändrade.}}",
|
||||||
"displays": "Visningar",
|
|
||||||
"edit_attribute": "Redigera attribut",
|
"edit_attribute": "Redigera attribut",
|
||||||
"edit_attribute_description": "Uppdatera etikett och beskrivning för detta attribut.",
|
"edit_attribute_description": "Uppdatera etikett och beskrivning för detta attribut.",
|
||||||
"edit_attribute_values": "Redigera attribut",
|
"edit_attribute_values": "Redigera attribut",
|
||||||
"edit_attribute_values_description": "Ändra värdena för specifika attribut för denna kontakt.",
|
"edit_attribute_values_description": "Ändra värdena för specifika attribut för denna kontakt.",
|
||||||
"edit_attributes": "Redigera attribut",
|
|
||||||
"edit_attributes_success": "Kontaktens attribut har uppdaterats",
|
"edit_attributes_success": "Kontaktens attribut har uppdaterats",
|
||||||
"generate_personal_link": "Generera personlig länk",
|
"generate_personal_link": "Generera personlig länk",
|
||||||
"generate_personal_link_description": "Välj en publicerad enkät för att generera en personlig länk för denna kontakt.",
|
"generate_personal_link_description": "Välj en publicerad enkät för att generera en personlig länk för denna kontakt.",
|
||||||
"invalid_csv_column_names": "Ogiltiga CSV-kolumnnamn: {columns}. Kolumnnamn som ska bli nya attribut får bara innehålla små bokstäver, siffror och understreck, och måste börja med en bokstav.",
|
|
||||||
"invalid_date_format": "Ogiltigt datumformat. Ange ett giltigt datum.",
|
|
||||||
"invalid_number_format": "Ogiltigt nummerformat. Ange ett giltigt nummer.",
|
|
||||||
"no_activity_yet": "Ingen aktivitet än",
|
|
||||||
"no_published_link_surveys_available": "Inga publicerade länkenkäter tillgängliga. Vänligen publicera en länkenkät först.",
|
"no_published_link_surveys_available": "Inga publicerade länkenkäter tillgängliga. Vänligen publicera en länkenkät först.",
|
||||||
"no_published_surveys": "Inga publicerade enkäter",
|
"no_published_surveys": "Inga publicerade enkäter",
|
||||||
"no_responses_found": "Inga svar hittades",
|
"no_responses_found": "Inga svar hittades",
|
||||||
"not_provided": "Ej angiven",
|
"not_provided": "Ej angiven",
|
||||||
"number_value_required": "Ett numeriskt värde krävs. Använd ta bort-knappen för att ta bort den här attributen.",
|
|
||||||
"personal_link_generated": "Personlig länk genererad",
|
"personal_link_generated": "Personlig länk genererad",
|
||||||
"personal_link_generated_but_clipboard_failed": "Personlig länk genererad men kunde inte kopieras till urklipp: {url}",
|
"personal_link_generated_but_clipboard_failed": "Personlig länk genererad men kunde inte kopieras till urklipp: {url}",
|
||||||
"personal_survey_link": "Personlig enkätlänk",
|
"personal_survey_link": "Personlig enkätlänk",
|
||||||
@@ -683,24 +656,13 @@
|
|||||||
"search_contact": "Sök kontakt",
|
"search_contact": "Sök kontakt",
|
||||||
"select_a_survey": "Välj en enkät",
|
"select_a_survey": "Välj en enkät",
|
||||||
"select_attribute": "Välj attribut",
|
"select_attribute": "Välj attribut",
|
||||||
"select_attribute_key": "Välj attributnyckel",
|
|
||||||
"survey_viewed": "Enkät visad",
|
|
||||||
"survey_viewed_at": "Visad kl.",
|
|
||||||
"system_attributes": "Systemattribut",
|
|
||||||
"unlock_contacts_description": "Hantera kontakter och skicka ut riktade enkäter",
|
"unlock_contacts_description": "Hantera kontakter och skicka ut riktade enkäter",
|
||||||
"unlock_contacts_title": "Lås upp kontakter med en högre plan",
|
"unlock_contacts_title": "Lås upp kontakter med en högre plan",
|
||||||
"upload_contacts_error_attribute_type_mismatch": "Attributet \"{key}\" är av typen \"{dataType}\" men CSV-filen innehåller ogiltiga värden: {values}",
|
|
||||||
"upload_contacts_error_duplicate_mappings": "Dubblettmappningar hittades för följande attribut: {attributes}",
|
|
||||||
"upload_contacts_error_file_too_large": "Filstorleken överskrider maxgränsen på 800 KB",
|
|
||||||
"upload_contacts_error_generic": "Ett fel uppstod vid uppladdning av kontakter. Försök igen senare.",
|
|
||||||
"upload_contacts_error_invalid_file_type": "Ladda upp en CSV-fil",
|
|
||||||
"upload_contacts_error_no_valid_contacts": "Den uppladdade CSV-filen innehåller inga giltiga kontakter, se exempel på CSV-fil för korrekt format.",
|
|
||||||
"upload_contacts_modal_attribute_header": "Formbricks-attribut",
|
|
||||||
"upload_contacts_modal_attributes_description": "Mappa kolumnerna i din CSV till attributen i Formbricks.",
|
"upload_contacts_modal_attributes_description": "Mappa kolumnerna i din CSV till attributen i Formbricks.",
|
||||||
"upload_contacts_modal_attributes_new": "Nytt attribut",
|
"upload_contacts_modal_attributes_new": "Nytt attribut",
|
||||||
"upload_contacts_modal_attributes_search_or_add": "Sök eller lägg till attribut",
|
"upload_contacts_modal_attributes_search_or_add": "Sök eller lägg till attribut",
|
||||||
|
"upload_contacts_modal_attributes_should_be_mapped_to": "ska mappas till",
|
||||||
"upload_contacts_modal_attributes_title": "Attribut",
|
"upload_contacts_modal_attributes_title": "Attribut",
|
||||||
"upload_contacts_modal_csv_column_header": "CSV-kolumn",
|
|
||||||
"upload_contacts_modal_description": "Ladda upp en CSV för att snabbt importera kontakter med attribut",
|
"upload_contacts_modal_description": "Ladda upp en CSV för att snabbt importera kontakter med attribut",
|
||||||
"upload_contacts_modal_download_example_csv": "Ladda ner exempel-CSV",
|
"upload_contacts_modal_download_example_csv": "Ladda ner exempel-CSV",
|
||||||
"upload_contacts_modal_duplicates_description": "Hur ska vi hantera om en kontakt redan finns i dina kontakter?",
|
"upload_contacts_modal_duplicates_description": "Hur ska vi hantera om en kontakt redan finns i dina kontakter?",
|
||||||
@@ -757,12 +719,7 @@
|
|||||||
"link_google_sheet": "Länka Google Kalkylark",
|
"link_google_sheet": "Länka Google Kalkylark",
|
||||||
"link_new_sheet": "Länka nytt kalkylark",
|
"link_new_sheet": "Länka nytt kalkylark",
|
||||||
"no_integrations_yet": "Dina Google Kalkylark-integrationer visas här så snart du lägger till dem. ⏲️",
|
"no_integrations_yet": "Dina Google Kalkylark-integrationer visas här så snart du lägger till dem. ⏲️",
|
||||||
"reconnect_button": "Återanslut",
|
"spreadsheet_url": "Kalkylblads-URL"
|
||||||
"reconnect_button_description": "Din Google Sheets-anslutning har gått ut. Återanslut för att fortsätta synkronisera svar. Dina befintliga kalkylarkslänkar och data kommer att sparas.",
|
|
||||||
"reconnect_button_tooltip": "Återanslut integrationen för att uppdatera din åtkomst. Dina befintliga kalkylarkslänkar och data kommer att sparas.",
|
|
||||||
"spreadsheet_permission_error": "Du har inte behörighet att komma åt det här kalkylarket. Kontrollera att kalkylarket är delat med ditt Google-konto och att du har skrivrättigheter till kalkylarket.",
|
|
||||||
"spreadsheet_url": "Kalkylblads-URL",
|
|
||||||
"token_expired_error": "Google Sheets refresh token har gått ut eller återkallats. Återanslut integrationen."
|
|
||||||
},
|
},
|
||||||
"include_created_at": "Inkludera Skapad vid",
|
"include_created_at": "Inkludera Skapad vid",
|
||||||
"include_hidden_fields": "Inkludera dolda fält",
|
"include_hidden_fields": "Inkludera dolda fält",
|
||||||
@@ -886,40 +843,6 @@
|
|||||||
"no_attributes_yet": "Inga attribut ännu!",
|
"no_attributes_yet": "Inga attribut ännu!",
|
||||||
"no_filters_yet": "Det finns inga filter ännu!",
|
"no_filters_yet": "Det finns inga filter ännu!",
|
||||||
"no_segments_yet": "Du har för närvarande inga sparade segment.",
|
"no_segments_yet": "Du har för närvarande inga sparade segment.",
|
||||||
"operator_contains": "innehåller",
|
|
||||||
"operator_does_not_contain": "innehåller inte",
|
|
||||||
"operator_ends_with": "slutar med",
|
|
||||||
"operator_is_after": "är efter",
|
|
||||||
"operator_is_before": "är före",
|
|
||||||
"operator_is_between": "är mellan",
|
|
||||||
"operator_is_newer_than": "är nyare än",
|
|
||||||
"operator_is_not_set": "är inte satt",
|
|
||||||
"operator_is_older_than": "är äldre än",
|
|
||||||
"operator_is_same_day": "är samma dag",
|
|
||||||
"operator_is_set": "är satt",
|
|
||||||
"operator_starts_with": "börjar med",
|
|
||||||
"operator_title_contains": "Innehåller",
|
|
||||||
"operator_title_does_not_contain": "Innehåller inte",
|
|
||||||
"operator_title_ends_with": "Slutar med",
|
|
||||||
"operator_title_equals": "Är lika med",
|
|
||||||
"operator_title_greater_equal": "Större än eller lika med",
|
|
||||||
"operator_title_greater_than": "Större än",
|
|
||||||
"operator_title_is_after": "Är efter",
|
|
||||||
"operator_title_is_before": "Är före",
|
|
||||||
"operator_title_is_between": "Är mellan",
|
|
||||||
"operator_title_is_newer_than": "Är nyare än",
|
|
||||||
"operator_title_is_not_set": "Är inte satt",
|
|
||||||
"operator_title_is_older_than": "Är äldre än",
|
|
||||||
"operator_title_is_same_day": "Är samma dag",
|
|
||||||
"operator_title_is_set": "Är satt",
|
|
||||||
"operator_title_less_equal": "Mindre än eller lika med",
|
|
||||||
"operator_title_less_than": "Mindre än",
|
|
||||||
"operator_title_not_equals": "Är inte lika med",
|
|
||||||
"operator_title_starts_with": "Börjar med",
|
|
||||||
"operator_title_user_is_in": "Användaren är i",
|
|
||||||
"operator_title_user_is_not_in": "Användaren är inte i",
|
|
||||||
"operator_user_is_in": "Användaren är i",
|
|
||||||
"operator_user_is_not_in": "Användaren är inte i",
|
|
||||||
"person_and_attributes": "Person och attribut",
|
"person_and_attributes": "Person och attribut",
|
||||||
"phone": "Telefon",
|
"phone": "Telefon",
|
||||||
"please_remove_the_segment_from_these_surveys_in_order_to_delete_it": "Vänligen ta bort segmentet från dessa enkäter för att kunna ta bort det.",
|
"please_remove_the_segment_from_these_surveys_in_order_to_delete_it": "Vänligen ta bort segmentet från dessa enkäter för att kunna ta bort det.",
|
||||||
@@ -944,7 +867,6 @@
|
|||||||
"user_targeting_is_currently_only_available_when": "Användarinriktning är för närvarande endast tillgänglig när",
|
"user_targeting_is_currently_only_available_when": "Användarinriktning är för närvarande endast tillgänglig när",
|
||||||
"value_cannot_be_empty": "Värdet kan inte vara tomt.",
|
"value_cannot_be_empty": "Värdet kan inte vara tomt.",
|
||||||
"value_must_be_a_number": "Värdet måste vara ett nummer.",
|
"value_must_be_a_number": "Värdet måste vara ett nummer.",
|
||||||
"value_must_be_positive": "Värdet måste vara ett positivt nummer.",
|
|
||||||
"view_filters": "Visa filter",
|
"view_filters": "Visa filter",
|
||||||
"where": "Där",
|
"where": "Där",
|
||||||
"with_the_formbricks_sdk": "med Formbricks SDK"
|
"with_the_formbricks_sdk": "med Formbricks SDK"
|
||||||
@@ -1031,32 +953,19 @@
|
|||||||
"enterprise_features": "Enterprise-funktioner",
|
"enterprise_features": "Enterprise-funktioner",
|
||||||
"get_an_enterprise_license_to_get_access_to_all_features": "Skaffa en Enterprise-licens för att få tillgång till alla funktioner.",
|
"get_an_enterprise_license_to_get_access_to_all_features": "Skaffa en Enterprise-licens för att få tillgång till alla funktioner.",
|
||||||
"keep_full_control_over_your_data_privacy_and_security": "Behåll full kontroll över din datasekretess och säkerhet.",
|
"keep_full_control_over_your_data_privacy_and_security": "Behåll full kontroll över din datasekretess och säkerhet.",
|
||||||
"license_invalid_description": "Licensnyckeln i din ENTERPRISE_LICENSE_KEY-miljövariabel är ogiltig. Kontrollera om det finns stavfel eller begär en ny nyckel.",
|
|
||||||
"license_status": "Licensstatus",
|
|
||||||
"license_status_active": "Aktiv",
|
|
||||||
"license_status_description": "Status för din företagslicens.",
|
|
||||||
"license_status_expired": "Utgången",
|
|
||||||
"license_status_invalid": "Ogiltig licens",
|
|
||||||
"license_status_unreachable": "Otillgänglig",
|
|
||||||
"license_unreachable_grace_period": "Licensservern kan inte nås. Dina enterprise-funktioner är aktiva under en 3-dagars respitperiod som slutar {gracePeriodEnd}.",
|
|
||||||
"no_call_needed_no_strings_attached_request_a_free_30_day_trial_license_to_test_all_features_by_filling_out_this_form": "Inget samtal behövs, inga åtaganden: Begär en gratis 30-dagars provlicens för att testa alla funktioner genom att fylla i detta formulär:",
|
"no_call_needed_no_strings_attached_request_a_free_30_day_trial_license_to_test_all_features_by_filling_out_this_form": "Inget samtal behövs, inga åtaganden: Begär en gratis 30-dagars provlicens för att testa alla funktioner genom att fylla i detta formulär:",
|
||||||
"no_credit_card_no_sales_call_just_test_it": "Inget kreditkort. Inget säljsamtal. Testa bara :)",
|
"no_credit_card_no_sales_call_just_test_it": "Inget kreditkort. Inget säljsamtal. Testa bara :)",
|
||||||
"on_request": "På begäran",
|
"on_request": "På begäran",
|
||||||
"organization_roles": "Organisationsroller (Admin, Redaktör, Utvecklare, etc.)",
|
"organization_roles": "Organisationsroller (Admin, Redaktör, Utvecklare, etc.)",
|
||||||
"questions_please_reach_out_to": "Frågor? Kontakta",
|
"questions_please_reach_out_to": "Frågor? Kontakta",
|
||||||
"recheck_license": "Kontrollera licensen igen",
|
|
||||||
"recheck_license_failed": "Licenskontrollen misslyckades. Licensservern kan vara otillgänglig.",
|
|
||||||
"recheck_license_invalid": "Licensnyckeln är ogiltig. Kontrollera din ENTERPRISE_LICENSE_KEY.",
|
|
||||||
"recheck_license_success": "Licenskontrollen lyckades",
|
|
||||||
"recheck_license_unreachable": "Licensservern är otillgänglig. Försök igen senare.",
|
|
||||||
"rechecking": "Kontrollerar igen...",
|
|
||||||
"request_30_day_trial_license": "Begär 30-dagars provlicens",
|
"request_30_day_trial_license": "Begär 30-dagars provlicens",
|
||||||
"saml_sso": "SAML SSO",
|
"saml_sso": "SAML SSO",
|
||||||
"service_level_agreement": "Servicenivåavtal",
|
"service_level_agreement": "Servicenivåavtal",
|
||||||
"soc2_hipaa_iso_27001_compliance_check": "SOC2, HIPAA, ISO 27001 efterlevnadskontroll",
|
"soc2_hipaa_iso_27001_compliance_check": "SOC2, HIPAA, ISO 27001 efterlevnadskontroll",
|
||||||
"sso": "SSO (Google, Microsoft, OpenID Connect)",
|
"sso": "SSO (Google, Microsoft, OpenID Connect)",
|
||||||
"teams": "Team och åtkomstroller (Läs, Läs och skriv, Hantera)",
|
"teams": "Team och åtkomstroller (Läs, Läs och skriv, Hantera)",
|
||||||
"unlock_the_full_power_of_formbricks_free_for_30_days": "Lås upp Formbricks fulla kraft. Gratis i 30 dagar."
|
"unlock_the_full_power_of_formbricks_free_for_30_days": "Lås upp Formbricks fulla kraft. Gratis i 30 dagar.",
|
||||||
|
"your_enterprise_license_is_active_all_features_unlocked": "Din Enterprise-licens är aktiv. Alla funktioner upplåsta."
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"bulk_invite_warning_description": "På gratisplanen tilldelas alla organisationsmedlemmar alltid rollen \"Ägare\".",
|
"bulk_invite_warning_description": "På gratisplanen tilldelas alla organisationsmedlemmar alltid rollen \"Ägare\".",
|
||||||
@@ -1080,7 +989,7 @@
|
|||||||
"from_your_organization": "från din organisation",
|
"from_your_organization": "från din organisation",
|
||||||
"invitation_sent_once_more": "Inbjudan skickad igen.",
|
"invitation_sent_once_more": "Inbjudan skickad igen.",
|
||||||
"invite_deleted_successfully": "Inbjudan borttagen",
|
"invite_deleted_successfully": "Inbjudan borttagen",
|
||||||
"invite_expires_on": "Inbjudan går ut den {date}",
|
"invited_on": "Inbjuden den {date}",
|
||||||
"invites_failed": "Inbjudningar misslyckades",
|
"invites_failed": "Inbjudningar misslyckades",
|
||||||
"leave_organization": "Lämna organisation",
|
"leave_organization": "Lämna organisation",
|
||||||
"leave_organization_description": "Du kommer att lämna denna organisation och förlora åtkomst till alla enkäter och svar. Du kan endast återansluta om du blir inbjuden igen.",
|
"leave_organization_description": "Du kommer att lämna denna organisation och förlora åtkomst till alla enkäter och svar. Du kan endast återansluta om du blir inbjuden igen.",
|
||||||
@@ -1193,6 +1102,8 @@
|
|||||||
"please_fill_all_workspace_fields": "Fyll i alla fält för att lägga till en ny arbetsyta.",
|
"please_fill_all_workspace_fields": "Fyll i alla fält för att lägga till en ny arbetsyta.",
|
||||||
"read": "Läs",
|
"read": "Läs",
|
||||||
"read_write": "Läs och skriv",
|
"read_write": "Läs och skriv",
|
||||||
|
"select_member": "Välj medlem",
|
||||||
|
"select_workspace": "Välj arbetsyta",
|
||||||
"team_admin": "Teamadministratör",
|
"team_admin": "Teamadministratör",
|
||||||
"team_created_successfully": "Team skapat.",
|
"team_created_successfully": "Team skapat.",
|
||||||
"team_deleted_successfully": "Team borttaget.",
|
"team_deleted_successfully": "Team borttaget.",
|
||||||
@@ -1242,6 +1153,7 @@
|
|||||||
"add_fallback_placeholder": "Lägg till en platshållare att visa om det inte finns något värde att återkalla.",
|
"add_fallback_placeholder": "Lägg till en platshållare att visa om det inte finns något värde att återkalla.",
|
||||||
"add_hidden_field_id": "Lägg till dolt fält-ID",
|
"add_hidden_field_id": "Lägg till dolt fält-ID",
|
||||||
"add_highlight_border": "Lägg till markerad kant",
|
"add_highlight_border": "Lägg till markerad kant",
|
||||||
|
"add_highlight_border_description": "Lägg till en yttre kant till ditt enkätkort.",
|
||||||
"add_logic": "Lägg till logik",
|
"add_logic": "Lägg till logik",
|
||||||
"add_none_of_the_above": "Lägg till \"Inget av ovanstående\"",
|
"add_none_of_the_above": "Lägg till \"Inget av ovanstående\"",
|
||||||
"add_option": "Lägg till alternativ",
|
"add_option": "Lägg till alternativ",
|
||||||
@@ -1280,7 +1192,6 @@
|
|||||||
"block_duplicated": "Block duplicerat.",
|
"block_duplicated": "Block duplicerat.",
|
||||||
"bold": "Fet",
|
"bold": "Fet",
|
||||||
"brand_color": "Varumärkesfärg",
|
"brand_color": "Varumärkesfärg",
|
||||||
"brand_color_description": "Används för knappar, länkar och markeringar.",
|
|
||||||
"brightness": "Ljusstyrka",
|
"brightness": "Ljusstyrka",
|
||||||
"bulk_edit": "Massredigera",
|
"bulk_edit": "Massredigera",
|
||||||
"bulk_edit_description": "Redigera alla alternativ nedan, ett per rad. Tomma rader kommer att hoppas över och dubbletter tas bort.",
|
"bulk_edit_description": "Redigera alla alternativ nedan, ett per rad. Tomma rader kommer att hoppas över och dubbletter tas bort.",
|
||||||
@@ -1298,9 +1209,7 @@
|
|||||||
"capture_new_action": "Fånga ny åtgärd",
|
"capture_new_action": "Fånga ny åtgärd",
|
||||||
"card_arrangement_for_survey_type_derived": "Kortarrangemang för {surveyTypeDerived}-enkäter",
|
"card_arrangement_for_survey_type_derived": "Kortarrangemang för {surveyTypeDerived}-enkäter",
|
||||||
"card_background_color": "Kortets bakgrundsfärg",
|
"card_background_color": "Kortets bakgrundsfärg",
|
||||||
"card_background_color_description": "Fyller enkätkortets yta.",
|
|
||||||
"card_border_color": "Kortets kantfärg",
|
"card_border_color": "Kortets kantfärg",
|
||||||
"card_border_color_description": "Markerar enkätkortets kant.",
|
|
||||||
"card_styling": "Kortstil",
|
"card_styling": "Kortstil",
|
||||||
"casual": "Avslappnad",
|
"casual": "Avslappnad",
|
||||||
"caution_edit_duplicate": "Duplicera och redigera",
|
"caution_edit_duplicate": "Duplicera och redigera",
|
||||||
@@ -1311,12 +1220,20 @@
|
|||||||
"caution_explanation_responses_are_safe": "Äldre och nyare svar blandas vilket kan leda till vilseledande datasammanfattningar.",
|
"caution_explanation_responses_are_safe": "Äldre och nyare svar blandas vilket kan leda till vilseledande datasammanfattningar.",
|
||||||
"caution_recommendation": "Detta kan orsaka datainkonsekvenser i enkätsammanfattningen. Vi rekommenderar att duplicera enkäten istället.",
|
"caution_recommendation": "Detta kan orsaka datainkonsekvenser i enkätsammanfattningen. Vi rekommenderar att duplicera enkäten istället.",
|
||||||
"caution_text": "Ändringar kommer att leda till inkonsekvenser",
|
"caution_text": "Ändringar kommer att leda till inkonsekvenser",
|
||||||
|
"centered_modal_overlay_color": "Centrerad modal överläggsfärg",
|
||||||
"change_anyway": "Ändra ändå",
|
"change_anyway": "Ändra ändå",
|
||||||
"change_background": "Ändra bakgrund",
|
"change_background": "Ändra bakgrund",
|
||||||
"change_question_type": "Ändra frågetyp",
|
"change_question_type": "Ändra frågetyp",
|
||||||
"change_survey_type": "Byte av enkättyp påverkar befintlig åtkomst",
|
"change_survey_type": "Byte av enkättyp påverkar befintlig åtkomst",
|
||||||
|
"change_the_background_color_of_the_card": "Ändra kortets bakgrundsfärg.",
|
||||||
|
"change_the_background_color_of_the_input_fields": "Ändra inmatningsfältens bakgrundsfärg.",
|
||||||
"change_the_background_to_a_color_image_or_animation": "Ändra bakgrunden till en färg, bild eller animering.",
|
"change_the_background_to_a_color_image_or_animation": "Ändra bakgrunden till en färg, bild eller animering.",
|
||||||
|
"change_the_border_color_of_the_card": "Ändra kortets kantfärg.",
|
||||||
|
"change_the_border_color_of_the_input_fields": "Ändra inmatningsfältens kantfärg.",
|
||||||
|
"change_the_border_radius_of_the_card_and_the_inputs": "Ändra kantradie för kortet och inmatningsfälten.",
|
||||||
|
"change_the_brand_color_of_the_survey": "Ändra enkätens varumärkesfärg.",
|
||||||
"change_the_placement_of_this_survey": "Ändra placeringen av denna enkät.",
|
"change_the_placement_of_this_survey": "Ändra placeringen av denna enkät.",
|
||||||
|
"change_the_question_color_of_the_survey": "Ändra enkätens frågefärg.",
|
||||||
"changes_saved": "Ändringar sparade.",
|
"changes_saved": "Ändringar sparade.",
|
||||||
"changing_survey_type_will_remove_existing_distribution_channels": "Att ändra enkättypen påverkar hur den kan delas. Om respondenter redan har åtkomstlänkar för den nuvarande typen kan de förlora åtkomst efter bytet.",
|
"changing_survey_type_will_remove_existing_distribution_channels": "Att ändra enkättypen påverkar hur den kan delas. Om respondenter redan har åtkomstlänkar för den nuvarande typen kan de förlora åtkomst efter bytet.",
|
||||||
"checkbox_label": "Kryssruteetikett",
|
"checkbox_label": "Kryssruteetikett",
|
||||||
@@ -1456,6 +1373,7 @@
|
|||||||
"hide_progress_bar": "Dölj framstegsindikator",
|
"hide_progress_bar": "Dölj framstegsindikator",
|
||||||
"hide_question_settings": "Dölj frågeinställningar",
|
"hide_question_settings": "Dölj frågeinställningar",
|
||||||
"hostname": "Värdnamn",
|
"hostname": "Värdnamn",
|
||||||
|
"how_funky_do_you_want_your_cards_in_survey_type_derived_surveys": "Hur coola vill du att dina kort ska vara i {surveyTypeDerived}-enkäter",
|
||||||
"if_you_need_more_please": "Om du behöver mer, vänligen",
|
"if_you_need_more_please": "Om du behöver mer, vänligen",
|
||||||
"if_you_really_want_that_answer_ask_until_you_get_it": "Fortsätt visa när villkoren är uppfyllda tills ett svar skickas in.",
|
"if_you_really_want_that_answer_ask_until_you_get_it": "Fortsätt visa när villkoren är uppfyllda tills ett svar skickas in.",
|
||||||
"ignore_global_waiting_time": "Ignorera väntetid",
|
"ignore_global_waiting_time": "Ignorera väntetid",
|
||||||
@@ -1466,9 +1384,7 @@
|
|||||||
"initial_value": "Initialt värde",
|
"initial_value": "Initialt värde",
|
||||||
"inner_text": "Inre text",
|
"inner_text": "Inre text",
|
||||||
"input_border_color": "Inmatningsfältets kantfärg",
|
"input_border_color": "Inmatningsfältets kantfärg",
|
||||||
"input_border_color_description": "Markerar kanten på textfält och textområden.",
|
|
||||||
"input_color": "Inmatningsfärg",
|
"input_color": "Inmatningsfärg",
|
||||||
"input_color_description": "Fyller insidan av textfält.",
|
|
||||||
"insert_link": "Infoga länk",
|
"insert_link": "Infoga länk",
|
||||||
"invalid_targeting": "Ogiltig målgruppsinriktning: Vänligen kontrollera dina målgruppsfilter",
|
"invalid_targeting": "Ogiltig målgruppsinriktning: Vänligen kontrollera dina målgruppsfilter",
|
||||||
"invalid_video_url_warning": "Vänligen ange en giltig YouTube-, Vimeo- eller Loom-URL. Vi stöder för närvarande inte andra videohostingleverantörer.",
|
"invalid_video_url_warning": "Vänligen ange en giltig YouTube-, Vimeo- eller Loom-URL. Vi stöder för närvarande inte andra videohostingleverantörer.",
|
||||||
@@ -1552,12 +1468,13 @@
|
|||||||
"protect_survey_with_pin_description": "Endast användare som har PIN-koden kan komma åt enkäten.",
|
"protect_survey_with_pin_description": "Endast användare som har PIN-koden kan komma åt enkäten.",
|
||||||
"publish": "Publicera",
|
"publish": "Publicera",
|
||||||
"question": "Fråga",
|
"question": "Fråga",
|
||||||
|
"question_color": "Frågefärg",
|
||||||
"question_deleted": "Fråga borttagen.",
|
"question_deleted": "Fråga borttagen.",
|
||||||
"question_duplicated": "Fråga duplicerad.",
|
"question_duplicated": "Fråga duplicerad.",
|
||||||
"question_id_updated": "Fråge-ID uppdaterat",
|
"question_id_updated": "Fråge-ID uppdaterat",
|
||||||
"question_used_in_logic_warning_text": "Element från det här blocket används i en logikregel. Är du säker på att du vill ta bort det?",
|
"question_used_in_logic_warning_text": "Element från det här blocket används i en logikregel. Är du säker på att du vill ta bort det?",
|
||||||
"question_used_in_logic_warning_title": "Logikkonflikt",
|
"question_used_in_logic_warning_title": "Logikkonflikt",
|
||||||
"question_used_in_quota": "Denna fråga används i kvoten “{quotaName}”",
|
"question_used_in_quota": "Denna fråga används i kvoten \"{quotaName}\"",
|
||||||
"question_used_in_recall": "Denna fråga återkallas i fråga {questionIndex}.",
|
"question_used_in_recall": "Denna fråga återkallas i fråga {questionIndex}.",
|
||||||
"question_used_in_recall_ending_card": "Denna fråga återkallas i avslutningskortet",
|
"question_used_in_recall_ending_card": "Denna fråga återkallas i avslutningskortet",
|
||||||
"quotas": {
|
"quotas": {
|
||||||
@@ -1613,7 +1530,6 @@
|
|||||||
"response_limits_redirections_and_more": "Svarsgränser, omdirigeringar och mer.",
|
"response_limits_redirections_and_more": "Svarsgränser, omdirigeringar och mer.",
|
||||||
"response_options": "Svarsalternativ",
|
"response_options": "Svarsalternativ",
|
||||||
"roundness": "Rundhet",
|
"roundness": "Rundhet",
|
||||||
"roundness_description": "Styr hur rundade kortets hörn är.",
|
|
||||||
"row_used_in_logic_error": "Denna rad används i logiken för fråga {questionIndex}. Vänligen ta bort den från logiken först.",
|
"row_used_in_logic_error": "Denna rad används i logiken för fråga {questionIndex}. Vänligen ta bort den från logiken först.",
|
||||||
"rows": "Rader",
|
"rows": "Rader",
|
||||||
"save_and_close": "Spara och stäng",
|
"save_and_close": "Spara och stäng",
|
||||||
@@ -1655,6 +1571,7 @@
|
|||||||
"styling_set_to_theme_styles": "Styling inställd på temastil",
|
"styling_set_to_theme_styles": "Styling inställd på temastil",
|
||||||
"subheading": "Underrubrik",
|
"subheading": "Underrubrik",
|
||||||
"subtract": "Subtrahera -",
|
"subtract": "Subtrahera -",
|
||||||
|
"suggest_colors": "Föreslå färger",
|
||||||
"survey_completed_heading": "Enkät slutförd",
|
"survey_completed_heading": "Enkät slutförd",
|
||||||
"survey_completed_subheading": "Denna gratis och öppenkällkodsenkät har stängts",
|
"survey_completed_subheading": "Denna gratis och öppenkällkodsenkät har stängts",
|
||||||
"survey_display_settings": "Visningsinställningar för enkät",
|
"survey_display_settings": "Visningsinställningar för enkät",
|
||||||
@@ -1731,7 +1648,7 @@
|
|||||||
"validation_rules": "Valideringsregler",
|
"validation_rules": "Valideringsregler",
|
||||||
"validation_rules_description": "Acceptera endast svar som uppfyller följande kriterier",
|
"validation_rules_description": "Acceptera endast svar som uppfyller följande kriterier",
|
||||||
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} används i logiken för fråga {questionIndex}. Vänligen ta bort den från logiken först.",
|
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "{variable} används i logiken för fråga {questionIndex}. Vänligen ta bort den från logiken först.",
|
||||||
"variable_is_used_in_quota_please_remove_it_from_quota_first": "Variabeln “{variableName}” används i kvoten “{quotaName}”",
|
"variable_is_used_in_quota_please_remove_it_from_quota_first": "Variabel \"{variableName}\" används i kvoten \"{quotaName}\"",
|
||||||
"variable_name_conflicts_with_hidden_field": "Variabelnamnet krockar med ett befintligt dolt fält-ID.",
|
"variable_name_conflicts_with_hidden_field": "Variabelnamnet krockar med ett befintligt dolt fält-ID.",
|
||||||
"variable_name_is_already_taken_please_choose_another": "Variabelnamnet är redan taget, vänligen välj ett annat.",
|
"variable_name_is_already_taken_please_choose_another": "Variabelnamnet är redan taget, vänligen välj ett annat.",
|
||||||
"variable_name_must_start_with_a_letter": "Variabelnamnet måste börja med en bokstav.",
|
"variable_name_must_start_with_a_letter": "Variabelnamnet måste börja med en bokstav.",
|
||||||
@@ -1957,7 +1874,6 @@
|
|||||||
"filtered_responses_excel": "Filtrerade svar (Excel)",
|
"filtered_responses_excel": "Filtrerade svar (Excel)",
|
||||||
"generating_qr_code": "Genererar QR-kod",
|
"generating_qr_code": "Genererar QR-kod",
|
||||||
"impressions": "Visningar",
|
"impressions": "Visningar",
|
||||||
"impressions_identified_only": "Visar bara visningar från identifierade kontakter",
|
|
||||||
"impressions_tooltip": "Antal gånger enkäten har visats.",
|
"impressions_tooltip": "Antal gånger enkäten har visats.",
|
||||||
"in_app": {
|
"in_app": {
|
||||||
"connection_description": "Enkäten kommer att visas för användare på din webbplats som matchar kriterierna nedan",
|
"connection_description": "Enkäten kommer att visas för användare på din webbplats som matchar kriterierna nedan",
|
||||||
@@ -2000,7 +1916,6 @@
|
|||||||
"last_quarter": "Senaste kvartalet",
|
"last_quarter": "Senaste kvartalet",
|
||||||
"last_year": "Senaste året",
|
"last_year": "Senaste året",
|
||||||
"limit": "Gräns",
|
"limit": "Gräns",
|
||||||
"no_identified_impressions": "Inga visningar från identifierade kontakter",
|
|
||||||
"no_responses_found": "Inga svar hittades",
|
"no_responses_found": "Inga svar hittades",
|
||||||
"other_values_found": "Andra värden hittades",
|
"other_values_found": "Andra värden hittades",
|
||||||
"overall": "Övergripande",
|
"overall": "Övergripande",
|
||||||
@@ -2140,71 +2055,9 @@
|
|||||||
"look": {
|
"look": {
|
||||||
"add_background_color": "Lägg till bakgrundsfärg",
|
"add_background_color": "Lägg till bakgrundsfärg",
|
||||||
"add_background_color_description": "Lägg till en bakgrundsfärg i logobehållaren.",
|
"add_background_color_description": "Lägg till en bakgrundsfärg i logobehållaren.",
|
||||||
"advanced_styling_field_border_radius": "Hörnradie",
|
|
||||||
"advanced_styling_field_button_bg": "Knappens bakgrund",
|
|
||||||
"advanced_styling_field_button_bg_description": "Fyller Nästa / Skicka-knappen.",
|
|
||||||
"advanced_styling_field_button_border_radius_description": "Rundar av knappens hörn.",
|
|
||||||
"advanced_styling_field_button_font_size_description": "Ändrar storleken på knappens text.",
|
|
||||||
"advanced_styling_field_button_font_weight_description": "Gör knapptexten tunnare eller fetare.",
|
|
||||||
"advanced_styling_field_button_height_description": "Styr knappens höjd.",
|
|
||||||
"advanced_styling_field_button_padding_x_description": "Lägger till utrymme till vänster och höger.",
|
|
||||||
"advanced_styling_field_button_padding_y_description": "Lägger till utrymme upptill och nedtill.",
|
|
||||||
"advanced_styling_field_button_text": "Knapptext",
|
|
||||||
"advanced_styling_field_button_text_description": "Färglägger texten i knappar.",
|
|
||||||
"advanced_styling_field_description_color": "Beskrivningsfärg",
|
|
||||||
"advanced_styling_field_description_color_description": "Färglägger texten under varje rubrik.",
|
|
||||||
"advanced_styling_field_description_size": "Beskrivningens teckenstorlek",
|
|
||||||
"advanced_styling_field_description_size_description": "Ändrar storleken på beskrivningstexten.",
|
|
||||||
"advanced_styling_field_description_weight": "Beskrivningens teckentjocklek",
|
|
||||||
"advanced_styling_field_description_weight_description": "Gör beskrivningstexten tunnare eller fetare.",
|
|
||||||
"advanced_styling_field_font_size": "Teckenstorlek",
|
|
||||||
"advanced_styling_field_font_weight": "Teckentjocklek",
|
|
||||||
"advanced_styling_field_headline_color": "Rubrikfärg",
|
|
||||||
"advanced_styling_field_headline_color_description": "Färglägger huvudfrågan.",
|
|
||||||
"advanced_styling_field_headline_size": "Rubrikens teckenstorlek",
|
|
||||||
"advanced_styling_field_headline_size_description": "Ändrar storleken på rubriken.",
|
|
||||||
"advanced_styling_field_headline_weight": "Rubrikens teckentjocklek",
|
|
||||||
"advanced_styling_field_headline_weight_description": "Gör rubriktexten tunnare eller fetare.",
|
|
||||||
"advanced_styling_field_height": "Minsta höjd",
|
|
||||||
"advanced_styling_field_indicator_bg": "Indikatorns bakgrund",
|
|
||||||
"advanced_styling_field_indicator_bg_description": "Färglägger den fyllda delen av stapeln.",
|
|
||||||
"advanced_styling_field_input_border_radius_description": "Rundar av hörnen på inmatningsfält.",
|
|
||||||
"advanced_styling_field_input_font_size_description": "Ändrar storleken på texten i inmatningsfält.",
|
|
||||||
"advanced_styling_field_input_height_description": "Styr den minsta höjden på inmatningsfältet.",
|
|
||||||
"advanced_styling_field_input_padding_x_description": "Lägger till utrymme till vänster och höger.",
|
|
||||||
"advanced_styling_field_input_padding_y_description": "Lägger till utrymme upptill och nedtill.",
|
|
||||||
"advanced_styling_field_input_placeholder_opacity_description": "Tonar ut platshållartexten.",
|
|
||||||
"advanced_styling_field_input_shadow_description": "Lägger till en skugga runt inmatningsfälten.",
|
|
||||||
"advanced_styling_field_input_text": "Inmatningstext",
|
|
||||||
"advanced_styling_field_input_text_description": "Färgar den inmatade texten i fälten.",
|
|
||||||
"advanced_styling_field_option_bg": "Bakgrund",
|
|
||||||
"advanced_styling_field_option_bg_description": "Fyller alternativraderna.",
|
|
||||||
"advanced_styling_field_option_border_radius_description": "Rundar hörnen på alternativen.",
|
|
||||||
"advanced_styling_field_option_font_size_description": "Skalar textstorleken på alternativetiketten.",
|
|
||||||
"advanced_styling_field_option_label": "Etikettfärg",
|
|
||||||
"advanced_styling_field_option_label_description": "Färgar texten på alternativetiketten.",
|
|
||||||
"advanced_styling_field_option_padding_x_description": "Lägger till utrymme till vänster och höger.",
|
|
||||||
"advanced_styling_field_option_padding_y_description": "Lägger till utrymme upptill och nedtill.",
|
|
||||||
"advanced_styling_field_padding_x": "Horisontell padding",
|
|
||||||
"advanced_styling_field_padding_y": "Vertikal padding",
|
|
||||||
"advanced_styling_field_placeholder_opacity": "Platshållarens opacitet",
|
|
||||||
"advanced_styling_field_shadow": "Skugga",
|
|
||||||
"advanced_styling_field_track_bg": "Spårets bakgrund",
|
|
||||||
"advanced_styling_field_track_bg_description": "Färgar den ofyllda delen av stapeln.",
|
|
||||||
"advanced_styling_field_track_height": "Spårets höjd",
|
|
||||||
"advanced_styling_field_track_height_description": "Styr tjockleken på förloppsstapeln.",
|
|
||||||
"advanced_styling_field_upper_label_color": "Rubriketikettens färg",
|
|
||||||
"advanced_styling_field_upper_label_color_description": "Färgar den lilla etiketten ovanför fälten.",
|
|
||||||
"advanced_styling_field_upper_label_size": "Rubriketikettens teckenstorlek",
|
|
||||||
"advanced_styling_field_upper_label_size_description": "Skalar storleken på den lilla etiketten ovanför fälten.",
|
|
||||||
"advanced_styling_field_upper_label_weight": "Rubriketikettens teckentjocklek",
|
|
||||||
"advanced_styling_field_upper_label_weight_description": "Gör etiketten tunnare eller fetare.",
|
|
||||||
"advanced_styling_section_buttons": "Knappar",
|
|
||||||
"advanced_styling_section_headlines": "Rubriker & beskrivningar",
|
|
||||||
"advanced_styling_section_inputs": "Inmatningar",
|
|
||||||
"advanced_styling_section_options": "Alternativ (Radio/Checkbox)",
|
|
||||||
"app_survey_placement": "App-enkätplacering",
|
"app_survey_placement": "App-enkätplacering",
|
||||||
"app_survey_placement_settings_description": "Ändra var enkäter visas i din webbapp eller på din webbplats.",
|
"app_survey_placement_settings_description": "Ändra var enkäter visas i din webbapp eller på din webbplats.",
|
||||||
|
"centered_modal_overlay_color": "Centrerad modal överläggsfärg",
|
||||||
"email_customization": "E-postanpassning",
|
"email_customization": "E-postanpassning",
|
||||||
"email_customization_description": "Ändra utseendet på de e-postmeddelanden som Formbricks skickar åt dig.",
|
"email_customization_description": "Ändra utseendet på de e-postmeddelanden som Formbricks skickar åt dig.",
|
||||||
"enable_custom_styling": "Aktivera anpassad styling",
|
"enable_custom_styling": "Aktivera anpassad styling",
|
||||||
@@ -2215,9 +2068,6 @@
|
|||||||
"formbricks_branding_hidden": "Formbricks-varumärket är dolt.",
|
"formbricks_branding_hidden": "Formbricks-varumärket är dolt.",
|
||||||
"formbricks_branding_settings_description": "Vi uppskattar ditt stöd men förstår om du vill stänga av det.",
|
"formbricks_branding_settings_description": "Vi uppskattar ditt stöd men förstår om du vill stänga av det.",
|
||||||
"formbricks_branding_shown": "Formbricks-varumärket visas.",
|
"formbricks_branding_shown": "Formbricks-varumärket visas.",
|
||||||
"generate_theme_btn": "Generera",
|
|
||||||
"generate_theme_confirmation": "Vill du generera ett matchande färgtema baserat på din varumärkesfärg? Detta kommer att skriva över dina nuvarande färginställningar.",
|
|
||||||
"generate_theme_header": "Generera färgtema?",
|
|
||||||
"logo_removed_successfully": "Logotyp borttagen",
|
"logo_removed_successfully": "Logotyp borttagen",
|
||||||
"logo_settings_description": "Ladda upp företagets logotyp för att profilera enkäter och länkförhandsvisningar.",
|
"logo_settings_description": "Ladda upp företagets logotyp för att profilera enkäter och länkförhandsvisningar.",
|
||||||
"logo_updated_successfully": "Logotyp uppdaterad",
|
"logo_updated_successfully": "Logotyp uppdaterad",
|
||||||
@@ -2232,8 +2082,6 @@
|
|||||||
"show_formbricks_branding_in": "Visa Formbricks-varumärket i {type}-enkäter",
|
"show_formbricks_branding_in": "Visa Formbricks-varumärket i {type}-enkäter",
|
||||||
"show_powered_by_formbricks": "Visa 'Powered by Formbricks'-signatur",
|
"show_powered_by_formbricks": "Visa 'Powered by Formbricks'-signatur",
|
||||||
"styling_updated_successfully": "Stiluppdatering lyckades",
|
"styling_updated_successfully": "Stiluppdatering lyckades",
|
||||||
"suggest_colors": "Föreslå färger",
|
|
||||||
"suggested_colors_applied_please_save": "Föreslagna färger har skapats. Tryck på \"Spara\" för att spara ändringarna.",
|
|
||||||
"theme": "Tema",
|
"theme": "Tema",
|
||||||
"theme_settings_description": "Skapa ett stilmall för alla undersökningar. Du kan aktivera anpassad stil för varje undersökning."
|
"theme_settings_description": "Skapa ett stilmall för alla undersökningar. Du kan aktivera anpassad stil för varje undersökning."
|
||||||
},
|
},
|
||||||
@@ -2997,7 +2845,6 @@
|
|||||||
"preview_survey_question_2_choice_1_label": "Ja, håll mig informerad.",
|
"preview_survey_question_2_choice_1_label": "Ja, håll mig informerad.",
|
||||||
"preview_survey_question_2_choice_2_label": "Nej, tack!",
|
"preview_survey_question_2_choice_2_label": "Nej, tack!",
|
||||||
"preview_survey_question_2_headline": "Vill du hållas uppdaterad?",
|
"preview_survey_question_2_headline": "Vill du hållas uppdaterad?",
|
||||||
"preview_survey_question_2_subheader": "Det här är ett exempel på en beskrivning.",
|
|
||||||
"preview_survey_welcome_card_headline": "Välkommen!",
|
"preview_survey_welcome_card_headline": "Välkommen!",
|
||||||
"prioritize_features_description": "Identifiera vilka funktioner dina användare behöver mest och minst.",
|
"prioritize_features_description": "Identifiera vilka funktioner dina användare behöver mest och minst.",
|
||||||
"prioritize_features_name": "Prioritera funktioner",
|
"prioritize_features_name": "Prioritera funktioner",
|
||||||
|
|||||||
@@ -188,7 +188,6 @@
|
|||||||
"customer_success": "客户成功",
|
"customer_success": "客户成功",
|
||||||
"dark_overlay": "深色遮罩层",
|
"dark_overlay": "深色遮罩层",
|
||||||
"date": "日期",
|
"date": "日期",
|
||||||
"days": "天",
|
|
||||||
"default": "默认",
|
"default": "默认",
|
||||||
"delete": "删除",
|
"delete": "删除",
|
||||||
"description": "描述",
|
"description": "描述",
|
||||||
@@ -218,7 +217,6 @@
|
|||||||
"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": "展开 行",
|
||||||
@@ -277,7 +275,6 @@
|
|||||||
"mobile_overlay_app_works_best_on_desktop": "Formbricks 在 更大 的 屏幕 上 效果 最佳。 若 需要 管理 或 构建 调查, 请 切换 到 其他 设备。",
|
"mobile_overlay_app_works_best_on_desktop": "Formbricks 在 更大 的 屏幕 上 效果 最佳。 若 需要 管理 或 构建 调查, 请 切换 到 其他 设备。",
|
||||||
"mobile_overlay_surveys_look_good": "别 担心 – 您 的 调查 在 每 一 种 设备 和 屏幕 尺寸 上 看起来 都 很 棒!",
|
"mobile_overlay_surveys_look_good": "别 担心 – 您 的 调查 在 每 一 种 设备 和 屏幕 尺寸 上 看起来 都 很 棒!",
|
||||||
"mobile_overlay_title": "噢, 检测 到 小 屏幕!",
|
"mobile_overlay_title": "噢, 检测 到 小 屏幕!",
|
||||||
"months": "月",
|
|
||||||
"move_down": "下移",
|
"move_down": "下移",
|
||||||
"move_up": "上移",
|
"move_up": "上移",
|
||||||
"multiple_languages": "多种 语言",
|
"multiple_languages": "多种 语言",
|
||||||
@@ -288,7 +285,6 @@
|
|||||||
"no_background_image_found": "未找到 背景 图片。",
|
"no_background_image_found": "未找到 背景 图片。",
|
||||||
"no_code": "无代码",
|
"no_code": "无代码",
|
||||||
"no_files_uploaded": "没有 文件 被 上传",
|
"no_files_uploaded": "没有 文件 被 上传",
|
||||||
"no_overlay": "无覆盖层",
|
|
||||||
"no_quotas_found": "未找到配额",
|
"no_quotas_found": "未找到配额",
|
||||||
"no_result_found": "没有 结果",
|
"no_result_found": "没有 结果",
|
||||||
"no_results": "没有 结果",
|
"no_results": "没有 结果",
|
||||||
@@ -315,7 +311,6 @@
|
|||||||
"organization_teams_not_found": "未找到 组织 团队",
|
"organization_teams_not_found": "未找到 组织 团队",
|
||||||
"other": "其他",
|
"other": "其他",
|
||||||
"others": "其他",
|
"others": "其他",
|
||||||
"overlay_color": "覆盖层颜色",
|
|
||||||
"overview": "概览",
|
"overview": "概览",
|
||||||
"password": "密码",
|
"password": "密码",
|
||||||
"paused": "暂停",
|
"paused": "暂停",
|
||||||
@@ -355,7 +350,6 @@
|
|||||||
"request_trial_license": "申请试用许可证",
|
"request_trial_license": "申请试用许可证",
|
||||||
"reset_to_default": "重置为 默认",
|
"reset_to_default": "重置为 默认",
|
||||||
"response": "响应",
|
"response": "响应",
|
||||||
"response_id": "响应 ID",
|
|
||||||
"responses": "反馈",
|
"responses": "反馈",
|
||||||
"restart": "重新启动",
|
"restart": "重新启动",
|
||||||
"role": "角色",
|
"role": "角色",
|
||||||
@@ -396,7 +390,6 @@
|
|||||||
"status": "状态",
|
"status": "状态",
|
||||||
"step_by_step_manual": "分步 手册",
|
"step_by_step_manual": "分步 手册",
|
||||||
"storage_not_configured": "文件存储 未设置,上传 可能 失败",
|
"storage_not_configured": "文件存储 未设置,上传 可能 失败",
|
||||||
"string": "文本",
|
|
||||||
"styling": "样式",
|
"styling": "样式",
|
||||||
"submit": "提交",
|
"submit": "提交",
|
||||||
"summary": "概要",
|
"summary": "概要",
|
||||||
@@ -429,7 +422,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": "已更新",
|
||||||
@@ -453,7 +445,6 @@
|
|||||||
"website_and_app_connection": "网站 & 应用程序 连接",
|
"website_and_app_connection": "网站 & 应用程序 连接",
|
||||||
"website_app_survey": "网站 & 应用 调查",
|
"website_app_survey": "网站 & 应用 调查",
|
||||||
"website_survey": "网站 调查",
|
"website_survey": "网站 调查",
|
||||||
"weeks": "周",
|
|
||||||
"welcome_card": "欢迎 卡片",
|
"welcome_card": "欢迎 卡片",
|
||||||
"workspace_configuration": "工作区配置",
|
"workspace_configuration": "工作区配置",
|
||||||
"workspace_created_successfully": "工作区创建成功",
|
"workspace_created_successfully": "工作区创建成功",
|
||||||
@@ -464,7 +455,6 @@
|
|||||||
"workspace_not_found": "未找到工作区",
|
"workspace_not_found": "未找到工作区",
|
||||||
"workspace_permission_not_found": "未找到工作区权限",
|
"workspace_permission_not_found": "未找到工作区权限",
|
||||||
"workspaces": "工作区",
|
"workspaces": "工作区",
|
||||||
"years": "年",
|
|
||||||
"you": "你 ",
|
"you": "你 ",
|
||||||
"you_are_downgraded_to_the_community_edition": "您已降级到社区版。",
|
"you_are_downgraded_to_the_community_edition": "您已降级到社区版。",
|
||||||
"you_are_not_authorized_to_perform_this_action": "您无权执行此操作。",
|
"you_are_not_authorized_to_perform_this_action": "您无权执行此操作。",
|
||||||
@@ -625,7 +615,7 @@
|
|||||||
"attribute_created_successfully": "属性创建成功",
|
"attribute_created_successfully": "属性创建成功",
|
||||||
"attribute_description": "描述",
|
"attribute_description": "描述",
|
||||||
"attribute_description_placeholder": "简短描述",
|
"attribute_description_placeholder": "简短描述",
|
||||||
"attribute_key": "键",
|
"attribute_key": "属性",
|
||||||
"attribute_key_cannot_be_changed": "创建后键不可更改",
|
"attribute_key_cannot_be_changed": "创建后键不可更改",
|
||||||
"attribute_key_hint": "仅允许小写字母、数字和下划线,且必须以字母开头。",
|
"attribute_key_hint": "仅允许小写字母、数字和下划线,且必须以字母开头。",
|
||||||
"attribute_key_placeholder": "例如:date_of_birth",
|
"attribute_key_placeholder": "例如:date_of_birth",
|
||||||
@@ -636,45 +626,28 @@
|
|||||||
"attribute_updated_successfully": "属性更新成功",
|
"attribute_updated_successfully": "属性更新成功",
|
||||||
"attribute_value": "值",
|
"attribute_value": "值",
|
||||||
"attribute_value_placeholder": "属性值",
|
"attribute_value_placeholder": "属性值",
|
||||||
"attributes_msg_attribute_limit_exceeded": "无法创建 {count} 个新属性,因为这将超过最多 {limit} 个属性类别的限制。已有属性已成功更新。",
|
|
||||||
"attributes_msg_attribute_type_validation_error": "{error}(属性“{key}”的数据类型为:{dataType})",
|
|
||||||
"attributes_msg_email_already_exists": "该邮箱已存在于当前环境,未进行更新。",
|
|
||||||
"attributes_msg_email_or_userid_required": "必须填写邮箱或 userId。已保留原有值。",
|
|
||||||
"attributes_msg_new_attribute_created": "已创建新属性“{key}”,类型为“{dataType}”",
|
|
||||||
"attributes_msg_userid_already_exists": "该 userId 已存在于当前环境,未进行更新。",
|
|
||||||
"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": "自定义属性",
|
|
||||||
"data_type": "数据类型",
|
|
||||||
"data_type_cannot_be_changed": "数据类型创建后无法更改",
|
|
||||||
"data_type_description": "选择此属性的存储和筛选方式",
|
|
||||||
"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": "编辑属性",
|
||||||
"edit_attribute_values_description": "更改此联系人的特定属性值。",
|
"edit_attribute_values_description": "更改此联系人的特定属性值。",
|
||||||
"edit_attributes": "编辑属性",
|
|
||||||
"edit_attributes_success": "联系人属性更新成功",
|
"edit_attributes_success": "联系人属性更新成功",
|
||||||
"generate_personal_link": "生成个人链接",
|
"generate_personal_link": "生成个人链接",
|
||||||
"generate_personal_link_description": "选择一个已发布的调查,为此联系人生成个性化链接。",
|
"generate_personal_link_description": "选择一个已发布的调查,为此联系人生成个性化链接。",
|
||||||
"invalid_csv_column_names": "无效的 CSV 列名:{columns}。作为新属性的列名只能包含小写字母、数字和下划线,并且必须以字母开头。",
|
|
||||||
"invalid_date_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": "未找到 响应",
|
||||||
"not_provided": "未提供",
|
"not_provided": "未提供",
|
||||||
"number_value_required": "需要填写数字值。要移除此属性,请使用删除按钮。",
|
|
||||||
"personal_link_generated": "个人链接生成成功",
|
"personal_link_generated": "个人链接生成成功",
|
||||||
"personal_link_generated_but_clipboard_failed": "个性化链接已生成,但复制到剪贴板失败:{url}",
|
"personal_link_generated_but_clipboard_failed": "个性化链接已生成,但复制到剪贴板失败:{url}",
|
||||||
"personal_survey_link": "个人调查链接",
|
"personal_survey_link": "个人调查链接",
|
||||||
@@ -683,24 +656,13 @@
|
|||||||
"search_contact": "搜索 联系人",
|
"search_contact": "搜索 联系人",
|
||||||
"select_a_survey": "选择一个调查",
|
"select_a_survey": "选择一个调查",
|
||||||
"select_attribute": "选择 属性",
|
"select_attribute": "选择 属性",
|
||||||
"select_attribute_key": "选择属性键",
|
|
||||||
"survey_viewed": "已查看调查",
|
|
||||||
"survey_viewed_at": "查看时间",
|
|
||||||
"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_duplicate_mappings": "以下属性存在重复映射:{attributes}",
|
|
||||||
"upload_contacts_error_file_too_large": "文件大小超过最大限制 800KB",
|
|
||||||
"upload_contacts_error_generic": "上传联系人时发生错误,请稍后再试。",
|
|
||||||
"upload_contacts_error_invalid_file_type": "请上传 CSV 文件",
|
|
||||||
"upload_contacts_error_no_valid_contacts": "上传的 CSV 文件中不包含任何有效联系人,请参考示例 CSV 文件获取正确格式。",
|
|
||||||
"upload_contacts_modal_attribute_header": "Formbricks 属性",
|
|
||||||
"upload_contacts_modal_attributes_description": "将您 CSV 中的列映射到 Formbricks 中的属性。",
|
"upload_contacts_modal_attributes_description": "将您 CSV 中的列映射到 Formbricks 中的属性。",
|
||||||
"upload_contacts_modal_attributes_new": "新 属性",
|
"upload_contacts_modal_attributes_new": "新 属性",
|
||||||
"upload_contacts_modal_attributes_search_or_add": "搜索或添加属性",
|
"upload_contacts_modal_attributes_search_or_add": "搜索或添加属性",
|
||||||
|
"upload_contacts_modal_attributes_should_be_mapped_to": "应该映射到",
|
||||||
"upload_contacts_modal_attributes_title": "属性",
|
"upload_contacts_modal_attributes_title": "属性",
|
||||||
"upload_contacts_modal_csv_column_header": "CSV 列",
|
|
||||||
"upload_contacts_modal_description": "上传 CSV,快速 导入 具有 属性 的 联系人",
|
"upload_contacts_modal_description": "上传 CSV,快速 导入 具有 属性 的 联系人",
|
||||||
"upload_contacts_modal_download_example_csv": "下载 示例 CSV",
|
"upload_contacts_modal_download_example_csv": "下载 示例 CSV",
|
||||||
"upload_contacts_modal_duplicates_description": "如果联系人已经存在,应该如何处理?",
|
"upload_contacts_modal_duplicates_description": "如果联系人已经存在,应该如何处理?",
|
||||||
@@ -757,12 +719,7 @@
|
|||||||
"link_google_sheet": "链接 Google 表格",
|
"link_google_sheet": "链接 Google 表格",
|
||||||
"link_new_sheet": "链接 新 表格",
|
"link_new_sheet": "链接 新 表格",
|
||||||
"no_integrations_yet": "您的 Google Sheet 集成会在您 添加 后 出现在这里。 ⏲️",
|
"no_integrations_yet": "您的 Google Sheet 集成会在您 添加 后 出现在这里。 ⏲️",
|
||||||
"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": "包括 隐藏 字段",
|
||||||
@@ -886,40 +843,6 @@
|
|||||||
"no_attributes_yet": "暂无属性!",
|
"no_attributes_yet": "暂无属性!",
|
||||||
"no_filters_yet": "还 没有 筛选器!",
|
"no_filters_yet": "还 没有 筛选器!",
|
||||||
"no_segments_yet": "您 目前 尚无 保存 的 段。",
|
"no_segments_yet": "您 目前 尚无 保存 的 段。",
|
||||||
"operator_contains": "包含",
|
|
||||||
"operator_does_not_contain": "不包含",
|
|
||||||
"operator_ends_with": "以...结束",
|
|
||||||
"operator_is_after": "在...之后",
|
|
||||||
"operator_is_before": "在...之前",
|
|
||||||
"operator_is_between": "介于...之间",
|
|
||||||
"operator_is_newer_than": "比...更新",
|
|
||||||
"operator_is_not_set": "未设置",
|
|
||||||
"operator_is_older_than": "比...更早",
|
|
||||||
"operator_is_same_day": "同一天",
|
|
||||||
"operator_is_set": "已设置",
|
|
||||||
"operator_starts_with": "以...开始",
|
|
||||||
"operator_title_contains": "包含",
|
|
||||||
"operator_title_does_not_contain": "不包含",
|
|
||||||
"operator_title_ends_with": "以...结束",
|
|
||||||
"operator_title_equals": "等于",
|
|
||||||
"operator_title_greater_equal": "大于或等于",
|
|
||||||
"operator_title_greater_than": "大于",
|
|
||||||
"operator_title_is_after": "在...之后",
|
|
||||||
"operator_title_is_before": "在...之前",
|
|
||||||
"operator_title_is_between": "介于...之间",
|
|
||||||
"operator_title_is_newer_than": "比...更新",
|
|
||||||
"operator_title_is_not_set": "未设置",
|
|
||||||
"operator_title_is_older_than": "比...更早",
|
|
||||||
"operator_title_is_same_day": "同一天",
|
|
||||||
"operator_title_is_set": "已设置",
|
|
||||||
"operator_title_less_equal": "小于或等于",
|
|
||||||
"operator_title_less_than": "小于",
|
|
||||||
"operator_title_not_equals": "不等于",
|
|
||||||
"operator_title_starts_with": "以...开始",
|
|
||||||
"operator_title_user_is_in": "用户属于",
|
|
||||||
"operator_title_user_is_not_in": "用户不属于",
|
|
||||||
"operator_user_is_in": "用户属于",
|
|
||||||
"operator_user_is_not_in": "用户不属于",
|
|
||||||
"person_and_attributes": "人员 及 属性",
|
"person_and_attributes": "人员 及 属性",
|
||||||
"phone": "电话",
|
"phone": "电话",
|
||||||
"please_remove_the_segment_from_these_surveys_in_order_to_delete_it": "请 从 这些 调查 中 移除 该 部分 以 进行 删除。",
|
"please_remove_the_segment_from_these_surveys_in_order_to_delete_it": "请 从 这些 调查 中 移除 该 部分 以 进行 删除。",
|
||||||
@@ -944,7 +867,6 @@
|
|||||||
"user_targeting_is_currently_only_available_when": "目标用户 功能 当前 仅 限于 当",
|
"user_targeting_is_currently_only_available_when": "目标用户 功能 当前 仅 限于 当",
|
||||||
"value_cannot_be_empty": "值 不能为空。",
|
"value_cannot_be_empty": "值 不能为空。",
|
||||||
"value_must_be_a_number": "值 必须 是 一个 数字。",
|
"value_must_be_a_number": "值 必须 是 一个 数字。",
|
||||||
"value_must_be_positive": "值必须是正数。",
|
|
||||||
"view_filters": "查看 筛选条件",
|
"view_filters": "查看 筛选条件",
|
||||||
"where": "位置",
|
"where": "位置",
|
||||||
"with_the_formbricks_sdk": "与 Formbricks SDK"
|
"with_the_formbricks_sdk": "与 Formbricks SDK"
|
||||||
@@ -1031,32 +953,19 @@
|
|||||||
"enterprise_features": "企业 功能",
|
"enterprise_features": "企业 功能",
|
||||||
"get_an_enterprise_license_to_get_access_to_all_features": "获取 企业 许可证 来 访问 所有 功能。",
|
"get_an_enterprise_license_to_get_access_to_all_features": "获取 企业 许可证 来 访问 所有 功能。",
|
||||||
"keep_full_control_over_your_data_privacy_and_security": "保持 对 您 的 数据 隐私 和 安全 的 完全 控制。",
|
"keep_full_control_over_your_data_privacy_and_security": "保持 对 您 的 数据 隐私 和 安全 的 完全 控制。",
|
||||||
"license_invalid_description": "你在 ENTERPRISE_LICENSE_KEY 环境变量中填写的许可证密钥无效。请检查是否有拼写错误,或者申请一个新的密钥。",
|
|
||||||
"license_status": "许可证状态",
|
|
||||||
"license_status_active": "已激活",
|
|
||||||
"license_status_description": "你的企业许可证状态。",
|
|
||||||
"license_status_expired": "已过期",
|
|
||||||
"license_status_invalid": "许可证无效",
|
|
||||||
"license_status_unreachable": "无法访问",
|
|
||||||
"license_unreachable_grace_period": "无法连接到许可证服务器。在为期 3 天的宽限期内,你的企业功能仍然可用,宽限期将于 {gracePeriodEnd} 结束。",
|
|
||||||
"no_call_needed_no_strings_attached_request_a_free_30_day_trial_license_to_test_all_features_by_filling_out_this_form": "无需 电话 ,无需 附加 条件: 申请 免费 30 天 试用 授权以 通过 填写 此 表格 测试 所有 功能:",
|
"no_call_needed_no_strings_attached_request_a_free_30_day_trial_license_to_test_all_features_by_filling_out_this_form": "无需 电话 ,无需 附加 条件: 申请 免费 30 天 试用 授权以 通过 填写 此 表格 测试 所有 功能:",
|
||||||
"no_credit_card_no_sales_call_just_test_it": "无需信用卡 。无需销售电话 。只需测试一下 :)",
|
"no_credit_card_no_sales_call_just_test_it": "无需信用卡 。无需销售电话 。只需测试一下 :)",
|
||||||
"on_request": "按请求",
|
"on_request": "按请求",
|
||||||
"organization_roles": "组织角色(管理员,编辑,开发者等)",
|
"organization_roles": "组织角色(管理员,编辑,开发者等)",
|
||||||
"questions_please_reach_out_to": "问题 ? 请 联系",
|
"questions_please_reach_out_to": "问题 ? 请 联系",
|
||||||
"recheck_license": "重新检查许可证",
|
|
||||||
"recheck_license_failed": "许可证检查失败。许可证服务器可能无法访问。",
|
|
||||||
"recheck_license_invalid": "许可证密钥无效。请确认你的 ENTERPRISE_LICENSE_KEY。",
|
|
||||||
"recheck_license_success": "许可证检查成功",
|
|
||||||
"recheck_license_unreachable": "许可证服务器无法访问,请稍后再试。",
|
|
||||||
"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": "服务水平协议",
|
||||||
"soc2_hipaa_iso_27001_compliance_check": "SOC2 , HIPAA , ISO 27001 合规检查",
|
"soc2_hipaa_iso_27001_compliance_check": "SOC2 , HIPAA , ISO 27001 合规检查",
|
||||||
"sso": "SSO (Google 、Microsoft 、OpenID Connect)",
|
"sso": "SSO (Google 、Microsoft 、OpenID Connect)",
|
||||||
"teams": "团队 & 访问 角色(读取, 读取 & 写入, 管理)",
|
"teams": "团队 & 访问 角色(读取, 读取 & 写入, 管理)",
|
||||||
"unlock_the_full_power_of_formbricks_free_for_30_days": "解锁 Formbricks 的全部功能。免费使用 30 天。"
|
"unlock_the_full_power_of_formbricks_free_for_30_days": "解锁 Formbricks 的全部功能。免费使用 30 天。",
|
||||||
|
"your_enterprise_license_is_active_all_features_unlocked": "您的企业许可证已激活 所有功能已解锁"
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"bulk_invite_warning_description": "在免费计划中,所有组织成员都会被分配为 \"Owner \"角色。",
|
"bulk_invite_warning_description": "在免费计划中,所有组织成员都会被分配为 \"Owner \"角色。",
|
||||||
@@ -1080,7 +989,7 @@
|
|||||||
"from_your_organization": "来自你的组织",
|
"from_your_organization": "来自你的组织",
|
||||||
"invitation_sent_once_more": "再次发送邀请。",
|
"invitation_sent_once_more": "再次发送邀请。",
|
||||||
"invite_deleted_successfully": "邀请 删除 成功",
|
"invite_deleted_successfully": "邀请 删除 成功",
|
||||||
"invite_expires_on": "邀请将于 {date} 过期",
|
"invited_on": "受邀于 {date}",
|
||||||
"invites_failed": "邀请失败",
|
"invites_failed": "邀请失败",
|
||||||
"leave_organization": "离开 组织",
|
"leave_organization": "离开 组织",
|
||||||
"leave_organization_description": "您将离开此组织,并失去对所有调查和响应的访问权限。只有再次被邀请后,您才能重新加入。",
|
"leave_organization_description": "您将离开此组织,并失去对所有调查和响应的访问权限。只有再次被邀请后,您才能重新加入。",
|
||||||
@@ -1193,6 +1102,8 @@
|
|||||||
"please_fill_all_workspace_fields": "请填写所有字段以添加新工作区。",
|
"please_fill_all_workspace_fields": "请填写所有字段以添加新工作区。",
|
||||||
"read": "阅读",
|
"read": "阅读",
|
||||||
"read_write": "读 & 写",
|
"read_write": "读 & 写",
|
||||||
|
"select_member": "选择成员",
|
||||||
|
"select_workspace": "选择工作区",
|
||||||
"team_admin": "团队管理员",
|
"team_admin": "团队管理员",
|
||||||
"team_created_successfully": "团队 创建 成功",
|
"team_created_successfully": "团队 创建 成功",
|
||||||
"team_deleted_successfully": "团队 删除 成功",
|
"team_deleted_successfully": "团队 删除 成功",
|
||||||
@@ -1242,6 +1153,7 @@
|
|||||||
"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": "添加 选项",
|
||||||
@@ -1280,7 +1192,6 @@
|
|||||||
"block_duplicated": "区块已复制。",
|
"block_duplicated": "区块已复制。",
|
||||||
"bold": "粗体",
|
"bold": "粗体",
|
||||||
"brand_color": "品牌 颜色",
|
"brand_color": "品牌 颜色",
|
||||||
"brand_color_description": "应用于按钮、链接和高亮部分。",
|
|
||||||
"brightness": "亮度",
|
"brightness": "亮度",
|
||||||
"bulk_edit": "批量编辑",
|
"bulk_edit": "批量编辑",
|
||||||
"bulk_edit_description": "编辑以下所有选项,每行一个。空行将被跳过,重复项将被移除。",
|
"bulk_edit_description": "编辑以下所有选项,每行一个。空行将被跳过,重复项将被移除。",
|
||||||
@@ -1298,9 +1209,7 @@
|
|||||||
"capture_new_action": "捕获 新动作",
|
"capture_new_action": "捕获 新动作",
|
||||||
"card_arrangement_for_survey_type_derived": "{surveyTypeDerived} 调查 的 卡片 布局",
|
"card_arrangement_for_survey_type_derived": "{surveyTypeDerived} 调查 的 卡片 布局",
|
||||||
"card_background_color": "卡片 的 背景 颜色",
|
"card_background_color": "卡片 的 背景 颜色",
|
||||||
"card_background_color_description": "填充调查卡区域。",
|
|
||||||
"card_border_color": "卡片 的 边框 颜色",
|
"card_border_color": "卡片 的 边框 颜色",
|
||||||
"card_border_color_description": "勾勒调查卡边框。",
|
|
||||||
"card_styling": "卡片样式",
|
"card_styling": "卡片样式",
|
||||||
"casual": "休闲",
|
"casual": "休闲",
|
||||||
"caution_edit_duplicate": "复制 并 编辑",
|
"caution_edit_duplicate": "复制 并 编辑",
|
||||||
@@ -1311,12 +1220,20 @@
|
|||||||
"caution_explanation_responses_are_safe": "旧 与 新 的 回复 混合 , 这 可能 导致 数据 总结 有误 。",
|
"caution_explanation_responses_are_safe": "旧 与 新 的 回复 混合 , 这 可能 导致 数据 总结 有误 。",
|
||||||
"caution_recommendation": "这 可能 会 导致 调查 统计 数据 的 不一致 。 我们 建议 复制 调查 。",
|
"caution_recommendation": "这 可能 会 导致 调查 统计 数据 的 不一致 。 我们 建议 复制 调查 。",
|
||||||
"caution_text": "更改 会导致 不一致",
|
"caution_text": "更改 会导致 不一致",
|
||||||
|
"centered_modal_overlay_color": "居中 模态遮罩层颜色",
|
||||||
"change_anyway": "还是更改",
|
"change_anyway": "还是更改",
|
||||||
"change_background": "更改 背景",
|
"change_background": "更改 背景",
|
||||||
"change_question_type": "更改 问题类型",
|
"change_question_type": "更改 问题类型",
|
||||||
"change_survey_type": "更改 调查 类型 会影 响 现有 访问",
|
"change_survey_type": "更改 调查 类型 会影 响 现有 访问",
|
||||||
|
"change_the_background_color_of_the_card": "更改 卡片 的 背景 颜色",
|
||||||
|
"change_the_background_color_of_the_input_fields": "更改 输入字段 的 背景颜色",
|
||||||
"change_the_background_to_a_color_image_or_animation": "将 背景 更改为 颜色 、 图像 或 动画。",
|
"change_the_background_to_a_color_image_or_animation": "将 背景 更改为 颜色 、 图像 或 动画。",
|
||||||
|
"change_the_border_color_of_the_card": "更改 卡片 的 边框 颜色",
|
||||||
|
"change_the_border_color_of_the_input_fields": "更改 输入字段 的边框颜色。",
|
||||||
|
"change_the_border_radius_of_the_card_and_the_inputs": "更改 卡片 和 输入 的 边框 半径",
|
||||||
|
"change_the_brand_color_of_the_survey": "更改调查的品牌颜色",
|
||||||
"change_the_placement_of_this_survey": "更改 此 调查 的 放置。",
|
"change_the_placement_of_this_survey": "更改 此 调查 的 放置。",
|
||||||
|
"change_the_question_color_of_the_survey": "更改调查的 问题颜色",
|
||||||
"changes_saved": "更改 已 保存",
|
"changes_saved": "更改 已 保存",
|
||||||
"changing_survey_type_will_remove_existing_distribution_channels": "更改 调查 类型 会影 响 分享 方式 。 如果 受访者 已经 拥有 当前 类型 的 访问 链接 , 在 更改 之后 ,他们 可能 会 失去 访问 权限 。",
|
"changing_survey_type_will_remove_existing_distribution_channels": "更改 调查 类型 会影 响 分享 方式 。 如果 受访者 已经 拥有 当前 类型 的 访问 链接 , 在 更改 之后 ,他们 可能 会 失去 访问 权限 。",
|
||||||
"checkbox_label": "复选框 标签",
|
"checkbox_label": "复选框 标签",
|
||||||
@@ -1456,6 +1373,7 @@
|
|||||||
"hide_progress_bar": "隐藏 进度 条",
|
"hide_progress_bar": "隐藏 进度 条",
|
||||||
"hide_question_settings": "隐藏问题设置",
|
"hide_question_settings": "隐藏问题设置",
|
||||||
"hostname": "主 机 名",
|
"hostname": "主 机 名",
|
||||||
|
"how_funky_do_you_want_your_cards_in_survey_type_derived_surveys": "在 {surveyTypeDerived} 调查 中,您 想要 卡片 多么 有趣",
|
||||||
"if_you_need_more_please": "如果您需要更多,请",
|
"if_you_need_more_please": "如果您需要更多,请",
|
||||||
"if_you_really_want_that_answer_ask_until_you_get_it": "每次触发时都会显示,直到提交回应为止。",
|
"if_you_really_want_that_answer_ask_until_you_get_it": "每次触发时都会显示,直到提交回应为止。",
|
||||||
"ignore_global_waiting_time": "忽略冷却期",
|
"ignore_global_waiting_time": "忽略冷却期",
|
||||||
@@ -1466,9 +1384,7 @@
|
|||||||
"initial_value": "初始 值",
|
"initial_value": "初始 值",
|
||||||
"inner_text": "内文",
|
"inner_text": "内文",
|
||||||
"input_border_color": "输入 边框 颜色",
|
"input_border_color": "输入 边框 颜色",
|
||||||
"input_border_color_description": "勾勒文本输入框和多行文本框的边框。",
|
|
||||||
"input_color": "输入颜色",
|
"input_color": "输入颜色",
|
||||||
"input_color_description": "填充文本输入框内部。",
|
|
||||||
"insert_link": "插入 链接",
|
"insert_link": "插入 链接",
|
||||||
"invalid_targeting": "无效的目标: 请检查 您 的受众过滤器",
|
"invalid_targeting": "无效的目标: 请检查 您 的受众过滤器",
|
||||||
"invalid_video_url_warning": "请输入有效的 YouTube、Vimeo 或 Loom URL 。我们目前不支持其他 视频 托管服务提供商。",
|
"invalid_video_url_warning": "请输入有效的 YouTube、Vimeo 或 Loom URL 。我们目前不支持其他 视频 托管服务提供商。",
|
||||||
@@ -1552,12 +1468,13 @@
|
|||||||
"protect_survey_with_pin_description": "只有 拥有 PIN 的 用户 可以 访问 调查。",
|
"protect_survey_with_pin_description": "只有 拥有 PIN 的 用户 可以 访问 调查。",
|
||||||
"publish": "发布",
|
"publish": "发布",
|
||||||
"question": "问题",
|
"question": "问题",
|
||||||
|
"question_color": "问题颜色",
|
||||||
"question_deleted": "问题 已删除",
|
"question_deleted": "问题 已删除",
|
||||||
"question_duplicated": "问题重复。",
|
"question_duplicated": "问题重复。",
|
||||||
"question_id_updated": "问题 ID 更新",
|
"question_id_updated": "问题 ID 更新",
|
||||||
"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}\" 配额 使用",
|
||||||
"question_used_in_recall": "此问题正在召回于问题 {questionIndex}。",
|
"question_used_in_recall": "此问题正在召回于问题 {questionIndex}。",
|
||||||
"question_used_in_recall_ending_card": "此 问题 正在召回于结束 卡片。",
|
"question_used_in_recall_ending_card": "此 问题 正在召回于结束 卡片。",
|
||||||
"quotas": {
|
"quotas": {
|
||||||
@@ -1613,7 +1530,6 @@
|
|||||||
"response_limits_redirections_and_more": "响应 限制 、 重定向 和 更多 。",
|
"response_limits_redirections_and_more": "响应 限制 、 重定向 和 更多 。",
|
||||||
"response_options": "响应 选项",
|
"response_options": "响应 选项",
|
||||||
"roundness": "圆度",
|
"roundness": "圆度",
|
||||||
"roundness_description": "控制卡片角的圆润程度。",
|
|
||||||
"row_used_in_logic_error": "\"这个 行 在 问题 {questionIndex} 的 逻辑 中 使用。请 先 从 逻辑 中 删除 它。\"",
|
"row_used_in_logic_error": "\"这个 行 在 问题 {questionIndex} 的 逻辑 中 使用。请 先 从 逻辑 中 删除 它。\"",
|
||||||
"rows": "行",
|
"rows": "行",
|
||||||
"save_and_close": "保存 和 关闭",
|
"save_and_close": "保存 和 关闭",
|
||||||
@@ -1655,6 +1571,7 @@
|
|||||||
"styling_set_to_theme_styles": "样式 设置 为 主题 风格",
|
"styling_set_to_theme_styles": "样式 设置 为 主题 风格",
|
||||||
"subheading": "子标题",
|
"subheading": "子标题",
|
||||||
"subtract": "减 -",
|
"subtract": "减 -",
|
||||||
|
"suggest_colors": "建议颜色",
|
||||||
"survey_completed_heading": "调查 完成",
|
"survey_completed_heading": "调查 完成",
|
||||||
"survey_completed_subheading": "此 免费 & 开源 调查 已 关闭",
|
"survey_completed_subheading": "此 免费 & 开源 调查 已 关闭",
|
||||||
"survey_display_settings": "调查显示设置",
|
"survey_display_settings": "调查显示设置",
|
||||||
@@ -1731,7 +1648,7 @@
|
|||||||
"validation_rules": "校验规则",
|
"validation_rules": "校验规则",
|
||||||
"validation_rules_description": "仅接受符合以下条件的回复",
|
"validation_rules_description": "仅接受符合以下条件的回复",
|
||||||
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "\"{variable} 在 问题 {questionIndex} 的 逻辑 中 使用。请 先 从 逻辑 中 删除 它。\"",
|
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "\"{variable} 在 问题 {questionIndex} 的 逻辑 中 使用。请 先 从 逻辑 中 删除 它。\"",
|
||||||
"variable_is_used_in_quota_please_remove_it_from_quota_first": "变量“{variableName}”正在被“{quotaName}”配额使用,请先将其从配额中移除",
|
"variable_is_used_in_quota_please_remove_it_from_quota_first": "变量 \"{variableName}\" 正在 被 \"{quotaName}\" 配额 使用",
|
||||||
"variable_name_conflicts_with_hidden_field": "变量名与已有的隐藏字段 ID 冲突。",
|
"variable_name_conflicts_with_hidden_field": "变量名与已有的隐藏字段 ID 冲突。",
|
||||||
"variable_name_is_already_taken_please_choose_another": "变量名已被占用,请选择其他。",
|
"variable_name_is_already_taken_please_choose_another": "变量名已被占用,请选择其他。",
|
||||||
"variable_name_must_start_with_a_letter": "变量名 必须 以字母开头。",
|
"variable_name_must_start_with_a_letter": "变量名 必须 以字母开头。",
|
||||||
@@ -1957,7 +1874,6 @@
|
|||||||
"filtered_responses_excel": "过滤 反馈 (Excel)",
|
"filtered_responses_excel": "过滤 反馈 (Excel)",
|
||||||
"generating_qr_code": "正在生成二维码",
|
"generating_qr_code": "正在生成二维码",
|
||||||
"impressions": "印象",
|
"impressions": "印象",
|
||||||
"impressions_identified_only": "仅显示已识别联系人的展示次数",
|
|
||||||
"impressions_tooltip": "调查 被 查看 的 次数",
|
"impressions_tooltip": "调查 被 查看 的 次数",
|
||||||
"in_app": {
|
"in_app": {
|
||||||
"connection_description": "调查将显示给符合以下条件的您网站用户",
|
"connection_description": "调查将显示给符合以下条件的您网站用户",
|
||||||
@@ -2000,7 +1916,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": "整体",
|
||||||
@@ -2140,71 +2055,9 @@
|
|||||||
"look": {
|
"look": {
|
||||||
"add_background_color": "添加背景色",
|
"add_background_color": "添加背景色",
|
||||||
"add_background_color_description": "为 logo 容器添加背景色。",
|
"add_background_color_description": "为 logo 容器添加背景色。",
|
||||||
"advanced_styling_field_border_radius": "边框圆角",
|
|
||||||
"advanced_styling_field_button_bg": "按钮背景",
|
|
||||||
"advanced_styling_field_button_bg_description": "填充“下一步/提交”按钮。",
|
|
||||||
"advanced_styling_field_button_border_radius_description": "设置按钮圆角。",
|
|
||||||
"advanced_styling_field_button_font_size_description": "调整按钮标签文字大小。",
|
|
||||||
"advanced_styling_field_button_font_weight_description": "设置按钮文字的粗细。",
|
|
||||||
"advanced_styling_field_button_height_description": "控制按钮高度。",
|
|
||||||
"advanced_styling_field_button_padding_x_description": "增加左右间距。",
|
|
||||||
"advanced_styling_field_button_padding_y_description": "增加上下间距。",
|
|
||||||
"advanced_styling_field_button_text": "按钮文字",
|
|
||||||
"advanced_styling_field_button_text_description": "设置按钮内标签的颜色。",
|
|
||||||
"advanced_styling_field_description_color": "描述颜色",
|
|
||||||
"advanced_styling_field_description_color_description": "设置每个标题下方文字的颜色。",
|
|
||||||
"advanced_styling_field_description_size": "描述字体大小",
|
|
||||||
"advanced_styling_field_description_size_description": "调整描述文字大小。",
|
|
||||||
"advanced_styling_field_description_weight": "描述字体粗细",
|
|
||||||
"advanced_styling_field_description_weight_description": "设置描述文字的粗细。",
|
|
||||||
"advanced_styling_field_font_size": "字体大小",
|
|
||||||
"advanced_styling_field_font_weight": "字体粗细",
|
|
||||||
"advanced_styling_field_headline_color": "标题颜色",
|
|
||||||
"advanced_styling_field_headline_color_description": "设置主问题文字的颜色。",
|
|
||||||
"advanced_styling_field_headline_size": "标题字体大小",
|
|
||||||
"advanced_styling_field_headline_size_description": "调整主标题文字大小。",
|
|
||||||
"advanced_styling_field_headline_weight": "标题字体粗细",
|
|
||||||
"advanced_styling_field_headline_weight_description": "设置主标题文字的粗细。",
|
|
||||||
"advanced_styling_field_height": "最小高度",
|
|
||||||
"advanced_styling_field_indicator_bg": "指示器背景",
|
|
||||||
"advanced_styling_field_indicator_bg_description": "设置进度条已填充部分的颜色。",
|
|
||||||
"advanced_styling_field_input_border_radius_description": "设置输入框圆角。",
|
|
||||||
"advanced_styling_field_input_font_size_description": "调整输入框内文字大小。",
|
|
||||||
"advanced_styling_field_input_height_description": "设置输入框的最小高度。",
|
|
||||||
"advanced_styling_field_input_padding_x_description": "增加输入框左右间距。",
|
|
||||||
"advanced_styling_field_input_padding_y_description": "为输入框上下添加间距。",
|
|
||||||
"advanced_styling_field_input_placeholder_opacity_description": "调整占位提示文字的透明度。",
|
|
||||||
"advanced_styling_field_input_shadow_description": "为输入框添加投影效果。",
|
|
||||||
"advanced_styling_field_input_text": "输入文字",
|
|
||||||
"advanced_styling_field_input_text_description": "设置输入框内已输入文字的颜色。",
|
|
||||||
"advanced_styling_field_option_bg": "背景色",
|
|
||||||
"advanced_styling_field_option_bg_description": "设置选项项的背景色。",
|
|
||||||
"advanced_styling_field_option_border_radius_description": "设置选项的圆角。",
|
|
||||||
"advanced_styling_field_option_font_size_description": "调整选项标签文字的大小。",
|
|
||||||
"advanced_styling_field_option_label": "标签颜色",
|
|
||||||
"advanced_styling_field_option_label_description": "设置选项标签文字的颜色。",
|
|
||||||
"advanced_styling_field_option_padding_x_description": "为选项左右添加间距。",
|
|
||||||
"advanced_styling_field_option_padding_y_description": "为选项上下添加间距。",
|
|
||||||
"advanced_styling_field_padding_x": "横向内边距",
|
|
||||||
"advanced_styling_field_padding_y": "纵向内边距",
|
|
||||||
"advanced_styling_field_placeholder_opacity": "占位符透明度",
|
|
||||||
"advanced_styling_field_shadow": "阴影",
|
|
||||||
"advanced_styling_field_track_bg": "轨道背景",
|
|
||||||
"advanced_styling_field_track_bg_description": "设置进度条未填充部分的颜色。",
|
|
||||||
"advanced_styling_field_track_height": "轨道高度",
|
|
||||||
"advanced_styling_field_track_height_description": "控制进度条的粗细。",
|
|
||||||
"advanced_styling_field_upper_label_color": "标题标签颜色",
|
|
||||||
"advanced_styling_field_upper_label_color_description": "设置输入框上方小标签的颜色。",
|
|
||||||
"advanced_styling_field_upper_label_size": "标题标签字体大小",
|
|
||||||
"advanced_styling_field_upper_label_size_description": "调整输入框上方小标签的大小。",
|
|
||||||
"advanced_styling_field_upper_label_weight": "标题标签字体粗细",
|
|
||||||
"advanced_styling_field_upper_label_weight_description": "设置标签文字的粗细。",
|
|
||||||
"advanced_styling_section_buttons": "按钮",
|
|
||||||
"advanced_styling_section_headlines": "标题和描述",
|
|
||||||
"advanced_styling_section_inputs": "输入项",
|
|
||||||
"advanced_styling_section_options": "选项(单选/多选)",
|
|
||||||
"app_survey_placement": "应用调查放置位置",
|
"app_survey_placement": "应用调查放置位置",
|
||||||
"app_survey_placement_settings_description": "更改调查在您的 Web 应用或网站中显示的位置。",
|
"app_survey_placement_settings_description": "更改调查在您的 Web 应用或网站中显示的位置。",
|
||||||
|
"centered_modal_overlay_color": "居中模态遮罩层颜色",
|
||||||
"email_customization": "邮件自定义",
|
"email_customization": "邮件自定义",
|
||||||
"email_customization_description": "更改 Formbricks 代表您发送邮件的外观和风格。",
|
"email_customization_description": "更改 Formbricks 代表您发送邮件的外观和风格。",
|
||||||
"enable_custom_styling": "启用自定义样式",
|
"enable_custom_styling": "启用自定义样式",
|
||||||
@@ -2215,9 +2068,6 @@
|
|||||||
"formbricks_branding_hidden": "Formbricks 品牌标识已隐藏。",
|
"formbricks_branding_hidden": "Formbricks 品牌标识已隐藏。",
|
||||||
"formbricks_branding_settings_description": "我们很感谢您的支持,但如果您关闭它,我们也能理解。",
|
"formbricks_branding_settings_description": "我们很感谢您的支持,但如果您关闭它,我们也能理解。",
|
||||||
"formbricks_branding_shown": "Formbricks 品牌标识已显示。",
|
"formbricks_branding_shown": "Formbricks 品牌标识已显示。",
|
||||||
"generate_theme_btn": "生成",
|
|
||||||
"generate_theme_confirmation": "要根据你的品牌色生成一个匹配的配色主题吗?这将覆盖你当前的颜色设置。",
|
|
||||||
"generate_theme_header": "生成配色主题?",
|
|
||||||
"logo_removed_successfully": "logo 移除成功",
|
"logo_removed_successfully": "logo 移除成功",
|
||||||
"logo_settings_description": "上传您的公司 logo,用于品牌调查和链接预览。",
|
"logo_settings_description": "上传您的公司 logo,用于品牌调查和链接预览。",
|
||||||
"logo_updated_successfully": "logo 更新成功",
|
"logo_updated_successfully": "logo 更新成功",
|
||||||
@@ -2232,8 +2082,6 @@
|
|||||||
"show_formbricks_branding_in": "在 {type} 调查中显示 Formbricks 品牌标识",
|
"show_formbricks_branding_in": "在 {type} 调查中显示 Formbricks 品牌标识",
|
||||||
"show_powered_by_formbricks": "显示“Powered by Formbricks”标识",
|
"show_powered_by_formbricks": "显示“Powered by Formbricks”标识",
|
||||||
"styling_updated_successfully": "样式更新成功",
|
"styling_updated_successfully": "样式更新成功",
|
||||||
"suggest_colors": "推荐颜色",
|
|
||||||
"suggested_colors_applied_please_save": "已成功生成推荐配色。请点击“保存”以保留更改。",
|
|
||||||
"theme": "主题",
|
"theme": "主题",
|
||||||
"theme_settings_description": "为所有问卷创建一个样式主题。你可以为每个问卷启用自定义样式。"
|
"theme_settings_description": "为所有问卷创建一个样式主题。你可以为每个问卷启用自定义样式。"
|
||||||
},
|
},
|
||||||
@@ -2997,7 +2845,6 @@
|
|||||||
"preview_survey_question_2_choice_1_label": "是 , 保持我 更新 。",
|
"preview_survey_question_2_choice_1_label": "是 , 保持我 更新 。",
|
||||||
"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_welcome_card_headline": "欢迎!",
|
"preview_survey_welcome_card_headline": "欢迎!",
|
||||||
"prioritize_features_description": "确定 用户 最 需要 和 最 不 需要 的 功能。",
|
"prioritize_features_description": "确定 用户 最 需要 和 最 不 需要 的 功能。",
|
||||||
"prioritize_features_name": "优先 功能",
|
"prioritize_features_name": "优先 功能",
|
||||||
|
|||||||
@@ -188,7 +188,6 @@
|
|||||||
"customer_success": "客戶成功",
|
"customer_success": "客戶成功",
|
||||||
"dark_overlay": "深色覆蓋",
|
"dark_overlay": "深色覆蓋",
|
||||||
"date": "日期",
|
"date": "日期",
|
||||||
"days": "天",
|
|
||||||
"default": "預設",
|
"default": "預設",
|
||||||
"delete": "刪除",
|
"delete": "刪除",
|
||||||
"description": "描述",
|
"description": "描述",
|
||||||
@@ -218,7 +217,6 @@
|
|||||||
"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": "展開列",
|
||||||
@@ -277,7 +275,6 @@
|
|||||||
"mobile_overlay_app_works_best_on_desktop": "Formbricks 適合在大螢幕上使用。若要管理或建立問卷,請切換到其他裝置。",
|
"mobile_overlay_app_works_best_on_desktop": "Formbricks 適合在大螢幕上使用。若要管理或建立問卷,請切換到其他裝置。",
|
||||||
"mobile_overlay_surveys_look_good": "別擔心 -你的 問卷 在每個 裝置 和 螢幕尺寸 上 都 很出色!",
|
"mobile_overlay_surveys_look_good": "別擔心 -你的 問卷 在每個 裝置 和 螢幕尺寸 上 都 很出色!",
|
||||||
"mobile_overlay_title": "糟糕 ,偵測到小螢幕!",
|
"mobile_overlay_title": "糟糕 ,偵測到小螢幕!",
|
||||||
"months": "月",
|
|
||||||
"move_down": "下移",
|
"move_down": "下移",
|
||||||
"move_up": "上移",
|
"move_up": "上移",
|
||||||
"multiple_languages": "多種語言",
|
"multiple_languages": "多種語言",
|
||||||
@@ -288,7 +285,6 @@
|
|||||||
"no_background_image_found": "找不到背景圖片。",
|
"no_background_image_found": "找不到背景圖片。",
|
||||||
"no_code": "無程式碼",
|
"no_code": "無程式碼",
|
||||||
"no_files_uploaded": "沒有上傳任何檔案",
|
"no_files_uploaded": "沒有上傳任何檔案",
|
||||||
"no_overlay": "無覆蓋層",
|
|
||||||
"no_quotas_found": "找不到 配額",
|
"no_quotas_found": "找不到 配額",
|
||||||
"no_result_found": "找不到結果",
|
"no_result_found": "找不到結果",
|
||||||
"no_results": "沒有結果",
|
"no_results": "沒有結果",
|
||||||
@@ -315,7 +311,6 @@
|
|||||||
"organization_teams_not_found": "找不到組織團隊",
|
"organization_teams_not_found": "找不到組織團隊",
|
||||||
"other": "其他",
|
"other": "其他",
|
||||||
"others": "其他",
|
"others": "其他",
|
||||||
"overlay_color": "覆蓋層顏色",
|
|
||||||
"overview": "概覽",
|
"overview": "概覽",
|
||||||
"password": "密碼",
|
"password": "密碼",
|
||||||
"paused": "已暫停",
|
"paused": "已暫停",
|
||||||
@@ -355,7 +350,6 @@
|
|||||||
"request_trial_license": "請求試用授權",
|
"request_trial_license": "請求試用授權",
|
||||||
"reset_to_default": "重設為預設值",
|
"reset_to_default": "重設為預設值",
|
||||||
"response": "回應",
|
"response": "回應",
|
||||||
"response_id": "回應 ID",
|
|
||||||
"responses": "回應",
|
"responses": "回應",
|
||||||
"restart": "重新開始",
|
"restart": "重新開始",
|
||||||
"role": "角色",
|
"role": "角色",
|
||||||
@@ -396,7 +390,6 @@
|
|||||||
"status": "狀態",
|
"status": "狀態",
|
||||||
"step_by_step_manual": "逐步手冊",
|
"step_by_step_manual": "逐步手冊",
|
||||||
"storage_not_configured": "檔案儲存未設定,上傳可能會失敗",
|
"storage_not_configured": "檔案儲存未設定,上傳可能會失敗",
|
||||||
"string": "文字",
|
|
||||||
"styling": "樣式設定",
|
"styling": "樣式設定",
|
||||||
"submit": "提交",
|
"submit": "提交",
|
||||||
"summary": "摘要",
|
"summary": "摘要",
|
||||||
@@ -429,7 +422,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": "已更新",
|
||||||
@@ -453,7 +445,6 @@
|
|||||||
"website_and_app_connection": "網站與應用程式連線",
|
"website_and_app_connection": "網站與應用程式連線",
|
||||||
"website_app_survey": "網站與應用程式問卷",
|
"website_app_survey": "網站與應用程式問卷",
|
||||||
"website_survey": "網站問卷",
|
"website_survey": "網站問卷",
|
||||||
"weeks": "週",
|
|
||||||
"welcome_card": "歡迎卡片",
|
"welcome_card": "歡迎卡片",
|
||||||
"workspace_configuration": "工作區設定",
|
"workspace_configuration": "工作區設定",
|
||||||
"workspace_created_successfully": "工作區已成功建立",
|
"workspace_created_successfully": "工作區已成功建立",
|
||||||
@@ -464,7 +455,6 @@
|
|||||||
"workspace_not_found": "找不到工作區",
|
"workspace_not_found": "找不到工作區",
|
||||||
"workspace_permission_not_found": "找不到工作區權限",
|
"workspace_permission_not_found": "找不到工作區權限",
|
||||||
"workspaces": "工作區",
|
"workspaces": "工作區",
|
||||||
"years": "年",
|
|
||||||
"you": "您",
|
"you": "您",
|
||||||
"you_are_downgraded_to_the_community_edition": "您已降級至社群版。",
|
"you_are_downgraded_to_the_community_edition": "您已降級至社群版。",
|
||||||
"you_are_not_authorized_to_perform_this_action": "您沒有執行此操作的權限。",
|
"you_are_not_authorized_to_perform_this_action": "您沒有執行此操作的權限。",
|
||||||
@@ -625,7 +615,7 @@
|
|||||||
"attribute_created_successfully": "屬性建立成功",
|
"attribute_created_successfully": "屬性建立成功",
|
||||||
"attribute_description": "描述",
|
"attribute_description": "描述",
|
||||||
"attribute_description_placeholder": "簡短描述",
|
"attribute_description_placeholder": "簡短描述",
|
||||||
"attribute_key": "金鑰",
|
"attribute_key": "屬性",
|
||||||
"attribute_key_cannot_be_changed": "建立後無法變更金鑰",
|
"attribute_key_cannot_be_changed": "建立後無法變更金鑰",
|
||||||
"attribute_key_hint": "僅限小寫字母、數字和底線,且必須以字母開頭。",
|
"attribute_key_hint": "僅限小寫字母、數字和底線,且必須以字母開頭。",
|
||||||
"attribute_key_placeholder": "例如:date_of_birth",
|
"attribute_key_placeholder": "例如:date_of_birth",
|
||||||
@@ -636,45 +626,28 @@
|
|||||||
"attribute_updated_successfully": "屬性更新成功",
|
"attribute_updated_successfully": "屬性更新成功",
|
||||||
"attribute_value": "值",
|
"attribute_value": "值",
|
||||||
"attribute_value_placeholder": "屬性值",
|
"attribute_value_placeholder": "屬性值",
|
||||||
"attributes_msg_attribute_limit_exceeded": "無法建立 {count} 個新屬性,因為這樣會超過 {limit} 個屬性類別的上限。現有屬性已成功更新。",
|
|
||||||
"attributes_msg_attribute_type_validation_error": "{error}(屬性「{key}」的資料型別為:{dataType})",
|
|
||||||
"attributes_msg_email_already_exists": "此環境已存在該 email,未進行更新。",
|
|
||||||
"attributes_msg_email_or_userid_required": "必須提供 email 或 userId。已保留現有值。",
|
|
||||||
"attributes_msg_new_attribute_created": "已建立新屬性「{key}」,型別為「{dataType}」",
|
|
||||||
"attributes_msg_userid_already_exists": "此環境已存在該 userId,未進行更新。",
|
|
||||||
"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": "自訂屬性",
|
|
||||||
"data_type": "資料型態",
|
|
||||||
"data_type_cannot_be_changed": "建立後無法變更資料型態",
|
|
||||||
"data_type_description": "選擇此屬性要如何儲存與篩選",
|
|
||||||
"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 {這將刪除與這個 contact 相關的所有調查響應和聯繫人屬性。基於這個 contact 數據的任何定向和個性化功能將會丟失。如果這個 contact 有作為調查配額依據的響應,配額計數將會減少,但配額限制將保持不變。} other {這將刪除與這些 contacts 相關的所有調查響應和聯繫人屬性。基於這些 contacts 數據的任何定向和個性化功能將會丟失。如果這些 contacts 有作為調查配額依據的響應,配額計數將會減少,但配額限制將保持不變。}}",
|
"delete_contact_confirmation_with_quotas": "{value, plural, one {這將刪除與這個 contact 相關的所有調查響應和聯繫人屬性。基於這個 contact 數據的任何定向和個性化功能將會丟失。如果這個 contact 有作為調查配額依據的響應,配額計數將會減少,但配額限制將保持不變。} other {這將刪除與這些 contacts 相關的所有調查響應和聯繫人屬性。基於這些 contacts 數據的任何定向和個性化功能將會丟失。如果這些 contacts 有作為調查配額依據的響應,配額計數將會減少,但配額限制將保持不變。}}",
|
||||||
"displays": "顯示次數",
|
|
||||||
"edit_attribute": "編輯屬性",
|
"edit_attribute": "編輯屬性",
|
||||||
"edit_attribute_description": "更新此屬性的標籤與描述。",
|
"edit_attribute_description": "更新此屬性的標籤與描述。",
|
||||||
"edit_attribute_values": "編輯屬性",
|
"edit_attribute_values": "編輯屬性",
|
||||||
"edit_attribute_values_description": "變更此聯絡人特定屬性的值。",
|
"edit_attribute_values_description": "變更此聯絡人特定屬性的值。",
|
||||||
"edit_attributes": "編輯屬性",
|
|
||||||
"edit_attributes_success": "聯絡人屬性已成功更新",
|
"edit_attributes_success": "聯絡人屬性已成功更新",
|
||||||
"generate_personal_link": "產生個人連結",
|
"generate_personal_link": "產生個人連結",
|
||||||
"generate_personal_link_description": "選擇一個已發佈的問卷,為此聯絡人產生個人化連結。",
|
"generate_personal_link_description": "選擇一個已發佈的問卷,為此聯絡人產生個人化連結。",
|
||||||
"invalid_csv_column_names": "無效的 CSV 欄位名稱:{columns}。作為新屬性的欄位名稱只能包含小寫字母、數字和底線,且必須以字母開頭。",
|
|
||||||
"invalid_date_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": "找不到回應",
|
||||||
"not_provided": "未提供",
|
"not_provided": "未提供",
|
||||||
"number_value_required": "必須填寫數字值。如要移除此屬性,請使用刪除按鈕。",
|
|
||||||
"personal_link_generated": "個人連結已成功產生",
|
"personal_link_generated": "個人連結已成功產生",
|
||||||
"personal_link_generated_but_clipboard_failed": "已生成個人連結,但無法複製到剪貼簿:{url}",
|
"personal_link_generated_but_clipboard_failed": "已生成個人連結,但無法複製到剪貼簿:{url}",
|
||||||
"personal_survey_link": "個人調查連結",
|
"personal_survey_link": "個人調查連結",
|
||||||
@@ -683,24 +656,13 @@
|
|||||||
"search_contact": "搜尋聯絡人",
|
"search_contact": "搜尋聯絡人",
|
||||||
"select_a_survey": "選擇問卷",
|
"select_a_survey": "選擇問卷",
|
||||||
"select_attribute": "選取屬性",
|
"select_attribute": "選取屬性",
|
||||||
"select_attribute_key": "選取屬性鍵值",
|
|
||||||
"survey_viewed": "已查看問卷",
|
|
||||||
"survey_viewed_at": "查看時間",
|
|
||||||
"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_duplicate_mappings": "以下屬性有重複對應:{attributes}",
|
|
||||||
"upload_contacts_error_file_too_large": "檔案大小超過 800KB 的上限",
|
|
||||||
"upload_contacts_error_generic": "上傳聯絡人時發生錯誤,請稍後再試。",
|
|
||||||
"upload_contacts_error_invalid_file_type": "請上傳 CSV 檔案",
|
|
||||||
"upload_contacts_error_no_valid_contacts": "上傳的 CSV 檔案中沒有任何有效的聯絡人,請參考範例 CSV 檔案以取得正確格式。",
|
|
||||||
"upload_contacts_modal_attribute_header": "Formbricks 屬性",
|
|
||||||
"upload_contacts_modal_attributes_description": "將 CSV 中的欄位對應到 Formbricks 中的屬性。",
|
"upload_contacts_modal_attributes_description": "將 CSV 中的欄位對應到 Formbricks 中的屬性。",
|
||||||
"upload_contacts_modal_attributes_new": "新增屬性",
|
"upload_contacts_modal_attributes_new": "新增屬性",
|
||||||
"upload_contacts_modal_attributes_search_or_add": "搜尋或新增屬性",
|
"upload_contacts_modal_attributes_search_or_add": "搜尋或新增屬性",
|
||||||
|
"upload_contacts_modal_attributes_should_be_mapped_to": "應對應到",
|
||||||
"upload_contacts_modal_attributes_title": "屬性",
|
"upload_contacts_modal_attributes_title": "屬性",
|
||||||
"upload_contacts_modal_csv_column_header": "CSV 欄位",
|
|
||||||
"upload_contacts_modal_description": "上傳 CSV 以快速匯入具有屬性的聯絡人",
|
"upload_contacts_modal_description": "上傳 CSV 以快速匯入具有屬性的聯絡人",
|
||||||
"upload_contacts_modal_download_example_csv": "下載範例 CSV",
|
"upload_contacts_modal_download_example_csv": "下載範例 CSV",
|
||||||
"upload_contacts_modal_duplicates_description": "如果聯絡人已存在於您的聯絡人中,我們應該如何處理?",
|
"upload_contacts_modal_duplicates_description": "如果聯絡人已存在於您的聯絡人中,我們應該如何處理?",
|
||||||
@@ -757,12 +719,7 @@
|
|||||||
"link_google_sheet": "連結 Google 試算表",
|
"link_google_sheet": "連結 Google 試算表",
|
||||||
"link_new_sheet": "連結新試算表",
|
"link_new_sheet": "連結新試算表",
|
||||||
"no_integrations_yet": "您的 Google 試算表整合將在您新增後立即顯示在此處。⏲️",
|
"no_integrations_yet": "您的 Google 試算表整合將在您新增後立即顯示在此處。⏲️",
|
||||||
"reconnect_button": "重新連線",
|
"spreadsheet_url": "試算表網址"
|
||||||
"reconnect_button_description": "你的 Google Sheets 連線已過期。請重新連線以繼續同步回應。你現有的試算表連結和資料都會被保留。",
|
|
||||||
"reconnect_button_tooltip": "重新連線整合以刷新存取權限。你現有的試算表連結和資料都會被保留。",
|
|
||||||
"spreadsheet_permission_error": "你沒有權限存取這個試算表。請確認該試算表已與你的 Google 帳戶分享,且你擁有寫入權限。",
|
|
||||||
"spreadsheet_url": "試算表網址",
|
|
||||||
"token_expired_error": "Google Sheets 的刷新權杖已過期或被撤銷。請重新連線整合。"
|
|
||||||
},
|
},
|
||||||
"include_created_at": "包含建立於",
|
"include_created_at": "包含建立於",
|
||||||
"include_hidden_fields": "包含隱藏欄位",
|
"include_hidden_fields": "包含隱藏欄位",
|
||||||
@@ -886,40 +843,6 @@
|
|||||||
"no_attributes_yet": "尚無屬性!",
|
"no_attributes_yet": "尚無屬性!",
|
||||||
"no_filters_yet": "尚無篩選器!",
|
"no_filters_yet": "尚無篩選器!",
|
||||||
"no_segments_yet": "您目前沒有已儲存的區隔。",
|
"no_segments_yet": "您目前沒有已儲存的區隔。",
|
||||||
"operator_contains": "包含",
|
|
||||||
"operator_does_not_contain": "不包含",
|
|
||||||
"operator_ends_with": "結尾為",
|
|
||||||
"operator_is_after": "在之後",
|
|
||||||
"operator_is_before": "在之前",
|
|
||||||
"operator_is_between": "介於",
|
|
||||||
"operator_is_newer_than": "較新於",
|
|
||||||
"operator_is_not_set": "未設定",
|
|
||||||
"operator_is_older_than": "較舊於",
|
|
||||||
"operator_is_same_day": "同一天",
|
|
||||||
"operator_is_set": "已設定",
|
|
||||||
"operator_starts_with": "開頭為",
|
|
||||||
"operator_title_contains": "包含",
|
|
||||||
"operator_title_does_not_contain": "不包含",
|
|
||||||
"operator_title_ends_with": "結尾為",
|
|
||||||
"operator_title_equals": "等於",
|
|
||||||
"operator_title_greater_equal": "大於或等於",
|
|
||||||
"operator_title_greater_than": "大於",
|
|
||||||
"operator_title_is_after": "在之後",
|
|
||||||
"operator_title_is_before": "在之前",
|
|
||||||
"operator_title_is_between": "介於",
|
|
||||||
"operator_title_is_newer_than": "較新於",
|
|
||||||
"operator_title_is_not_set": "未設定",
|
|
||||||
"operator_title_is_older_than": "較舊於",
|
|
||||||
"operator_title_is_same_day": "同一天",
|
|
||||||
"operator_title_is_set": "已設定",
|
|
||||||
"operator_title_less_equal": "小於或等於",
|
|
||||||
"operator_title_less_than": "小於",
|
|
||||||
"operator_title_not_equals": "不等於",
|
|
||||||
"operator_title_starts_with": "開頭為",
|
|
||||||
"operator_title_user_is_in": "使用者屬於",
|
|
||||||
"operator_title_user_is_not_in": "使用者不屬於",
|
|
||||||
"operator_user_is_in": "使用者屬於",
|
|
||||||
"operator_user_is_not_in": "使用者不屬於",
|
|
||||||
"person_and_attributes": "人員與屬性",
|
"person_and_attributes": "人員與屬性",
|
||||||
"phone": "電話",
|
"phone": "電話",
|
||||||
"please_remove_the_segment_from_these_surveys_in_order_to_delete_it": "請從這些問卷中移除區隔,以便將其刪除。",
|
"please_remove_the_segment_from_these_surveys_in_order_to_delete_it": "請從這些問卷中移除區隔,以便將其刪除。",
|
||||||
@@ -944,7 +867,6 @@
|
|||||||
"user_targeting_is_currently_only_available_when": "使用者目標設定目前僅在以下情況下可用:",
|
"user_targeting_is_currently_only_available_when": "使用者目標設定目前僅在以下情況下可用:",
|
||||||
"value_cannot_be_empty": "值不能為空。",
|
"value_cannot_be_empty": "值不能為空。",
|
||||||
"value_must_be_a_number": "值必須是數字。",
|
"value_must_be_a_number": "值必須是數字。",
|
||||||
"value_must_be_positive": "值必須是正數。",
|
|
||||||
"view_filters": "檢視篩選器",
|
"view_filters": "檢視篩選器",
|
||||||
"where": "何處",
|
"where": "何處",
|
||||||
"with_the_formbricks_sdk": "使用 Formbricks SDK"
|
"with_the_formbricks_sdk": "使用 Formbricks SDK"
|
||||||
@@ -1031,32 +953,19 @@
|
|||||||
"enterprise_features": "企業版功能",
|
"enterprise_features": "企業版功能",
|
||||||
"get_an_enterprise_license_to_get_access_to_all_features": "取得企業授權以存取所有功能。",
|
"get_an_enterprise_license_to_get_access_to_all_features": "取得企業授權以存取所有功能。",
|
||||||
"keep_full_control_over_your_data_privacy_and_security": "完全掌控您的資料隱私權和安全性。",
|
"keep_full_control_over_your_data_privacy_and_security": "完全掌控您的資料隱私權和安全性。",
|
||||||
"license_invalid_description": "你在 ENTERPRISE_LICENSE_KEY 環境變數中填寫的授權金鑰無效。請檢查是否有輸入錯誤,或申請新的金鑰。",
|
|
||||||
"license_status": "授權狀態",
|
|
||||||
"license_status_active": "有效",
|
|
||||||
"license_status_description": "你的企業授權狀態。",
|
|
||||||
"license_status_expired": "已過期",
|
|
||||||
"license_status_invalid": "授權無效",
|
|
||||||
"license_status_unreachable": "無法連線",
|
|
||||||
"license_unreachable_grace_period": "無法連線至授權伺服器。在 3 天的寬限期內,你的企業功能仍可使用,寬限期將於 {gracePeriodEnd} 結束。",
|
|
||||||
"no_call_needed_no_strings_attached_request_a_free_30_day_trial_license_to_test_all_features_by_filling_out_this_form": "無需通話,無附加條件:填寫此表單,請求免費 30 天試用授權以測試所有功能:",
|
"no_call_needed_no_strings_attached_request_a_free_30_day_trial_license_to_test_all_features_by_filling_out_this_form": "無需通話,無附加條件:填寫此表單,請求免費 30 天試用授權以測試所有功能:",
|
||||||
"no_credit_card_no_sales_call_just_test_it": "無需信用卡。無需銷售電話。只需測試一下 :)",
|
"no_credit_card_no_sales_call_just_test_it": "無需信用卡。無需銷售電話。只需測試一下 :)",
|
||||||
"on_request": "依要求",
|
"on_request": "依要求",
|
||||||
"organization_roles": "組織角色(管理員、編輯者、開發人員等)",
|
"organization_roles": "組織角色(管理員、編輯者、開發人員等)",
|
||||||
"questions_please_reach_out_to": "有任何問題?請聯絡",
|
"questions_please_reach_out_to": "有任何問題?請聯絡",
|
||||||
"recheck_license": "重新檢查授權",
|
|
||||||
"recheck_license_failed": "授權檢查失敗。授權伺服器可能無法連線。",
|
|
||||||
"recheck_license_invalid": "授權金鑰無效。請確認你的 ENTERPRISE_LICENSE_KEY。",
|
|
||||||
"recheck_license_success": "授權檢查成功",
|
|
||||||
"recheck_license_unreachable": "授權伺服器無法連線,請稍後再試。",
|
|
||||||
"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": "服務等級協定",
|
||||||
"soc2_hipaa_iso_27001_compliance_check": "SOC2、HIPAA、ISO 27001 合規性檢查",
|
"soc2_hipaa_iso_27001_compliance_check": "SOC2、HIPAA、ISO 27001 合規性檢查",
|
||||||
"sso": "SSO(Google、Microsoft、OpenID Connect)",
|
"sso": "SSO(Google、Microsoft、OpenID Connect)",
|
||||||
"teams": "團隊和存取角色(讀取、讀取和寫入、管理)",
|
"teams": "團隊和存取角色(讀取、讀取和寫入、管理)",
|
||||||
"unlock_the_full_power_of_formbricks_free_for_30_days": "免費解鎖 Formbricks 的全部功能,為期 30 天。"
|
"unlock_the_full_power_of_formbricks_free_for_30_days": "免費解鎖 Formbricks 的全部功能,為期 30 天。",
|
||||||
|
"your_enterprise_license_is_active_all_features_unlocked": "您的企業授權處於活動狀態。所有功能都已解鎖。"
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"bulk_invite_warning_description": "在免費方案中,所有組織成員始終會被指派「擁有者」角色。",
|
"bulk_invite_warning_description": "在免費方案中,所有組織成員始終會被指派「擁有者」角色。",
|
||||||
@@ -1080,7 +989,7 @@
|
|||||||
"from_your_organization": "來自您的組織",
|
"from_your_organization": "來自您的組織",
|
||||||
"invitation_sent_once_more": "已再次發送邀請。",
|
"invitation_sent_once_more": "已再次發送邀請。",
|
||||||
"invite_deleted_successfully": "邀請已成功刪除",
|
"invite_deleted_successfully": "邀請已成功刪除",
|
||||||
"invite_expires_on": "邀請將於 '{'date'}' 過期",
|
"invited_on": "邀請於 '{'date'}'",
|
||||||
"invites_failed": "邀請失敗",
|
"invites_failed": "邀請失敗",
|
||||||
"leave_organization": "離開組織",
|
"leave_organization": "離開組織",
|
||||||
"leave_organization_description": "您將離開此組織並失去對所有問卷和回應的存取權限。只有再次收到邀請,您才能重新加入。",
|
"leave_organization_description": "您將離開此組織並失去對所有問卷和回應的存取權限。只有再次收到邀請,您才能重新加入。",
|
||||||
@@ -1193,6 +1102,8 @@
|
|||||||
"please_fill_all_workspace_fields": "請填寫所有欄位以新增工作區。",
|
"please_fill_all_workspace_fields": "請填寫所有欄位以新增工作區。",
|
||||||
"read": "讀取",
|
"read": "讀取",
|
||||||
"read_write": "讀取和寫入",
|
"read_write": "讀取和寫入",
|
||||||
|
"select_member": "選擇成員",
|
||||||
|
"select_workspace": "選擇工作區",
|
||||||
"team_admin": "團隊管理員",
|
"team_admin": "團隊管理員",
|
||||||
"team_created_successfully": "團隊已成功建立。",
|
"team_created_successfully": "團隊已成功建立。",
|
||||||
"team_deleted_successfully": "團隊已成功刪除。",
|
"team_deleted_successfully": "團隊已成功刪除。",
|
||||||
@@ -1242,6 +1153,7 @@
|
|||||||
"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": "新增選項",
|
||||||
@@ -1280,7 +1192,6 @@
|
|||||||
"block_duplicated": "區塊已複製。",
|
"block_duplicated": "區塊已複製。",
|
||||||
"bold": "粗體",
|
"bold": "粗體",
|
||||||
"brand_color": "品牌顏色",
|
"brand_color": "品牌顏色",
|
||||||
"brand_color_description": "應用於按鈕、連結和重點標示。",
|
|
||||||
"brightness": "亮度",
|
"brightness": "亮度",
|
||||||
"bulk_edit": "批次編輯",
|
"bulk_edit": "批次編輯",
|
||||||
"bulk_edit_description": "在下方逐行編輯所有選項。空白行將被略過,重複項目將被移除。",
|
"bulk_edit_description": "在下方逐行編輯所有選項。空白行將被略過,重複項目將被移除。",
|
||||||
@@ -1298,9 +1209,7 @@
|
|||||||
"capture_new_action": "擷取新操作",
|
"capture_new_action": "擷取新操作",
|
||||||
"card_arrangement_for_survey_type_derived": "'{'surveyTypeDerived'}' 問卷的卡片排列",
|
"card_arrangement_for_survey_type_derived": "'{'surveyTypeDerived'}' 問卷的卡片排列",
|
||||||
"card_background_color": "卡片背景顏色",
|
"card_background_color": "卡片背景顏色",
|
||||||
"card_background_color_description": "填滿問卷卡片區域。",
|
|
||||||
"card_border_color": "卡片邊框顏色",
|
"card_border_color": "卡片邊框顏色",
|
||||||
"card_border_color_description": "描繪問卷卡片的邊框。",
|
|
||||||
"card_styling": "卡片樣式",
|
"card_styling": "卡片樣式",
|
||||||
"casual": "隨意",
|
"casual": "隨意",
|
||||||
"caution_edit_duplicate": "複製 & 編輯",
|
"caution_edit_duplicate": "複製 & 編輯",
|
||||||
@@ -1311,12 +1220,20 @@
|
|||||||
"caution_explanation_responses_are_safe": "較舊和較新的回應會混在一起,可能導致數據摘要失準。",
|
"caution_explanation_responses_are_safe": "較舊和較新的回應會混在一起,可能導致數據摘要失準。",
|
||||||
"caution_recommendation": "這可能導致調查摘要中的數據不一致。我們建議複製這個調查。",
|
"caution_recommendation": "這可能導致調查摘要中的數據不一致。我們建議複製這個調查。",
|
||||||
"caution_text": "變更會導致不一致",
|
"caution_text": "變更會導致不一致",
|
||||||
|
"centered_modal_overlay_color": "置中彈窗覆蓋顏色",
|
||||||
"change_anyway": "仍然變更",
|
"change_anyway": "仍然變更",
|
||||||
"change_background": "變更背景",
|
"change_background": "變更背景",
|
||||||
"change_question_type": "變更問題類型",
|
"change_question_type": "變更問題類型",
|
||||||
"change_survey_type": "切換問卷類型會影響現有訪問",
|
"change_survey_type": "切換問卷類型會影響現有訪問",
|
||||||
|
"change_the_background_color_of_the_card": "變更卡片的背景顏色。",
|
||||||
|
"change_the_background_color_of_the_input_fields": "變更輸入欄位的背景顏色。",
|
||||||
"change_the_background_to_a_color_image_or_animation": "將背景變更為顏色、圖片或動畫。",
|
"change_the_background_to_a_color_image_or_animation": "將背景變更為顏色、圖片或動畫。",
|
||||||
|
"change_the_border_color_of_the_card": "變更卡片的邊框顏色。",
|
||||||
|
"change_the_border_color_of_the_input_fields": "變更輸入欄位的邊框顏色。",
|
||||||
|
"change_the_border_radius_of_the_card_and_the_inputs": "變更卡片和輸入的邊框半徑。",
|
||||||
|
"change_the_brand_color_of_the_survey": "變更問卷的品牌顏色。",
|
||||||
"change_the_placement_of_this_survey": "變更此問卷的位置。",
|
"change_the_placement_of_this_survey": "變更此問卷的位置。",
|
||||||
|
"change_the_question_color_of_the_survey": "變更問卷的問題顏色。",
|
||||||
"changes_saved": "已儲存變更。",
|
"changes_saved": "已儲存變更。",
|
||||||
"changing_survey_type_will_remove_existing_distribution_channels": "更改問卷類型會影響其共享方式。如果受訪者已擁有當前類型的存取連結,則在切換後可能會失去存取權限。",
|
"changing_survey_type_will_remove_existing_distribution_channels": "更改問卷類型會影響其共享方式。如果受訪者已擁有當前類型的存取連結,則在切換後可能會失去存取權限。",
|
||||||
"checkbox_label": "核取方塊標籤",
|
"checkbox_label": "核取方塊標籤",
|
||||||
@@ -1456,6 +1373,7 @@
|
|||||||
"hide_progress_bar": "隱藏進度列",
|
"hide_progress_bar": "隱藏進度列",
|
||||||
"hide_question_settings": "隱藏問題設定",
|
"hide_question_settings": "隱藏問題設定",
|
||||||
"hostname": "主機名稱",
|
"hostname": "主機名稱",
|
||||||
|
"how_funky_do_you_want_your_cards_in_survey_type_derived_surveys": "您希望 '{'surveyTypeDerived'}' 問卷中的卡片有多酷炫",
|
||||||
"if_you_need_more_please": "如果您需要更多,請",
|
"if_you_need_more_please": "如果您需要更多,請",
|
||||||
"if_you_really_want_that_answer_ask_until_you_get_it": "每次觸發時都顯示,直到提交回應為止。",
|
"if_you_really_want_that_answer_ask_until_you_get_it": "每次觸發時都顯示,直到提交回應為止。",
|
||||||
"ignore_global_waiting_time": "忽略冷卻期",
|
"ignore_global_waiting_time": "忽略冷卻期",
|
||||||
@@ -1466,9 +1384,7 @@
|
|||||||
"initial_value": "初始值",
|
"initial_value": "初始值",
|
||||||
"inner_text": "內部文字",
|
"inner_text": "內部文字",
|
||||||
"input_border_color": "輸入邊框顏色",
|
"input_border_color": "輸入邊框顏色",
|
||||||
"input_border_color_description": "描繪文字輸入框和文字區域的邊框。",
|
|
||||||
"input_color": "輸入顏色",
|
"input_color": "輸入顏色",
|
||||||
"input_color_description": "填滿文字輸入框的內部。",
|
|
||||||
"insert_link": "插入 連結",
|
"insert_link": "插入 連結",
|
||||||
"invalid_targeting": "目標設定無效:請檢查您的受眾篩選器",
|
"invalid_targeting": "目標設定無效:請檢查您的受眾篩選器",
|
||||||
"invalid_video_url_warning": "請輸入有效的 YouTube、Vimeo 或 Loom 網址。我們目前不支援其他影片託管提供者。",
|
"invalid_video_url_warning": "請輸入有效的 YouTube、Vimeo 或 Loom 網址。我們目前不支援其他影片託管提供者。",
|
||||||
@@ -1552,12 +1468,13 @@
|
|||||||
"protect_survey_with_pin_description": "只有擁有 PIN 碼的使用者才能存取問卷。",
|
"protect_survey_with_pin_description": "只有擁有 PIN 碼的使用者才能存取問卷。",
|
||||||
"publish": "發布",
|
"publish": "發布",
|
||||||
"question": "問題",
|
"question": "問題",
|
||||||
|
"question_color": "問題顏色",
|
||||||
"question_deleted": "問題已刪除。",
|
"question_deleted": "問題已刪除。",
|
||||||
"question_duplicated": "問題已複製。",
|
"question_duplicated": "問題已複製。",
|
||||||
"question_id_updated": "問題 ID 已更新",
|
"question_id_updated": "問題 ID 已更新",
|
||||||
"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}\" 配額中",
|
||||||
"question_used_in_recall": "此問題於問題 {questionIndex} 中被召回。",
|
"question_used_in_recall": "此問題於問題 {questionIndex} 中被召回。",
|
||||||
"question_used_in_recall_ending_card": "此問題於結尾卡中被召回。",
|
"question_used_in_recall_ending_card": "此問題於結尾卡中被召回。",
|
||||||
"quotas": {
|
"quotas": {
|
||||||
@@ -1613,7 +1530,6 @@
|
|||||||
"response_limits_redirections_and_more": "回應限制、重新導向等。",
|
"response_limits_redirections_and_more": "回應限制、重新導向等。",
|
||||||
"response_options": "回應選項",
|
"response_options": "回應選項",
|
||||||
"roundness": "圓角",
|
"roundness": "圓角",
|
||||||
"roundness_description": "調整卡片邊角的圓弧度。",
|
|
||||||
"row_used_in_logic_error": "此 row 用於問題 '{'questionIndex'}' 的邏輯中。請先從邏輯中移除。",
|
"row_used_in_logic_error": "此 row 用於問題 '{'questionIndex'}' 的邏輯中。請先從邏輯中移除。",
|
||||||
"rows": "列",
|
"rows": "列",
|
||||||
"save_and_close": "儲存並關閉",
|
"save_and_close": "儲存並關閉",
|
||||||
@@ -1655,6 +1571,7 @@
|
|||||||
"styling_set_to_theme_styles": "樣式設定為主題樣式",
|
"styling_set_to_theme_styles": "樣式設定為主題樣式",
|
||||||
"subheading": "副標題",
|
"subheading": "副標題",
|
||||||
"subtract": "減 -",
|
"subtract": "減 -",
|
||||||
|
"suggest_colors": "建議顏色",
|
||||||
"survey_completed_heading": "問卷已完成",
|
"survey_completed_heading": "問卷已完成",
|
||||||
"survey_completed_subheading": "此免費且開源的問卷已關閉",
|
"survey_completed_subheading": "此免費且開源的問卷已關閉",
|
||||||
"survey_display_settings": "問卷顯示設定",
|
"survey_display_settings": "問卷顯示設定",
|
||||||
@@ -1731,7 +1648,7 @@
|
|||||||
"validation_rules": "驗證規則",
|
"validation_rules": "驗證規則",
|
||||||
"validation_rules_description": "僅接受符合下列條件的回應",
|
"validation_rules_description": "僅接受符合下列條件的回應",
|
||||||
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "'{'variable'}' 用於問題 '{'questionIndex'}' 的邏輯中。請先從邏輯中移除。",
|
"variable_is_used_in_logic_of_question_please_remove_it_from_logic_first": "'{'variable'}' 用於問題 '{'questionIndex'}' 的邏輯中。請先從邏輯中移除。",
|
||||||
"variable_is_used_in_quota_please_remove_it_from_quota_first": "變數「{variableName}」正被使用於「{quotaName}」配額中",
|
"variable_is_used_in_quota_please_remove_it_from_quota_first": "變數 \"{variableName}\" 正被使用於 \"{quotaName}\" 配額中",
|
||||||
"variable_name_conflicts_with_hidden_field": "變數名稱與現有的隱藏欄位 ID 衝突。",
|
"variable_name_conflicts_with_hidden_field": "變數名稱與現有的隱藏欄位 ID 衝突。",
|
||||||
"variable_name_is_already_taken_please_choose_another": "已使用此變數名稱,請選擇另一個名稱。",
|
"variable_name_is_already_taken_please_choose_another": "已使用此變數名稱,請選擇另一個名稱。",
|
||||||
"variable_name_must_start_with_a_letter": "變數名稱必須以字母開頭。",
|
"variable_name_must_start_with_a_letter": "變數名稱必須以字母開頭。",
|
||||||
@@ -1957,7 +1874,6 @@
|
|||||||
"filtered_responses_excel": "篩選回應 (Excel)",
|
"filtered_responses_excel": "篩選回應 (Excel)",
|
||||||
"generating_qr_code": "正在生成 QR code",
|
"generating_qr_code": "正在生成 QR code",
|
||||||
"impressions": "曝光數",
|
"impressions": "曝光數",
|
||||||
"impressions_identified_only": "僅顯示已識別聯絡人的曝光次數",
|
|
||||||
"impressions_tooltip": "問卷已檢視的次數。",
|
"impressions_tooltip": "問卷已檢視的次數。",
|
||||||
"in_app": {
|
"in_app": {
|
||||||
"connection_description": "調查將顯示給符合以下列出條件的網站用戶",
|
"connection_description": "調查將顯示給符合以下列出條件的網站用戶",
|
||||||
@@ -2000,7 +1916,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": "整體",
|
||||||
@@ -2140,71 +2055,9 @@
|
|||||||
"look": {
|
"look": {
|
||||||
"add_background_color": "新增背景顏色",
|
"add_background_color": "新增背景顏色",
|
||||||
"add_background_color_description": "為標誌容器新增背景顏色。",
|
"add_background_color_description": "為標誌容器新增背景顏色。",
|
||||||
"advanced_styling_field_border_radius": "邊框圓角",
|
|
||||||
"advanced_styling_field_button_bg": "按鈕背景",
|
|
||||||
"advanced_styling_field_button_bg_description": "填滿「下一步」/「送出」按鈕。",
|
|
||||||
"advanced_styling_field_button_border_radius_description": "調整按鈕的圓角。",
|
|
||||||
"advanced_styling_field_button_font_size_description": "調整按鈕標籤文字的大小。",
|
|
||||||
"advanced_styling_field_button_font_weight_description": "讓按鈕文字變細或變粗。",
|
|
||||||
"advanced_styling_field_button_height_description": "調整按鈕的高度。",
|
|
||||||
"advanced_styling_field_button_padding_x_description": "在左右兩側增加間距。",
|
|
||||||
"advanced_styling_field_button_padding_y_description": "在上下兩側增加間距。",
|
|
||||||
"advanced_styling_field_button_text": "按鈕文字",
|
|
||||||
"advanced_styling_field_button_text_description": "設定按鈕內標籤的顏色。",
|
|
||||||
"advanced_styling_field_description_color": "說明文字顏色",
|
|
||||||
"advanced_styling_field_description_color_description": "設定每個標題下方文字的顏色。",
|
|
||||||
"advanced_styling_field_description_size": "說明文字大小",
|
|
||||||
"advanced_styling_field_description_size_description": "調整說明文字的大小。",
|
|
||||||
"advanced_styling_field_description_weight": "說明文字粗細",
|
|
||||||
"advanced_styling_field_description_weight_description": "讓說明文字變細或變粗。",
|
|
||||||
"advanced_styling_field_font_size": "字體大小",
|
|
||||||
"advanced_styling_field_font_weight": "字體粗細",
|
|
||||||
"advanced_styling_field_headline_color": "標題顏色",
|
|
||||||
"advanced_styling_field_headline_color_description": "設定主要問題文字的顏色。",
|
|
||||||
"advanced_styling_field_headline_size": "標題字體大小",
|
|
||||||
"advanced_styling_field_headline_size_description": "調整標題文字的大小。",
|
|
||||||
"advanced_styling_field_headline_weight": "標題字體粗細",
|
|
||||||
"advanced_styling_field_headline_weight_description": "讓標題文字變細或變粗。",
|
|
||||||
"advanced_styling_field_height": "最小高度",
|
|
||||||
"advanced_styling_field_indicator_bg": "指示器背景",
|
|
||||||
"advanced_styling_field_indicator_bg_description": "設定進度條已填滿部分的顏色。",
|
|
||||||
"advanced_styling_field_input_border_radius_description": "調整輸入框的圓角。",
|
|
||||||
"advanced_styling_field_input_font_size_description": "調整輸入框內輸入文字的大小。",
|
|
||||||
"advanced_styling_field_input_height_description": "設定輸入欄位的最小高度。",
|
|
||||||
"advanced_styling_field_input_padding_x_description": "在左右兩側增加間距。",
|
|
||||||
"advanced_styling_field_input_padding_y_description": "在上方和下方增加間距。",
|
|
||||||
"advanced_styling_field_input_placeholder_opacity_description": "讓提示文字變得更淡。",
|
|
||||||
"advanced_styling_field_input_shadow_description": "在輸入框周圍加上陰影。",
|
|
||||||
"advanced_styling_field_input_text": "輸入文字",
|
|
||||||
"advanced_styling_field_input_text_description": "設定輸入文字的顏色。",
|
|
||||||
"advanced_styling_field_option_bg": "背景",
|
|
||||||
"advanced_styling_field_option_bg_description": "填滿選項項目背景。",
|
|
||||||
"advanced_styling_field_option_border_radius_description": "讓選項的邊角變圓。",
|
|
||||||
"advanced_styling_field_option_font_size_description": "調整選項標籤文字的大小。",
|
|
||||||
"advanced_styling_field_option_label": "標籤顏色",
|
|
||||||
"advanced_styling_field_option_label_description": "設定選項標籤文字的顏色。",
|
|
||||||
"advanced_styling_field_option_padding_x_description": "在左側和右側增加間距。",
|
|
||||||
"advanced_styling_field_option_padding_y_description": "在上方和下方增加間距。",
|
|
||||||
"advanced_styling_field_padding_x": "左右內距",
|
|
||||||
"advanced_styling_field_padding_y": "上下內距",
|
|
||||||
"advanced_styling_field_placeholder_opacity": "預設文字透明度",
|
|
||||||
"advanced_styling_field_shadow": "陰影",
|
|
||||||
"advanced_styling_field_track_bg": "軌道背景",
|
|
||||||
"advanced_styling_field_track_bg_description": "設定進度條未填滿部分的顏色。",
|
|
||||||
"advanced_styling_field_track_height": "軌道高度",
|
|
||||||
"advanced_styling_field_track_height_description": "調整進度條的厚度。",
|
|
||||||
"advanced_styling_field_upper_label_color": "標題標籤顏色",
|
|
||||||
"advanced_styling_field_upper_label_color_description": "設定輸入框上方小標籤的顏色。",
|
|
||||||
"advanced_styling_field_upper_label_size": "標題標籤字體大小",
|
|
||||||
"advanced_styling_field_upper_label_size_description": "調整輸入框上方小標籤的大小。",
|
|
||||||
"advanced_styling_field_upper_label_weight": "標題標籤字體粗細",
|
|
||||||
"advanced_styling_field_upper_label_weight_description": "讓標籤字體變細或變粗。",
|
|
||||||
"advanced_styling_section_buttons": "按鈕",
|
|
||||||
"advanced_styling_section_headlines": "標題與說明",
|
|
||||||
"advanced_styling_section_inputs": "輸入欄位",
|
|
||||||
"advanced_styling_section_options": "選項(單選/複選)",
|
|
||||||
"app_survey_placement": "應用程式問卷位置",
|
"app_survey_placement": "應用程式問卷位置",
|
||||||
"app_survey_placement_settings_description": "變更問卷在您的網頁應用程式或網站中顯示的位置。",
|
"app_survey_placement_settings_description": "變更問卷在您的網頁應用程式或網站中顯示的位置。",
|
||||||
|
"centered_modal_overlay_color": "置中彈窗覆蓋顏色",
|
||||||
"email_customization": "電子郵件自訂化",
|
"email_customization": "電子郵件自訂化",
|
||||||
"email_customization_description": "變更 Formbricks 代表您發送的電子郵件外觀與風格。",
|
"email_customization_description": "變更 Formbricks 代表您發送的電子郵件外觀與風格。",
|
||||||
"enable_custom_styling": "啟用自訂樣式",
|
"enable_custom_styling": "啟用自訂樣式",
|
||||||
@@ -2215,9 +2068,6 @@
|
|||||||
"formbricks_branding_hidden": "Formbricks 品牌標示已隱藏。",
|
"formbricks_branding_hidden": "Formbricks 品牌標示已隱藏。",
|
||||||
"formbricks_branding_settings_description": "我們很感謝您的支持,但若您選擇關閉我們也能理解。",
|
"formbricks_branding_settings_description": "我們很感謝您的支持,但若您選擇關閉我們也能理解。",
|
||||||
"formbricks_branding_shown": "Formbricks 品牌標示已顯示。",
|
"formbricks_branding_shown": "Formbricks 品牌標示已顯示。",
|
||||||
"generate_theme_btn": "產生",
|
|
||||||
"generate_theme_confirmation": "你想根據品牌色產生一組相符的主題色嗎?這將會覆蓋你目前的顏色設定。",
|
|
||||||
"generate_theme_header": "要產生主題色嗎?",
|
|
||||||
"logo_removed_successfully": "標誌已成功移除",
|
"logo_removed_successfully": "標誌已成功移除",
|
||||||
"logo_settings_description": "上傳您的公司標誌,以用於問卷和連結預覽的品牌展示。",
|
"logo_settings_description": "上傳您的公司標誌,以用於問卷和連結預覽的品牌展示。",
|
||||||
"logo_updated_successfully": "標誌已成功更新",
|
"logo_updated_successfully": "標誌已成功更新",
|
||||||
@@ -2232,8 +2082,6 @@
|
|||||||
"show_formbricks_branding_in": "在 {type} 問卷中顯示 Formbricks 品牌標示",
|
"show_formbricks_branding_in": "在 {type} 問卷中顯示 Formbricks 品牌標示",
|
||||||
"show_powered_by_formbricks": "顯示「Powered by Formbricks」標記",
|
"show_powered_by_formbricks": "顯示「Powered by Formbricks」標記",
|
||||||
"styling_updated_successfully": "樣式已成功更新",
|
"styling_updated_successfully": "樣式已成功更新",
|
||||||
"suggest_colors": "建議顏色",
|
|
||||||
"suggested_colors_applied_please_save": "已成功產生建議色彩。請按「儲存」以保存變更。",
|
|
||||||
"theme": "主題",
|
"theme": "主題",
|
||||||
"theme_settings_description": "為所有調查建立樣式主題。您可以為每個調查啟用自訂樣式。"
|
"theme_settings_description": "為所有調查建立樣式主題。您可以為每個調查啟用自訂樣式。"
|
||||||
},
|
},
|
||||||
@@ -2997,7 +2845,6 @@
|
|||||||
"preview_survey_question_2_choice_1_label": "是,請保持通知我。",
|
"preview_survey_question_2_choice_1_label": "是,請保持通知我。",
|
||||||
"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_welcome_card_headline": "歡迎!",
|
"preview_survey_welcome_card_headline": "歡迎!",
|
||||||
"prioritize_features_description": "找出您的使用者最需要和最不需要的功能。",
|
"prioritize_features_description": "找出您的使用者最需要和最不需要的功能。",
|
||||||
"prioritize_features_name": "優先排序功能",
|
"prioritize_features_name": "優先排序功能",
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user