diff --git a/package-lock.json b/package-lock.json
index 02cf4af998..8e7061cd5d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -35,6 +35,7 @@
"plausible-tracker": "0.3.9",
"qr-creator": "^1.0.0",
"stream-browserify": "^3.0.0",
+ "sveltekit-i18n": "^2.4.2",
"ua-parser-js": "^1.0.35",
"zod": "^3.22.3"
},
@@ -3185,6 +3186,21 @@
"vite": "^6.0.0"
}
},
+ "node_modules/@sveltekit-i18n/base": {
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/@sveltekit-i18n/base/-/base-1.3.7.tgz",
+ "integrity": "sha512-kg1kql1/ro/lIudwFiWrv949Q07gmweln87tflUZR51MNdXXzK4fiJQv5Mw50K/CdQ5BOk/dJ0WOH2vOtBI6yw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "svelte": ">=3.49.0"
+ }
+ },
+ "node_modules/@sveltekit-i18n/parser-default": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@sveltekit-i18n/parser-default/-/parser-default-1.1.1.tgz",
+ "integrity": "sha512-/gtzLlqm/sox7EoPKD56BxGZktK/syGc79EbJAPWY5KVitQD9SM0TP8yJCqDxTVPk7Lk0WJhrBGUE2Nn0f5M1w==",
+ "license": "MIT"
+ },
"node_modules/@szmarczak/http-timer": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz",
@@ -14841,6 +14857,22 @@
"typescript": "^4.9.4 || ^5.0.0"
}
},
+ "node_modules/sveltekit-i18n": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/sveltekit-i18n/-/sveltekit-i18n-2.4.2.tgz",
+ "integrity": "sha512-hjRWn4V4DBL8JQKJoJa3MRvn6d32Zo+rWkoSP5bsQ/XIAguPdQUZJ8LMe6Nc1rST8WEVdu9+vZI3aFdKYGR3+Q==",
+ "license": "MIT",
+ "workspaces": [
+ "./examples/*/"
+ ],
+ "dependencies": {
+ "@sveltekit-i18n/base": "~1.3.0",
+ "@sveltekit-i18n/parser-default": "~1.1.0"
+ },
+ "peerDependencies": {
+ "svelte": ">=3.49.0"
+ }
+ },
"node_modules/symbol-tree": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
diff --git a/package.json b/package.json
index 418df5d5e4..5fc5dbbff0 100644
--- a/package.json
+++ b/package.json
@@ -100,6 +100,7 @@
"plausible-tracker": "0.3.9",
"qr-creator": "^1.0.0",
"stream-browserify": "^3.0.0",
+ "sveltekit-i18n": "^2.4.2",
"ua-parser-js": "^1.0.35",
"zod": "^3.22.3"
},
diff --git a/src/frontend/src/lib/components/layout/LandingHeader.svelte b/src/frontend/src/lib/components/layout/LandingHeader.svelte
index f329edfe12..6b68f53334 100644
--- a/src/frontend/src/lib/components/layout/LandingHeader.svelte
+++ b/src/frontend/src/lib/components/layout/LandingHeader.svelte
@@ -1,8 +1,14 @@
+
{@render children?.()}
diff --git a/src/frontend/src/lib/utils/translations/de.json b/src/frontend/src/lib/utils/translations/de.json
new file mode 100644
index 0000000000..f67e19291f
--- /dev/null
+++ b/src/frontend/src/lib/utils/translations/de.json
@@ -0,0 +1,5 @@
+{
+ "landing": {
+ "experience": "Erleben"
+ }
+}
\ No newline at end of file
diff --git a/src/frontend/src/lib/utils/translations/en.json b/src/frontend/src/lib/utils/translations/en.json
new file mode 100644
index 0000000000..509b126b8e
--- /dev/null
+++ b/src/frontend/src/lib/utils/translations/en.json
@@ -0,0 +1,5 @@
+{
+ "landing": {
+ "experience": "Experience"
+ }
+}
\ No newline at end of file
diff --git a/src/frontend/src/lib/utils/translations/es.json b/src/frontend/src/lib/utils/translations/es.json
new file mode 100644
index 0000000000..2bec4f7c7c
--- /dev/null
+++ b/src/frontend/src/lib/utils/translations/es.json
@@ -0,0 +1,5 @@
+{
+ "landing": {
+ "experience": "Experiencia"
+ }
+}
\ No newline at end of file
diff --git a/src/frontend/src/lib/utils/translations/id.json b/src/frontend/src/lib/utils/translations/id.json
new file mode 100644
index 0000000000..5fe008e755
--- /dev/null
+++ b/src/frontend/src/lib/utils/translations/id.json
@@ -0,0 +1,5 @@
+{
+ "landing": {
+ "experience": "Pengalaman"
+ }
+}
\ No newline at end of file
diff --git a/src/frontend/src/lib/utils/translations/index.ts b/src/frontend/src/lib/utils/translations/index.ts
new file mode 100644
index 0000000000..1fa7fc1eb0
--- /dev/null
+++ b/src/frontend/src/lib/utils/translations/index.ts
@@ -0,0 +1,42 @@
+import i18n from "sveltekit-i18n";
+import type { Config } from "sveltekit-i18n";
+import lang from "./lang.json";
+
+const config: Config = {
+ translations: {
+ en: { lang },
+ es: { lang },
+ id: { lang },
+ de: { lang },
+ },
+ loaders: [
+ {
+ locale: "en",
+ key: "t",
+ loader: async () => (await import("./en.json")).default,
+ },
+ {
+ locale: "es",
+ key: "t",
+ loader: async () => (await import("./es.json")).default,
+ },
+ {
+ locale: "id",
+ key: "t",
+ loader: async () => (await import("./id.json")).default,
+ },
+ {
+ locale: "de",
+ key: "t",
+ loader: async () => (await import("./de.json")).default,
+ },
+ ],
+};
+
+export const { t, locale, locales, loading, loadTranslations } = new i18n(
+ config,
+);
+
+loading.subscribe(
+ ($loading) => $loading && console.log("Loading translations..."),
+);
diff --git a/src/frontend/src/lib/utils/translations/lang.json b/src/frontend/src/lib/utils/translations/lang.json
new file mode 100644
index 0000000000..fb30145f23
--- /dev/null
+++ b/src/frontend/src/lib/utils/translations/lang.json
@@ -0,0 +1,6 @@
+{
+ "en": "English",
+ "es": "Spanish",
+ "id": "Indonesian",
+ "de": "German"
+}
\ No newline at end of file
diff --git a/src/frontend/src/routes/(new-styling)/+layout.svelte b/src/frontend/src/routes/(new-styling)/+layout.svelte
index d417d929ad..adeef63638 100644
--- a/src/frontend/src/routes/(new-styling)/+layout.svelte
+++ b/src/frontend/src/routes/(new-styling)/+layout.svelte
@@ -4,8 +4,16 @@
import WaveCanvas from "$lib/components/backgrounds/WaveCanvas.svelte";
import Toaster from "$lib/components/utils/Toaster.svelte";
import type { LayoutProps } from "./$types";
+ import { loadTranslations, locale } from "$lib/utils/translations";
+ import { onMount } from "svelte";
const { children }: LayoutProps = $props();
+
+ onMount(async () => {
+ const initLocale = "en";
+
+ await loadTranslations(initLocale, "t");
+ });
diff --git a/src/frontend/src/routes/(new-styling)/+page.svelte b/src/frontend/src/routes/(new-styling)/+page.svelte
index 39e89b377f..22f830a844 100644
--- a/src/frontend/src/routes/(new-styling)/+page.svelte
+++ b/src/frontend/src/routes/(new-styling)/+page.svelte
@@ -21,6 +21,7 @@
} from "$lib/config";
import LandingHeader from "$lib/components/layout/LandingHeader.svelte";
import { fade } from "svelte/transition";
+ import { t } from "$lib/utils/translations";
const faq = [
{
@@ -126,7 +127,7 @@
- Experience
+ {$t("t.landing.experience", { default: "Experience" })}