diff --git a/apps/web/lib/survey/service.ts b/apps/web/lib/survey/service.ts
index a8977522bb..af4be01e6a 100644
--- a/apps/web/lib/survey/service.ts
+++ b/apps/web/lib/survey/service.ts
@@ -631,6 +631,7 @@ export const createSurvey = async (workspaceId: string, surveyBody: TSurveyCreat
const { createdBy, languages, ...restSurveyBody } = parsedSurveyBody;
const normalizedCloseOn = restSurveyBody.closeOn instanceof Date ? restSurveyBody.closeOn : null;
const normalizedPublishOn = restSurveyBody.publishOn instanceof Date ? restSurveyBody.publishOn : null;
+ const hasMultipleEnabledLanguages = (languages ?? []).filter((language) => language.enabled).length > 1;
const actionClasses = await getActionClasses(parsedWorkspaceId);
@@ -641,6 +642,8 @@ export const createSurvey = async (workspaceId: string, surveyBody: TSurveyCreat
publishOn: normalizedPublishOn,
status: restSurveyBody.status ?? "draft",
}),
+ autoSelectLanguage:
+ restSurveyBody.autoSelectLanguage ?? (hasMultipleEnabledLanguages ? true : undefined),
// @ts-expect-error - languages would be undefined in case of empty array
languages: languages?.length ? languages : undefined,
triggers: restSurveyBody.triggers
diff --git a/apps/web/locales/en-US.json b/apps/web/locales/en-US.json
index 07fc792d59..63af3a514f 100644
--- a/apps/web/locales/en-US.json
+++ b/apps/web/locales/en-US.json
@@ -2833,8 +2833,8 @@
"auto_save_disabled": "Auto-save disabled",
"auto_save_disabled_tooltip": "Your survey is only auto-saved when in draft. This assures public surveys are not unintentionally updated.",
"auto_save_on": "Auto-save on",
- "auto_select_browser_language": "Auto-select browser language",
- "auto_select_browser_language_description": "Use the respondent's browser language when the survey has a matching active language. Falls back to the default language.",
+ "auto_select_browser_language": "Use browser language by default",
+ "auto_select_browser_language_description": "Automatically open the survey in the respondent's browser language when that language is active. Falls back to the default language.",
"automatically_close_survey_after": "Automatically close survey after",
"automatically_close_the_survey_after_a_certain_number_of_responses": "Automatically close the survey after a certain number of responses.",
"automatically_close_the_survey_if_the_user_does_not_respond_after_certain_number_of_seconds": "Automatically close the survey if the user does not respond after certain number of seconds.",
diff --git a/apps/web/modules/survey/link/lib/utils.test.ts b/apps/web/modules/survey/link/lib/utils.test.ts
index 15e3c9297d..2695e34ed2 100644
--- a/apps/web/modules/survey/link/lib/utils.test.ts
+++ b/apps/web/modules/survey/link/lib/utils.test.ts
@@ -147,6 +147,28 @@ describe("getSurveyLanguageCode", () => {
expect(getSurveyLanguageCode(undefined, survey, ["es-MX", "en-US"])).toBe("es-ES");
});
+ test("uses aliases and ignores disabled languages", () => {
+ const survey = {
+ ...createMockSurvey([
+ language("en", { default: true }),
+ language("de", { enabled: false }),
+ language("fr-FR", {
+ language: {
+ id: "lang-fr-FR",
+ code: "fr-FR",
+ alias: "fr",
+ createdAt: new Date(),
+ updatedAt: new Date(),
+ projectId: "p1",
+ },
+ }),
+ ]),
+ autoSelectLanguage: true,
+ };
+
+ expect(getSurveyLanguageCode(undefined, survey, ["de-DE", "fr-CA"])).toBe("fr-FR");
+ });
+
test("falls back to default language when auto-selection is disabled or unmatched", () => {
const survey = createMockSurvey([language("en", { default: true }), language("de")]);
diff --git a/apps/web/modules/survey/link/lib/utils.ts b/apps/web/modules/survey/link/lib/utils.ts
index 40ba843441..ebc581d875 100644
--- a/apps/web/modules/survey/link/lib/utils.ts
+++ b/apps/web/modules/survey/link/lib/utils.ts
@@ -1,6 +1,7 @@
import { TJsWorkspaceStateSurvey } from "@formbricks/types/js";
import { TSurveyBlock } from "@formbricks/types/surveys/blocks";
import { TSurveyElement } from "@formbricks/types/surveys/elements";
+import { resolveSurveyLanguage } from "@formbricks/types/surveys/language";
import { TSurvey } from "@formbricks/types/surveys/types";
export function isRTL(text: string): boolean {
@@ -55,77 +56,20 @@ export function isRTLLanguage(survey: TJsWorkspaceStateSurvey, languageCode: str
export const getElementsFromSurveyBlocks = (blocks: TSurveyBlock[]): TSurveyElement[] =>
blocks.flatMap((block) => block.elements);
-const normalizeLanguageCode = (languageCode: string): string =>
- languageCode.trim().split(";")[0].replace("_", "-").toLowerCase();
-
-const getBaseLanguageCode = (languageCode: string): string =>
- normalizeLanguageCode(languageCode).split("-")[0];
-
-const getSelectableLanguageCode = (surveyLanguage: TSurvey["languages"][number]): string | undefined => {
- if (surveyLanguage.default) return "default";
- if (!surveyLanguage.enabled) return undefined;
- return surveyLanguage.language.code;
-};
-
-const findExactLanguageMatch = (survey: TSurvey, languageCode: string): string | undefined => {
- const normalizedLanguageCode = normalizeLanguageCode(languageCode);
-
- const selectedLanguage = survey.languages.find((surveyLanguage) => {
- return (
- normalizeLanguageCode(surveyLanguage.language.code) === normalizedLanguageCode ||
- (surveyLanguage.language.alias
- ? normalizeLanguageCode(surveyLanguage.language.alias) === normalizedLanguageCode
- : false)
- );
- });
-
- return selectedLanguage ? getSelectableLanguageCode(selectedLanguage) : undefined;
-};
-
-const findLooseLanguageMatch = (survey: TSurvey, languageCode: string): string | undefined => {
- const baseLanguageCode = getBaseLanguageCode(languageCode);
-
- for (const surveyLanguage of survey.languages) {
- const selectableLanguageCode = getSelectableLanguageCode(surveyLanguage);
- if (!selectableLanguageCode) continue;
-
- const languageBaseCode = getBaseLanguageCode(surveyLanguage.language.code);
- const aliasBaseCode = surveyLanguage.language.alias
- ? getBaseLanguageCode(surveyLanguage.language.alias)
- : undefined;
-
- if (languageBaseCode === baseLanguageCode || aliasBaseCode === baseLanguageCode) {
- return selectableLanguageCode;
- }
- }
-
- return undefined;
-};
-
export const getSurveyLanguageCode = (
langParam: string | undefined,
survey: TSurvey,
browserLanguageCodes: string[] = []
): string => {
- if (langParam) {
- return findExactLanguageMatch(survey, langParam) ?? "default";
- }
-
- if (!survey.autoSelectLanguage) {
- return "default";
- }
-
- for (const browserLanguageCode of browserLanguageCodes) {
- const exactMatch = findExactLanguageMatch(survey, browserLanguageCode);
- if (exactMatch) return exactMatch;
- }
-
- for (const browserLanguageCode of browserLanguageCodes) {
- const looseMatch = findLooseLanguageMatch(survey, browserLanguageCode);
- if (looseMatch) return looseMatch;
- }
-
- return "default";
+ return (
+ resolveSurveyLanguage({
+ languages: survey.languages,
+ explicitLanguageCode: langParam,
+ browserLanguageCodes,
+ autoSelectLanguage: survey.autoSelectLanguage,
+ unmatchedExplicitLanguageBehavior: "fallback",
+ }) ?? "default"
+ );
};
/**
diff --git a/apps/web/modules/survey/multi-language-surveys/components/language-view.tsx b/apps/web/modules/survey/multi-language-surveys/components/language-view.tsx
index c9cb3e8d90..4dc8fcda09 100644
--- a/apps/web/modules/survey/multi-language-surveys/components/language-view.tsx
+++ b/apps/web/modules/survey/multi-language-surveys/components/language-view.tsx
@@ -149,7 +149,7 @@ export const LanguageView = ({
buttonVariant: "destructive",
onConfirm: () => {
// Strip all non-default language keys from the survey data
- let cleanedSurvey = localSurvey;
+ let cleanedSurvey = { ...localSurvey, autoSelectLanguage: false };
for (const lang of localSurvey.languages) {
if (!lang.default) {
cleanedSurvey = removeLanguageKeysFromSurvey(cleanedSurvey, lang.language.code);
@@ -172,6 +172,7 @@ export const LanguageView = ({
const language = workspaceLanguages.find((lang) => lang.code === languageCode);
if (!language) return;
+ const isNewMultiLanguageSurvey = localSurvey.languages.length === 0;
let languageExists = false;
const newLanguages =
localSurvey.languages.map((lang) => {
@@ -187,7 +188,11 @@ export const LanguageView = ({
}
setConfirmationModalInfo((prev) => ({ ...prev, open: false }));
- setLocalSurvey({ ...localSurvey, languages: newLanguages });
+ setLocalSurvey({
+ ...localSurvey,
+ languages: newLanguages,
+ autoSelectLanguage: isNewMultiLanguageSurvey ? true : localSurvey.autoSelectLanguage,
+ });
};
const handleToggleLanguage = (code: string) => {
@@ -254,7 +259,7 @@ export const LanguageView = ({
buttonText: t("workspace.surveys.edit.remove_translations"),
buttonVariant: "destructive",
onConfirm: () => {
- updateSurveyTranslations(localSurvey, []);
+ updateSurveyTranslations({ ...localSurvey, autoSelectLanguage: false }, []);
setIsMultiLanguageActivated(false);
setConfirmationModalInfo((prev) => ({ ...prev, open: false }));
},
diff --git a/docs/api-reference/openapi.json b/docs/api-reference/openapi.json
index 2bc9eb2434..74b41d865f 100644
--- a/docs/api-reference/openapi.json
+++ b/docs/api-reference/openapi.json
@@ -328,6 +328,10 @@
"example": null,
"type": "boolean"
},
+ "autoSelectLanguage": {
+ "example": null,
+ "type": "boolean"
+ },
"delay": {
"example": 0,
"type": "integer"
@@ -4367,6 +4371,7 @@
{
"autoClose": null,
"autoComplete": null,
+ "autoSelectLanguage": null,
"createdAt": "2024-08-05T11:08:27.042Z",
"createdBy": "clfv1zvij0000ru0gunwpy43a",
"delay": 0,
@@ -4551,6 +4556,7 @@
"value": {
"autoClose": null,
"autoComplete": null,
+ "autoSelectLanguage": null,
"createdBy": null,
"delay": 0,
"displayLimit": null,
@@ -4771,6 +4777,7 @@
"data": {
"autoClose": null,
"autoComplete": null,
+ "autoSelectLanguage": null,
"createdAt": "2024-08-05T11:08:27.042Z",
"createdBy": "clfv1zvij0000ru0gunwpy43a",
"delay": 0,
@@ -5037,6 +5044,7 @@
"data": {
"autoClose": null,
"autoComplete": null,
+ "autoSelectLanguage": null,
"createdAt": "2024-08-05T11:08:27.042Z",
"createdBy": "clfv1zvij0000ru0gunwpy43a",
"delay": 0,
@@ -5290,6 +5298,7 @@
"data": {
"autoClose": null,
"autoComplete": null,
+ "autoSelectLanguage": null,
"createdAt": "2024-08-05T11:08:27.042Z",
"createdBy": "clfv1zvij0000ru0gunwpy43a",
"delay": 0,
@@ -5493,6 +5502,7 @@
"data": {
"autoClose": null,
"autoComplete": null,
+ "autoSelectLanguage": null,
"createdAt": "2024-08-05T11:08:27.042Z",
"createdBy": "clfv1zvij0000ru0gunwpy43a",
"delay": 0,
diff --git a/docs/xm-and-surveys/surveys/general-features/multi-language-surveys.mdx b/docs/xm-and-surveys/surveys/general-features/multi-language-surveys.mdx
index f45eab46f8..b6c0ffa870 100644
--- a/docs/xm-and-surveys/surveys/general-features/multi-language-surveys.mdx
+++ b/docs/xm-and-surveys/surveys/general-features/multi-language-surveys.mdx
@@ -37,12 +37,14 @@ How to deliver a specific language depends on the survey type (app or link surve

You can come back to this page anytime to add more languages or remove existing ones.
+
Return to the dashboard to create a new survey or edit an existing one:

+
@@ -52,19 +54,26 @@ How to deliver a specific language depends on the survey type (app or link surve
Choose a **Default Language** for your survey.
+ New multi-language surveys use the respondent's browser language by default when a matching active language is
+ available. You can change this anytime with the **Use browser language by default** toggle. This setting is
+ separate from **Show language switch**; you can automatically open the right language without showing respondents a
+ manual language menu.
+
Changing the default language will reset all the translations you have made for the survey.
+
Add the languages from the dropdown that you want to support in your survey:

+
You can now see the survey in the selected language by clicking on the language dropdown in any of the questions.
-
+
Now you can translate all survey content, including questions, options, and button placeholders, into the selected language.
@@ -74,6 +83,17 @@ How to deliver a specific language depends on the survey type (app or link surve
+## Language Selection Order
+
+Formbricks selects the survey language in this order:
+
+1. An explicit `lang` URL parameter for link surveys, or an explicit SDK/user language for app surveys.
+2. The respondent's browser language when **Use browser language by default** is enabled.
+3. The survey's default language.
+
+Language matching first checks the exact identifier or alias. If there is no exact match, Formbricks checks language
+variants with the same base language, for example `es-MX` can match an active `es-ES` translation.
+
## App Surveys Configuration
@@ -91,8 +111,11 @@ How to deliver a specific language depends on the survey type (app or link surve
If a user has a language assigned, a survey has multi-language activated and it is missing a translation in
- the language of the user, the survey will not be displayed.
+ the language of the user, the survey will not be displayed. When no user language is assigned and **Use browser
+ language by default** is enabled, Formbricks uses the browser language and falls back to the survey default if
+ there is no match.
+
@@ -104,7 +127,9 @@ How to deliver a specific language depends on the survey type (app or link surve
## Link Surveys Configuration
-For link surveys, the translation delivery is dependent on the `lang` URL parameter.
+For link surveys, the `lang` URL parameter always takes priority. If no `lang` parameter is present and **Use browser
+language by default** is enabled, Formbricks uses the respondent's browser language when a matching active language is
+available.
@@ -122,7 +147,10 @@ For link surveys, the translation delivery is dependent on the `lang` URL parame
- German: [https://app.Formbricks.com/s/clptfos2i1pj516pvhxqyu3bn?lang=de](https://app.Formbricks.com/s/clptfos2i1pj516pvhxqyu3bn?lang=de)
- Without the `lang` parameter, Formbricks will show the survey in the default language you have set.
+ Without the `lang` parameter, Formbricks will use the browser language when **Use browser language by default** is
+ enabled and a matching active language exists. Otherwise, it will show the survey in the default language you have
+ set.
+
@@ -150,14 +178,13 @@ Formbricks fully supports Right-to-Left (RTL) languages such as Arabic, Hebrew,
Add an RTL language (like Arabic or Hebrew) in the **Survey Languages** settings
-
- Create translations for your survey content in the RTL language
-
+Create translations for your survey content in the RTL language
The survey will automatically display in RTL format when that language is selected

+
diff --git a/packages/js-core/package.json b/packages/js-core/package.json
index 1a6e2ea3b7..8a958b7cec 100644
--- a/packages/js-core/package.json
+++ b/packages/js-core/package.json
@@ -41,6 +41,9 @@
"test": "vitest run",
"test:coverage": "vitest run --coverage"
},
+ "dependencies": {
+ "@formbricks/types": "workspace:*"
+ },
"author": "Formbricks ",
"devDependencies": {
"@formbricks/config-typescript": "workspace:*",
diff --git a/packages/js-core/src/lib/common/tests/utils.test.ts b/packages/js-core/src/lib/common/tests/utils.test.ts
index d38fb79070..812bc6b7ee 100644
--- a/packages/js-core/src/lib/common/tests/utils.test.ts
+++ b/packages/js-core/src/lib/common/tests/utils.test.ts
@@ -456,6 +456,18 @@ describe("utils.ts", () => {
expect(getLanguageCode(survey, undefined, ["de-DE", "en-US"])).toBe("de");
expect(getLanguageCode({ ...survey, autoSelectLanguage: false }, undefined, ["de-DE"])).toBe("default");
});
+
+ test("does not fall back to browser language when an explicit user language is unavailable", () => {
+ const survey = {
+ autoSelectLanguage: true,
+ languages: [
+ { language: { code: "en" }, default: true, enabled: true },
+ { language: { code: "de" }, default: false, enabled: true },
+ ],
+ } as unknown as TEnvironmentStateSurvey;
+
+ expect(getLanguageCode(survey, "fr", ["de-DE"])).toBeUndefined();
+ });
});
// ---------------------------------------------------------------------------------
diff --git a/packages/js-core/src/lib/common/utils.ts b/packages/js-core/src/lib/common/utils.ts
index 45e774f95e..c655d4e20f 100644
--- a/packages/js-core/src/lib/common/utils.ts
+++ b/packages/js-core/src/lib/common/utils.ts
@@ -1,3 +1,4 @@
+import { resolveSurveyLanguage } from "@formbricks/types/surveys/language";
import { Logger } from "@/lib/common/logger";
import type {
TSurveyStyling,
@@ -161,79 +162,18 @@ export const getDefaultLanguageCode = (survey: TWorkspaceStateSurvey): string |
if (defaultSurveyLanguage) return defaultSurveyLanguage.language.code;
};
-const normalizeLanguageCode = (languageCode: string): string =>
- languageCode.trim().split(";")[0].replace("_", "-").toLowerCase();
-
-const getBaseLanguageCode = (languageCode: string): string =>
- normalizeLanguageCode(languageCode).split("-")[0];
-
-const getSelectableLanguageCode = (surveyLanguage: TWorkspaceStateSurvey["languages"][number]) => {
- if (surveyLanguage.default) {
- return "default";
- }
- if (!surveyLanguage.enabled) {
- return undefined;
- }
- return surveyLanguage.language.code;
-};
-
-const findExactLanguageMatch = (survey: TWorkspaceStateSurvey, language: string): string | undefined => {
- const normalizedLanguageCode = normalizeLanguageCode(language);
-
- const selectedLanguage = survey.languages.find((surveyLanguage) => {
- return (
- normalizeLanguageCode(surveyLanguage.language.code) === normalizedLanguageCode ||
- (surveyLanguage.language.alias
- ? normalizeLanguageCode(surveyLanguage.language.alias) === normalizedLanguageCode
- : false)
- );
- });
-
- return selectedLanguage ? getSelectableLanguageCode(selectedLanguage) : undefined;
-};
-
-const findLooseLanguageMatch = (survey: TWorkspaceStateSurvey, language: string): string | undefined => {
- const baseLanguageCode = getBaseLanguageCode(language);
-
- for (const surveyLanguage of survey.languages) {
- const selectableLanguageCode = getSelectableLanguageCode(surveyLanguage);
- if (!selectableLanguageCode) continue;
-
- const languageBaseCode = getBaseLanguageCode(surveyLanguage.language.code);
- const aliasBaseCode = surveyLanguage.language.alias
- ? getBaseLanguageCode(surveyLanguage.language.alias)
- : undefined;
-
- if (languageBaseCode === baseLanguageCode || aliasBaseCode === baseLanguageCode) {
- return selectableLanguageCode;
- }
- }
-
- return undefined;
-};
-
export const getLanguageCode = (
survey: TWorkspaceStateSurvey,
language?: string,
fallbackLanguages: string[] = []
): string | undefined => {
- if (language) {
- return findExactLanguageMatch(survey, language) ?? findLooseLanguageMatch(survey, language);
- }
-
- if (!survey.autoSelectLanguage) return "default";
-
- for (const fallbackLanguage of fallbackLanguages) {
- const exactMatch = findExactLanguageMatch(survey, fallbackLanguage);
- if (exactMatch) return exactMatch;
- }
-
- for (const fallbackLanguage of fallbackLanguages) {
- const looseMatch = findLooseLanguageMatch(survey, fallbackLanguage);
- if (looseMatch) return looseMatch;
- }
-
- return "default";
+ return resolveSurveyLanguage({
+ languages: survey.languages,
+ explicitLanguageCode: language,
+ browserLanguageCodes: fallbackLanguages,
+ autoSelectLanguage: survey.autoSelectLanguage,
+ unmatchedExplicitLanguageBehavior: "undefined",
+ });
};
export const getSecureRandom = (): number => {
diff --git a/packages/types/surveys/language.ts b/packages/types/surveys/language.ts
new file mode 100644
index 0000000000..db934c54d4
--- /dev/null
+++ b/packages/types/surveys/language.ts
@@ -0,0 +1,108 @@
+type TSurveyLanguageLike = {
+ default?: boolean | null;
+ enabled?: boolean | null;
+ language: {
+ code: string;
+ alias?: string | null;
+ };
+};
+
+interface ResolveSurveyLanguageInput {
+ languages: T[];
+ explicitLanguageCode?: string;
+ browserLanguageCodes?: string[];
+ autoSelectLanguage?: boolean | null;
+ unmatchedExplicitLanguageBehavior?: "fallback" | "undefined";
+}
+
+export const normalizeLanguageCode = (languageCode: string): string =>
+ languageCode.trim().split(";")[0].trim().replace("_", "-").toLowerCase();
+
+const getBaseLanguageCode = (languageCode: string): string =>
+ normalizeLanguageCode(languageCode).split("-")[0];
+
+const getSelectableLanguageCode = (surveyLanguage: TSurveyLanguageLike): string | undefined => {
+ if (surveyLanguage.default) return "default";
+ if (!surveyLanguage.enabled) return undefined;
+ return surveyLanguage.language.code;
+};
+
+const findExactLanguageMatch = (
+ languages: T[],
+ languageCode: string
+): string | undefined => {
+ const normalizedLanguageCode = normalizeLanguageCode(languageCode);
+
+ const selectedLanguage = languages.find((surveyLanguage) => {
+ return (
+ normalizeLanguageCode(surveyLanguage.language.code) === normalizedLanguageCode ||
+ (surveyLanguage.language.alias
+ ? normalizeLanguageCode(surveyLanguage.language.alias) === normalizedLanguageCode
+ : false)
+ );
+ });
+
+ return selectedLanguage ? getSelectableLanguageCode(selectedLanguage) : undefined;
+};
+
+const findLooseLanguageMatch = (
+ languages: T[],
+ languageCode: string
+): string | undefined => {
+ const baseLanguageCode = getBaseLanguageCode(languageCode);
+
+ for (const surveyLanguage of languages) {
+ const selectableLanguageCode = getSelectableLanguageCode(surveyLanguage);
+ if (!selectableLanguageCode) continue;
+
+ const languageBaseCode = getBaseLanguageCode(surveyLanguage.language.code);
+ const aliasBaseCode = surveyLanguage.language.alias
+ ? getBaseLanguageCode(surveyLanguage.language.alias)
+ : undefined;
+
+ if (languageBaseCode === baseLanguageCode || aliasBaseCode === baseLanguageCode) {
+ return selectableLanguageCode;
+ }
+ }
+
+ return undefined;
+};
+
+export const matchSurveyLanguage = (
+ languages: T[],
+ languageCode: string
+): string | undefined => {
+ return findExactLanguageMatch(languages, languageCode) ?? findLooseLanguageMatch(languages, languageCode);
+};
+
+/**
+ * Resolves survey language precedence without coupling callers to a delivery channel:
+ * explicit language (URL or SDK/user setting) -> browser languages when enabled -> survey default.
+ */
+export const resolveSurveyLanguage = ({
+ languages,
+ explicitLanguageCode,
+ browserLanguageCodes = [],
+ autoSelectLanguage,
+ unmatchedExplicitLanguageBehavior = "fallback",
+}: ResolveSurveyLanguageInput): string | undefined => {
+ if (explicitLanguageCode) {
+ const explicitMatch = matchSurveyLanguage(languages, explicitLanguageCode);
+ if (explicitMatch) return explicitMatch;
+ return unmatchedExplicitLanguageBehavior === "undefined" ? undefined : "default";
+ }
+
+ if (!autoSelectLanguage) return "default";
+
+ for (const browserLanguageCode of browserLanguageCodes) {
+ const exactMatch = findExactLanguageMatch(languages, browserLanguageCode);
+ if (exactMatch) return exactMatch;
+ }
+
+ for (const browserLanguageCode of browserLanguageCodes) {
+ const looseMatch = findLooseLanguageMatch(languages, browserLanguageCode);
+ if (looseMatch) return looseMatch;
+ }
+
+ return "default";
+};
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0fc8123adc..2e208080e9 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -840,6 +840,10 @@ importers:
version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.4.0)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)
packages/js-core:
+ dependencies:
+ '@formbricks/types':
+ specifier: workspace:*
+ version: link:../types
devDependencies:
'@formbricks/config-typescript':
specifier: workspace:*