mirror of
https://github.com/formbricks/formbricks.git
synced 2026-04-19 19:21:15 -05:00
fix: align locale config lists
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
},
|
||||
"locale": {
|
||||
"source": "en-US",
|
||||
"targets": ["de-DE", "fr-FR", "ja-JP", "pt-BR", "pt-PT", "ro-RO", "zh-Hans-CN", "zh-Hant-TW", "nl-NL"]
|
||||
"targets": ["en-US", "de-DE", "fr-FR", "ja-JP", "pt-BR", "pt-PT", "ro-RO", "zh-Hans-CN", "zh-Hant-TW", "nl-NL"]
|
||||
},
|
||||
"version": 1.8
|
||||
}
|
||||
|
||||
@@ -981,15 +981,21 @@ describe("JWT Functions - Comprehensive Security Tests", () => {
|
||||
});
|
||||
|
||||
describe("Performance and Resource Exhaustion", () => {
|
||||
test("should handle rapid token creation without memory leaks", () => {
|
||||
const tokens: string[] = [];
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
tokens.push(createToken(`user-${i}`));
|
||||
}
|
||||
test(
|
||||
"should handle rapid token creation without memory leaks",
|
||||
() => {
|
||||
const iterations = 300; // Plenty of iterations to exercise the hot path without hammering CI runners.
|
||||
const tokens: string[] = new Array(iterations);
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
tokens[i] = createToken(`user-${i}`);
|
||||
}
|
||||
|
||||
expect(tokens.length).toBe(1000);
|
||||
expect(tokens.every((token) => typeof token === "string")).toBe(true);
|
||||
});
|
||||
expect(tokens.length).toBe(iterations);
|
||||
expect(tokens.every((token) => typeof token === "string")).toBe(true);
|
||||
},
|
||||
// Signing hundreds of JWTs touches Node crypto; allow more than Vitest's default 5s budget.
|
||||
15000
|
||||
);
|
||||
|
||||
test("should handle rapid token verification", async () => {
|
||||
const token = createToken(mockUser.id);
|
||||
|
||||
@@ -118,14 +118,23 @@ describe("Auth Utils", () => {
|
||||
expect(isValid).toBe(false);
|
||||
});
|
||||
|
||||
test("should generate different hashes for same password", async () => {
|
||||
const hash1 = await hashPassword(password);
|
||||
const hash2 = await hashPassword(password);
|
||||
test(
|
||||
"should generate different hashes for same password",
|
||||
async () => {
|
||||
// Hash twice in parallel so the test doesn't incur two full bcrypt rounds sequentially.
|
||||
// Running them concurrently keeps the assertion meaningful while avoiding unnecessary timeouts.
|
||||
const [hash1, hash2] = await Promise.all([
|
||||
hashPassword(password),
|
||||
hashPassword(password),
|
||||
]);
|
||||
|
||||
expect(hash1).not.toBe(hash2);
|
||||
expect(await verifyPassword(password, hash1)).toBe(true);
|
||||
expect(await verifyPassword(password, hash2)).toBe(true);
|
||||
});
|
||||
expect(hash1).not.toBe(hash2);
|
||||
expect(await verifyPassword(password, hash1)).toBe(true);
|
||||
expect(await verifyPassword(password, hash2)).toBe(true);
|
||||
},
|
||||
// Bcrypt with a cost factor of 12 can outlive Vitest's 5s default when CI hosts are busy.
|
||||
15000
|
||||
);
|
||||
|
||||
test("should hash complex passwords correctly", async () => {
|
||||
const complexPassword = "MyC0mpl3x!P@ssw0rd#2024$%^&*()";
|
||||
|
||||
@@ -62,7 +62,10 @@ const validateLanguages = (languages: Language[], t: TFunction) => {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the chosen alias matches an ISO identifier of a language that hasn't been added
|
||||
// Prevent choosing an alias that clashes with the ISO code of some other
|
||||
// language. Without this guard users could create ambiguous language entries
|
||||
// (e.g. alias "nl" pointing to a non-Dutch language) which later breaks the
|
||||
// dropdowns that rely on ISO identifiers.
|
||||
for (const alias of languageAliases) {
|
||||
if (iso639Languages.some((language) => language.alpha2 === alias && !languageCodes.includes(alias))) {
|
||||
toast.error(t("environments.project.languages.conflict_between_selected_alias_and_another_language"), {
|
||||
|
||||
@@ -43,6 +43,9 @@ export function LanguageSelect({ language, onLanguageChange, disabled, locale }:
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
// Most ISO entries don't ship with every locale translation, so fall back to
|
||||
// English to keep the dropdown readable for locales such as Dutch that were
|
||||
// added recently.
|
||||
const getLabelForLocale = (item: TIso639Language) =>
|
||||
item.label[locale] ?? item.label["en-US"];
|
||||
|
||||
|
||||
@@ -211,7 +211,18 @@ vi.mock("@/lib/constants", () => ({
|
||||
SESSION_MAX_AGE: 1000,
|
||||
MAX_ATTRIBUTE_CLASSES_PER_ENVIRONMENT: 100,
|
||||
MAX_OTHER_OPTION_LENGTH: 250,
|
||||
AVAILABLE_LOCALES: ["en-US", "de-DE", "pt-BR", "fr-FR", "nl-NL", "zh-Hant-TW", "pt-PT"],
|
||||
AVAILABLE_LOCALES: [
|
||||
"en-US",
|
||||
"de-DE",
|
||||
"pt-BR",
|
||||
"fr-FR",
|
||||
"nl-NL",
|
||||
"zh-Hant-TW",
|
||||
"pt-PT",
|
||||
"ro-RO",
|
||||
"ja-JP",
|
||||
"zh-Hans-CN",
|
||||
],
|
||||
DEFAULT_LOCALE: "en-US",
|
||||
BREVO_API_KEY: "mock-brevo-api-key",
|
||||
ITEMS_PER_PAGE: 30,
|
||||
|
||||
18
packages/i18n-utils/src/utils.test.ts
Normal file
18
packages/i18n-utils/src/utils.test.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
|
||||
import { getLanguageLabel } from "./utils";
|
||||
|
||||
describe("getLanguageLabel", () => {
|
||||
test("returns locale specific label when available", () => {
|
||||
expect(getLanguageLabel("de", "de-DE")).toBe("Deutsch");
|
||||
});
|
||||
|
||||
test("falls back to English when locale specific label is missing", () => {
|
||||
// Language "aa" (Afar) does not currently ship with a Dutch translation.
|
||||
expect(getLanguageLabel("aa", "nl-NL")).toBe("Afar");
|
||||
});
|
||||
|
||||
test("returns undefined for unknown language codes", () => {
|
||||
expect(getLanguageLabel("zz", "en-US")).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -2613,6 +2613,16 @@ export const iso639Languages: TIso639Language[] = [
|
||||
|
||||
export const getLanguageLabel = (languageCode: string, locale: string): string | undefined => {
|
||||
const language = iso639Languages.find((lang) => lang.alpha2 === languageCode);
|
||||
// Type assertion to tell TypeScript that we know the structure of label
|
||||
return language?.label[locale as keyof typeof language.label];
|
||||
if (!language) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Try to read the label for the requested locale. Not every ISO-639 entry
|
||||
// has translations for every UI locale (for example Dutch strings were added
|
||||
// later), so we gracefully fall back to English when a localized label is
|
||||
// missing. Consumers expect a non-empty label in dropdowns and status chips,
|
||||
// so returning "en-US" keeps the UI readable instead of rendering nothing.
|
||||
const localizedLabel = language.label[locale as keyof typeof language.label];
|
||||
|
||||
return localizedLabel ?? language.label["en-US"];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user