mirror of
https://github.com/formbricks/formbricks.git
synced 2025-12-31 10:50:35 -06:00
Compare commits
4 Commits
fix/user-a
...
chore/test
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
68d0af027a | ||
|
|
eec02496ca | ||
|
|
edc58b5cb9 | ||
|
|
4b6b171540 |
@@ -17,7 +17,8 @@
|
||||
"zh-Hans-CN",
|
||||
"zh-Hant-TW",
|
||||
"nl-NL",
|
||||
"es-ES"
|
||||
"es-ES",
|
||||
"sv-SE"
|
||||
]
|
||||
},
|
||||
"version": 1.8
|
||||
|
||||
@@ -176,6 +176,7 @@ export const AVAILABLE_LOCALES: TUserLocale[] = [
|
||||
"ja-JP",
|
||||
"zh-Hans-CN",
|
||||
"es-ES",
|
||||
"sv-SE",
|
||||
];
|
||||
|
||||
// Billing constants
|
||||
|
||||
@@ -140,6 +140,7 @@ export const appLanguages = [
|
||||
"zh-Hans-CN": "英语(美国)",
|
||||
"nl-NL": "Engels (VS)",
|
||||
"es-ES": "Inglés (EE.UU.)",
|
||||
"sv-SE": "Engelska (USA)",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -156,6 +157,7 @@ export const appLanguages = [
|
||||
"zh-Hans-CN": "德语",
|
||||
"nl-NL": "Duits",
|
||||
"es-ES": "Alemán",
|
||||
"sv-SE": "Tyska",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -172,6 +174,7 @@ export const appLanguages = [
|
||||
"zh-Hans-CN": "葡萄牙语(巴西)",
|
||||
"nl-NL": "Portugees (Brazilië)",
|
||||
"es-ES": "Portugués (Brasil)",
|
||||
"sv-SE": "Portugisiska (Brasilien)",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -188,6 +191,7 @@ export const appLanguages = [
|
||||
"zh-Hans-CN": "法语",
|
||||
"nl-NL": "Frans",
|
||||
"es-ES": "Francés",
|
||||
"sv-SE": "Franska",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -199,11 +203,12 @@ export const appLanguages = [
|
||||
"fr-FR": "Chinois (Traditionnel)",
|
||||
"zh-Hant-TW": "繁體中文",
|
||||
"pt-PT": "Chinês (Tradicional)",
|
||||
"ro-RO": "Chineză (Tradicională)",
|
||||
"ro-RO": "Chineza (Tradițională)",
|
||||
"ja-JP": "中国語(繁体字)",
|
||||
"zh-Hans-CN": "繁体中文",
|
||||
"nl-NL": "Chinees (Traditioneel)",
|
||||
"es-ES": "Chino (Tradicional)",
|
||||
"sv-SE": "Kinesiska (traditionell)",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -220,6 +225,7 @@ export const appLanguages = [
|
||||
"zh-Hans-CN": "葡萄牙语(葡萄牙)",
|
||||
"nl-NL": "Portugees (Portugal)",
|
||||
"es-ES": "Portugués (Portugal)",
|
||||
"sv-SE": "Portugisiska (Portugal)",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -236,6 +242,7 @@ export const appLanguages = [
|
||||
"zh-Hans-CN": "罗马尼亚语",
|
||||
"nl-NL": "Roemeens",
|
||||
"es-ES": "Rumano",
|
||||
"sv-SE": "Rumänska",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -252,6 +259,7 @@ export const appLanguages = [
|
||||
"zh-Hans-CN": "日语",
|
||||
"nl-NL": "Japans",
|
||||
"es-ES": "Japonés",
|
||||
"sv-SE": "Japanska",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -263,11 +271,12 @@ export const appLanguages = [
|
||||
"fr-FR": "Chinois (Simplifié)",
|
||||
"zh-Hant-TW": "簡體中文",
|
||||
"pt-PT": "Chinês (Simplificado)",
|
||||
"ro-RO": "Chineză (Simplificată)",
|
||||
"ro-RO": "Chineza (Simplificată)",
|
||||
"ja-JP": "中国語(簡体字)",
|
||||
"zh-Hans-CN": "简体中文",
|
||||
"nl-NL": "Chinees (Vereenvoudigd)",
|
||||
"es-ES": "Chino (Simplificado)",
|
||||
"sv-SE": "Kinesiska (förenklad)",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -279,11 +288,12 @@ export const appLanguages = [
|
||||
"fr-FR": "Néerlandais",
|
||||
"zh-Hant-TW": "荷蘭語",
|
||||
"pt-PT": "Holandês",
|
||||
"ro-RO": "Olandeză",
|
||||
"ro-RO": "Olandeza",
|
||||
"ja-JP": "オランダ語",
|
||||
"zh-Hans-CN": "荷兰语",
|
||||
"nl-NL": "Nederlands",
|
||||
"es-ES": "Neerlandés",
|
||||
"sv-SE": "Nederländska",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -300,6 +310,24 @@ export const appLanguages = [
|
||||
"zh-Hans-CN": "西班牙语",
|
||||
"nl-NL": "Spaans",
|
||||
"es-ES": "Español",
|
||||
"sv-SE": "Spanska",
|
||||
},
|
||||
},
|
||||
{
|
||||
code: "sv-SE",
|
||||
label: {
|
||||
"en-US": "Swedish",
|
||||
"de-DE": "Schwedisch",
|
||||
"pt-BR": "Sueco",
|
||||
"fr-FR": "Suédois",
|
||||
"zh-Hant-TW": "瑞典語",
|
||||
"pt-PT": "Sueco",
|
||||
"ro-RO": "Suedeză",
|
||||
"ja-JP": "スウェーデン語",
|
||||
"zh-Hans-CN": "瑞典语",
|
||||
"nl-NL": "Zweeds",
|
||||
"es-ES": "Sueco",
|
||||
"sv-SE": "Svenska",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -69,6 +69,12 @@ describe("Time Utilities", () => {
|
||||
const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000);
|
||||
expect(timeSince(oneHourAgo.toISOString(), "de-DE")).toBe("vor etwa 1 Stunde");
|
||||
});
|
||||
|
||||
test("should format time since in Swedish", () => {
|
||||
const now = new Date();
|
||||
const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000);
|
||||
expect(timeSince(oneHourAgo.toISOString(), "sv-SE")).toBe("ungefär en timme sedan");
|
||||
});
|
||||
});
|
||||
|
||||
describe("timeSinceDate", () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { formatDistance, intlFormat } from "date-fns";
|
||||
import { de, enUS, es, fr, ja, nl, pt, ptBR, ro, zhCN, zhTW } from "date-fns/locale";
|
||||
import { de, enUS, es, fr, ja, nl, pt, ptBR, ro, sv, zhCN, zhTW } from "date-fns/locale";
|
||||
import { TUserLocale } from "@formbricks/types/user";
|
||||
|
||||
export const convertDateString = (dateString: string | null) => {
|
||||
@@ -93,6 +93,8 @@ const getLocaleForTimeSince = (locale: TUserLocale) => {
|
||||
return fr;
|
||||
case "nl-NL":
|
||||
return nl;
|
||||
case "sv-SE":
|
||||
return sv;
|
||||
case "zh-Hant-TW":
|
||||
return zhTW;
|
||||
case "pt-PT":
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as nextHeaders from "next/headers";
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
import { AVAILABLE_LOCALES, DEFAULT_LOCALE } from "@/lib/constants";
|
||||
import { appLanguages } from "@/lib/i18n/utils";
|
||||
import { findMatchingLocale } from "./locale";
|
||||
|
||||
// Mock the Next.js headers function
|
||||
@@ -84,4 +85,25 @@ describe("locale", () => {
|
||||
expect(result).toBe(germanLocale);
|
||||
expect(nextHeaders.headers).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("Swedish locale (sv-SE) is available and selectable", async () => {
|
||||
// Verify sv-SE is in AVAILABLE_LOCALES
|
||||
expect(AVAILABLE_LOCALES).toContain("sv-SE");
|
||||
|
||||
// Verify Swedish has a language entry with proper labels
|
||||
const swedishLanguage = appLanguages.find((lang) => lang.code === "sv-SE");
|
||||
expect(swedishLanguage).toBeDefined();
|
||||
expect(swedishLanguage?.label["en-US"]).toBe("Swedish");
|
||||
expect(swedishLanguage?.label["sv-SE"]).toBe("Svenska");
|
||||
|
||||
// Verify the locale can be matched from Accept-Language header
|
||||
vi.mocked(nextHeaders.headers).mockReturnValue({
|
||||
get: vi.fn().mockReturnValue("sv-SE,en-US"),
|
||||
} as any);
|
||||
|
||||
const result = await findMatchingLocale();
|
||||
|
||||
expect(result).toBe("sv-SE");
|
||||
expect(nextHeaders.headers).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
2937
apps/web/locales/sv-SE.json
Normal file
2937
apps/web/locales/sv-SE.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -220,6 +220,7 @@ vi.mock("@/lib/constants", () => ({
|
||||
"ja-JP",
|
||||
"zh-Hans-CN",
|
||||
"es-ES",
|
||||
"sv-SE",
|
||||
],
|
||||
DEFAULT_LOCALE: "en-US",
|
||||
BREVO_API_KEY: "mock-brevo-api-key",
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
},
|
||||
"locale": {
|
||||
"source": "en",
|
||||
"targets": ["de", "it", "fr", "es", "ar", "pt", "ru", "uz", "ro", "ja", "zh-Hans", "hi", "nl"]
|
||||
"targets": ["de", "it", "fr", "es", "ar", "pt", "ru", "uz", "ro", "ja", "zh-Hans", "hi", "nl", "sv"]
|
||||
},
|
||||
"version": 1.8
|
||||
}
|
||||
|
||||
73
packages/surveys/locales/sv.json
Normal file
73
packages/surveys/locales/sv.json
Normal file
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"common": {
|
||||
"and": "och",
|
||||
"apply": "Tillämpa",
|
||||
"auto_close_wrapper": "Automatisk stängning",
|
||||
"back": "Tillbaka",
|
||||
"click_or_drag_to_upload_files": "Klicka eller dra för att ladda upp filer.",
|
||||
"close_survey": "Stäng enkät",
|
||||
"company_logo": "Företagslogotyp",
|
||||
"delete_file": "Ta bort fil",
|
||||
"file_upload": "Ladda upp fil",
|
||||
"finish": "Slutför",
|
||||
"language_switch": "Språkväxlare",
|
||||
"less_than_x_minutes": "{count, plural, one {mindre än 1 minut} other {mindre än {count} minuter}}",
|
||||
"move_down": "Flytta {item} nedåt",
|
||||
"move_up": "Flytta {item} uppåt",
|
||||
"next": "Nästa",
|
||||
"open_in_new_tab": "Öppna i ny flik",
|
||||
"optional": "Valfritt",
|
||||
"options": "Alternativ",
|
||||
"people_responded": "{count, plural, one {1 person har svarat} other {{count} personer har svarat}}",
|
||||
"please_retry_now_or_try_again_later": "Försök igen nu eller försök igen senare.",
|
||||
"powered_by": "Drivs av",
|
||||
"privacy_policy": "Integritetspolicy",
|
||||
"protected_by_reCAPTCHA_and_the_Google": "Skyddas av reCAPTCHA och Googles",
|
||||
"question": "Fråga",
|
||||
"question_video": "Frågevideo",
|
||||
"ranking_items": "Rangordna objekt",
|
||||
"respondents_will_not_see_this_card": "Respondenter kommer inte att se detta kort",
|
||||
"retry": "Försök igen",
|
||||
"select_a_date": "Välj ett datum",
|
||||
"select_for_ranking": "Välj {item} för rangordning",
|
||||
"sending_responses": "Skickar svar...",
|
||||
"takes": "Tar",
|
||||
"terms_of_service": "Användarvillkor",
|
||||
"the_servers_cannot_be_reached_at_the_moment": "Servrarna kan inte nås för tillfället.",
|
||||
"they_will_be_redirected_immediately": "De kommer att omdirigeras omedelbart",
|
||||
"upload_files_by_clicking_or_dragging_them_here": "Ladda upp filer genom att klicka eller dra dem hit",
|
||||
"uploading": "Laddar upp",
|
||||
"x_minutes": "{count, plural, one {1 minut} other {{count} minuter}}",
|
||||
"x_plus_minutes": "{count}+ minuter",
|
||||
"you_have_selected_x_date": "Du har valt {date}",
|
||||
"you_have_successfully_uploaded_the_file": "Du har framgångsrikt laddat upp filen {fileName}",
|
||||
"your_feedback_is_stuck": "Din feedback fastnade :("
|
||||
},
|
||||
"errors": {
|
||||
"file_input": {
|
||||
"duplicate_files": "Följande filer är redan uppladdade: {duplicateNames}. Dubbletter av filer är inte tillåtna.",
|
||||
"file_size_exceeded": "Följande filer överstiger maxstorleken på {maxSizeInMB} MB och togs bort: {fileNames}",
|
||||
"file_size_exceeded_alert": "Filen måste vara mindre än {maxSizeInMB} MB",
|
||||
"no_valid_file_types_selected": "Inga giltiga filtyper valda. Vänligen välj en giltig filtyp.",
|
||||
"only_one_file_can_be_uploaded_at_a_time": "Endast en fil kan laddas upp åt gången.",
|
||||
"upload_failed": "Uppladdning misslyckades! Försök igen.",
|
||||
"you_can_only_upload_a_maximum_of_files": "Du kan ladda upp maximalt {FILE_LIMIT} filer."
|
||||
},
|
||||
"invalid_device_error": {
|
||||
"message": "Vänligen inaktivera skräppostskyddet i enkätinställningarna för att fortsätta använda denna enhet.",
|
||||
"title": "Denna enhet stöder inte skräppostskydd."
|
||||
},
|
||||
"please_book_an_appointment": "Vänligen boka ett möte",
|
||||
"please_enter_a_valid_email_address": "Vänligen ange en giltig e-postadress",
|
||||
"please_enter_a_valid_phone_number": "Vänligen ange ett giltigt telefonnummer",
|
||||
"please_enter_a_valid_url": "Vänligen ange en giltig URL",
|
||||
"please_fill_out_this_field": "Vänligen fyll i detta fält",
|
||||
"please_rank_all_items_before_submitting": "Vänligen rangordna alla objekt innan du skickar",
|
||||
"please_select_a_date": "Vänligen välj ett datum",
|
||||
"please_upload_a_file": "Vänligen ladda upp en fil",
|
||||
"recaptcha_error": {
|
||||
"message": "Ditt svar kunde inte skickas eftersom det flaggades som automatiserad aktivitet. Om du andas, försök igen.",
|
||||
"title": "Vi kunde inte verifiera att du är människa."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import nlTranslations from "../../locales/nl.json";
|
||||
import ptTranslations from "../../locales/pt.json";
|
||||
import roTranslations from "../../locales/ro.json";
|
||||
import ruTranslations from "../../locales/ru.json";
|
||||
import svTranslations from "../../locales/sv.json";
|
||||
import uzTranslations from "../../locales/uz.json";
|
||||
import zhHansTranslations from "../../locales/zh-Hans.json";
|
||||
|
||||
@@ -21,7 +22,23 @@ i18n
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
fallbackLng: "en",
|
||||
supportedLngs: ["en", "de", "it", "fr", "es", "ar", "pt", "ro", "ja", "ru", "uz", "zh-Hans", "hi", "nl"],
|
||||
supportedLngs: [
|
||||
"en",
|
||||
"de",
|
||||
"it",
|
||||
"fr",
|
||||
"es",
|
||||
"ar",
|
||||
"pt",
|
||||
"ro",
|
||||
"ja",
|
||||
"ru",
|
||||
"uz",
|
||||
"zh-Hans",
|
||||
"hi",
|
||||
"nl",
|
||||
"sv",
|
||||
],
|
||||
|
||||
resources: {
|
||||
en: { translation: enTranslations },
|
||||
@@ -36,6 +53,7 @@ i18n
|
||||
nl: { translation: nlTranslations },
|
||||
ru: { translation: ruTranslations },
|
||||
uz: { translation: uzTranslations },
|
||||
sv: { translation: svTranslations },
|
||||
"zh-Hans": { translation: zhHansTranslations },
|
||||
hi: { translation: hiTranslations },
|
||||
},
|
||||
|
||||
@@ -12,6 +12,7 @@ export const ZUserLocale = z.enum([
|
||||
"ja-JP",
|
||||
"zh-Hans-CN",
|
||||
"es-ES",
|
||||
"sv-SE",
|
||||
]);
|
||||
|
||||
export type TUserLocale = z.infer<typeof ZUserLocale>;
|
||||
|
||||
Reference in New Issue
Block a user