import { iso639Languages } from "@formbricks/i18n-utils/src/utils"; import { TI18nString } from "@formbricks/types/i18n"; import { TLanguage } from "@formbricks/types/project"; import { TSurveyLanguage } from "@formbricks/types/surveys/types"; import { structuredClone } from "@/lib/pollyfills/structuredClone"; // Helper function to create an i18nString from a regular string. export const createI18nString = ( text: string | TI18nString, languages: string[], targetLanguageCode?: string ): TI18nString => { if (typeof text === "object") { // It's already an i18n object, so clone it const i18nString: TI18nString = structuredClone(text); // Add new language keys with empty strings if they don't exist languages?.forEach((language) => { if (!(language in i18nString)) { i18nString[language] = ""; } }); // Remove language keys that are not in the languages array Object.keys(i18nString).forEach((key) => { if (key !== (targetLanguageCode ?? "default") && languages && !languages.includes(key)) { delete i18nString[key]; } }); return i18nString; } else { // It's a regular string, so create a new i18n object const i18nString = { [targetLanguageCode ?? "default"]: text, }; // Initialize all provided languages with empty strings languages?.forEach((language) => { if (language !== (targetLanguageCode ?? "default")) { i18nString[language] = ""; } }); return i18nString; } }; // Type guard to check if an object is an I18nString export const isI18nObject = (obj: unknown): obj is TI18nString => { return typeof obj === "object" && obj !== null && Object.keys(obj).includes("default"); }; export const isLabelValidForAllLanguages = (label: TI18nString, languages: string[]): boolean => { return languages.every((language) => label[language] && label[language].trim() !== ""); }; export const getLocalizedValue = (value: TI18nString | undefined, languageId: string): string => { if (!value) { return ""; } if (isI18nObject(value)) { if (value[languageId]) { return value[languageId]; } return ""; } return ""; }; export const extractLanguageCodes = (surveyLanguages: TSurveyLanguage[]): string[] => { if (!surveyLanguages) return []; return surveyLanguages.map((surveyLanguage) => surveyLanguage.default ? "default" : surveyLanguage.language.code ); }; export const getEnabledLanguages = (surveyLanguages: TSurveyLanguage[]) => { return surveyLanguages.filter((surveyLanguage) => surveyLanguage.enabled); }; export const extractLanguageIds = (languages: TLanguage[]): string[] => { return languages.map((language) => language.code); }; export const getLanguageCode = (surveyLanguages: TSurveyLanguage[], languageCode: string | null) => { if (!surveyLanguages?.length || !languageCode) return "default"; const language = surveyLanguages.find((surveyLanguage) => surveyLanguage.language.code === languageCode); return language?.default ? "default" : language?.language.code || "default"; }; export const iso639Identifiers = iso639Languages.map((language) => language.code); // Helper function to add language keys to a multi-language object (e.g. survey or question) // Iterates over the object recursively and adds empty strings for new language keys export const addMultiLanguageLabels = (object: unknown, languageSymbols: string[]): any => { // Helper function to add language keys to a multi-language object function addLanguageKeys(obj: { default: string; [key: string]: string }) { languageSymbols.forEach((lang) => { if (!obj.hasOwnProperty(lang)) { obj[lang] = ""; // Add empty string for new language keys } }); } // Recursive function to process an object or array function processObject(obj: unknown) { if (Array.isArray(obj)) { obj.forEach((item) => processObject(item)); } else if (obj && typeof obj === "object") { for (const key in obj) { if (obj.hasOwnProperty(key)) { if (key === "default" && typeof obj[key] === "string") { addLanguageKeys(obj as { default: string; [key: string]: string }); } else { processObject(obj[key]); } } } } } // Start processing the question object processObject(object); return object; }; export const appLanguages = [ { code: "de-DE", label: { "en-US": "German", native: "Deutsch", }, }, { code: "en-US", label: { "en-US": "English (US)", native: "English (US)", }, }, { code: "es-ES", label: { "en-US": "Spanish", native: "Español", }, }, { code: "fr-FR", label: { "en-US": "French", native: "Français", }, }, { code: "hu-HU", label: { "en-US": "Hungarian", native: "Magyar", }, }, { code: "ja-JP", label: { "en-US": "Japanese", native: "日本語", }, }, { code: "nl-NL", label: { "en-US": "Dutch", native: "Nederlands", }, }, { code: "pt-BR", label: { "en-US": "Portuguese (Brazil)", native: "Português (Brasil)", }, }, { code: "pt-PT", label: { "en-US": "Portuguese (Portugal)", native: "Português (Portugal)", }, }, { code: "ro-RO", label: { "en-US": "Romanian", native: "Română", }, }, { code: "ru-RU", label: { "en-US": "Russian", native: "Русский", }, }, { code: "sv-SE", label: { "en-US": "Swedish", native: "Svenska", }, }, { code: "zh-Hans-CN", label: { "en-US": "Chinese (Simplified)", native: "简体中文", }, }, { code: "zh-Hant-TW", label: { "en-US": "Chinese (Traditional)", native: "繁體中文", }, }, ]; export const sortedAppLanguages = [...appLanguages].sort((a, b) => a.label["en-US"].localeCompare(b.label["en-US"]) );