feat: i18n
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://inlang.com/schema/message-format-plugin",
|
||||||
|
"hello_world": "Hallo Welt",
|
||||||
|
"pfadi": "Pfadi"
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://inlang.com/schema/message-format-plugin",
|
||||||
|
"hello_world": "Hello World",
|
||||||
|
"pfadi": "Scouts"
|
||||||
|
}
|
||||||
@@ -27,6 +27,8 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/outfit": "^5.0.13",
|
"@fontsource/outfit": "^5.0.13",
|
||||||
|
"@inlang/paraglide-js": "^1.11.0",
|
||||||
|
"@inlang/paraglide-sveltekit": "^0.16.1",
|
||||||
"@ts-stack/markdown": "^1.5.0",
|
"@ts-stack/markdown": "^1.5.0",
|
||||||
"mime-types": "^3.0.2",
|
"mime-types": "^3.0.2",
|
||||||
"svelte-preprocess": "^6.0.2",
|
"svelte-preprocess": "^6.0.2",
|
||||||
|
|||||||
Generated
+1216
-5
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://inlang.com/schema/project-settings",
|
||||||
|
"sourceLanguageTag": "de",
|
||||||
|
"languageTags": [
|
||||||
|
"de",
|
||||||
|
"en"
|
||||||
|
],
|
||||||
|
"modules": [
|
||||||
|
"https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@latest/dist/index.js",
|
||||||
|
"https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@latest/dist/index.js"
|
||||||
|
],
|
||||||
|
"plugin.inlang.messageFormat": {
|
||||||
|
"pathPattern": "./messages/{languageTag}.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
-10
@@ -1,12 +1,12 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="%paraglide.lang%" dir="%paraglide.textDirection%">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
</head>
|
</head>
|
||||||
<body data-sveltekit-preload-data="hover">
|
<body data-sveltekit-preload-data="hover">
|
||||||
<div style="display: contents">%sveltekit.body%</div>
|
<div style="display: contents">%sveltekit.body%</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import { i18n } from "$lib/i18n"
|
||||||
|
|
||||||
|
export const handle = i18n.handle()
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { createI18n } from "@inlang/paraglide-sveltekit"
|
||||||
|
import * as runtime from "$lib/paraglide/runtime"
|
||||||
|
|
||||||
|
export const i18n = createI18n(runtime, {
|
||||||
|
pathnames: {
|
||||||
|
"/about": {
|
||||||
|
en: "/about",
|
||||||
|
de: "/ueber-uns"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -54,8 +54,8 @@ export async function getCoverImage(slug: string): Promise<string | null> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPages() {
|
export async function getPages(lang: string = 'de') {
|
||||||
return withCache("pages", async () => {
|
return withCache(`pages:${lang}`, async () => {
|
||||||
try {
|
try {
|
||||||
const items = await client.getDirectoryContents("/");
|
const items = await client.getDirectoryContents("/");
|
||||||
|
|
||||||
@@ -71,7 +71,32 @@ export async function getPages() {
|
|||||||
const pages = await Promise.all(
|
const pages = await Promise.all(
|
||||||
directories.map(async (item: any) => {
|
directories.map(async (item: any) => {
|
||||||
const slug = item.basename;
|
const slug = item.basename;
|
||||||
const title = slug.charAt(0).toUpperCase() + slug.slice(1);
|
const baseName = slug.charAt(0).toUpperCase() + slug.slice(1);
|
||||||
|
const filename = lang === 'de' ? `${baseName}.md` : `${baseName}.${lang}.md`;
|
||||||
|
const path = `/${slug}/${filename}`;
|
||||||
|
|
||||||
|
let title = baseName;
|
||||||
|
try {
|
||||||
|
const content = await getPageContent(path);
|
||||||
|
const headerMatch = content.match(/^#{1,6}\s+(.+)$/m);
|
||||||
|
if (headerMatch) {
|
||||||
|
title = headerMatch[1];
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Fallback to German if English is missing
|
||||||
|
if (lang !== 'de') {
|
||||||
|
try {
|
||||||
|
const fallbackContent = await getPageContent(`/${slug}/${baseName}.md`);
|
||||||
|
const headerMatch = fallbackContent.match(/^#{1,6}\s+(.+)$/m);
|
||||||
|
if (headerMatch) {
|
||||||
|
title = headerMatch[1];
|
||||||
|
}
|
||||||
|
} catch (innerE) {
|
||||||
|
console.warn(`Could not fetch fallback title for ${slug}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const coverImage = await getCoverImage(slug);
|
const coverImage = await getCoverImage(slug);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import type { ParamMatcher } from '@sveltejs/kit';
|
||||||
|
|
||||||
|
export const match: ParamMatcher = (param) => {
|
||||||
|
return ['de', 'en'].includes(param);
|
||||||
|
};
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { LayoutServerLoad } from './$types';
|
import type { LayoutServerLoad } from './$types';
|
||||||
import { getPages } from '$lib/server/nextcloud';
|
import { getPages } from '$lib/server/nextcloud';
|
||||||
|
|
||||||
export const load: LayoutServerLoad = async () => {
|
export const load: LayoutServerLoad = async ({ params }) => {
|
||||||
const pages = await getPages();
|
const pages = await getPages(params.lang);
|
||||||
return {
|
return {
|
||||||
pages
|
pages
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,34 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import "../app.css";
|
import "../app.css";
|
||||||
import type { Snippet } from 'svelte';
|
import type { Snippet } from 'svelte';
|
||||||
|
import { ParaglideJS } from '@inlang/paraglide-sveltekit';
|
||||||
|
import { i18n } from '$lib/i18n';
|
||||||
|
import { languageTag } from '$lib/paraglide/runtime';
|
||||||
|
|
||||||
let { children }: { children?: Snippet } = $props();
|
let { children }: { children?: Snippet } = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<header class="fixed z-50 mix-blend-difference p-1">
|
<ParaglideJS {i18n}>
|
||||||
<a href="/" class="text-white text-2xl">schreifuchs.ch</a>
|
<header class="fixed z-50 mix-blend-difference p-1 w-full flex justify-between items-start">
|
||||||
</header>
|
<a href="/" class="text-white text-2xl">schreifuchs.ch</a>
|
||||||
{@render children?.()}
|
<nav class="flex gap-2 p-2">
|
||||||
|
<a
|
||||||
|
href="/"
|
||||||
|
hreflang="de"
|
||||||
|
data-no-translate
|
||||||
|
class="text-white hover:underline {languageTag() === 'de' ? 'font-bold' : ''}"
|
||||||
|
>
|
||||||
|
DE
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="/en"
|
||||||
|
hreflang="en"
|
||||||
|
data-no-translate
|
||||||
|
class="text-white hover:underline {languageTag() === 'en' ? 'font-bold' : ''}"
|
||||||
|
>
|
||||||
|
EN
|
||||||
|
</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
{@render children?.()}
|
||||||
|
</ParaglideJS>
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import ImageLinkTile from "$lib/components/ImageLinkTile.svelte";
|
import ImageLinkTile from "$lib/components/ImageLinkTile.svelte";
|
||||||
import type { PageData } from "./$types";
|
import type { PageData } from "./$types";
|
||||||
|
import { i18n } from "$lib/i18n";
|
||||||
|
import * as m from "$lib/paraglide/messages";
|
||||||
|
|
||||||
// Import local images to preserve existing design
|
// Import local images to preserve existing design
|
||||||
import mouse from "$lib/images/home/mouse.webp";
|
import mouse from "$lib/images/home/mouse.webp";
|
||||||
@@ -8,7 +10,6 @@
|
|||||||
import scouts from "$lib/images/home/scouts.webp";
|
import scouts from "$lib/images/home/scouts.webp";
|
||||||
import console_image from "$lib/images/home/console.webp";
|
import console_image from "$lib/images/home/console.webp";
|
||||||
import lauch from "$lib/images/home/lauch.webp";
|
import lauch from "$lib/images/home/lauch.webp";
|
||||||
import monitor from "$lib/images/informatik/monitor.webp"; // Using monitor for Informatik if console is not preferred
|
|
||||||
|
|
||||||
let { data }: { data: PageData } = $props();
|
let { data }: { data: PageData } = $props();
|
||||||
|
|
||||||
@@ -42,7 +43,7 @@
|
|||||||
<!-- Static/External Links -->
|
<!-- Static/External Links -->
|
||||||
<ImageLinkTile src={scouts} href="https://pfadifrisco.ch/" target="_blank">
|
<ImageLinkTile src={scouts} href="https://pfadifrisco.ch/" target="_blank">
|
||||||
<h2 class="text-3xl font-bold text-white hover:underline text-shadow-lg">
|
<h2 class="text-3xl font-bold text-white hover:underline text-shadow-lg">
|
||||||
Pfadi
|
{m.pfadi()}
|
||||||
</h2>
|
</h2>
|
||||||
</ImageLinkTile>
|
</ImageLinkTile>
|
||||||
</main>
|
</main>
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import { getCoverImage, getPageContent } from "$lib/server/nextcloud";
|
||||||
|
import type { PageServerLoad } from "./$types";
|
||||||
|
import { error } from "@sveltejs/kit";
|
||||||
|
|
||||||
|
export const load: PageServerLoad = async ({ params }) => {
|
||||||
|
const { slug, lang } = params;
|
||||||
|
const language = lang || 'de';
|
||||||
|
|
||||||
|
// Capitalize the first letter to match the convention: fotos -> Fotos.md
|
||||||
|
const baseName = slug.charAt(0).toUpperCase() + slug.slice(1);
|
||||||
|
const filename = language === 'de' ? `${baseName}.md` : `${baseName}.${language}.md`;
|
||||||
|
const path = `/${slug}/${filename}`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
let rawContent;
|
||||||
|
try {
|
||||||
|
rawContent = await getPageContent(path);
|
||||||
|
} catch (e) {
|
||||||
|
if (language !== 'de') {
|
||||||
|
console.warn(`Localized content ${filename} not found, falling back to German.`);
|
||||||
|
rawContent = await getPageContent(`/${slug}/${baseName}.md`);
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the first header (e.g., # Title or ## Title)
|
||||||
|
const headerMatch = rawContent.match(/^#{1,6}\s+(.+)$/m);
|
||||||
|
const title = headerMatch ? headerMatch[1] : baseName;
|
||||||
|
|
||||||
|
// Remove the first header from content so it's not rendered twice
|
||||||
|
const content = headerMatch
|
||||||
|
? rawContent.replace(headerMatch[0], "").trim()
|
||||||
|
: rawContent;
|
||||||
|
|
||||||
|
const coverImage = await getCoverImage(slug);
|
||||||
|
|
||||||
|
return {
|
||||||
|
content,
|
||||||
|
title,
|
||||||
|
slug,
|
||||||
|
coverImage,
|
||||||
|
lang: language
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Error fetching ${filename} from Nextcloud`, e);
|
||||||
|
throw error(404, "Page not found");
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import { getCoverImage, getPageContent } from "$lib/server/nextcloud";
|
|
||||||
import type { PageServerLoad } from "./$types";
|
|
||||||
import { error } from "@sveltejs/kit";
|
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ params }) => {
|
|
||||||
const { slug } = params;
|
|
||||||
// Capitalize the first letter to match the convention: fotos -> Fotos.md
|
|
||||||
const filename = slug.charAt(0).toUpperCase() + slug.slice(1) + ".md";
|
|
||||||
const path = `/${slug}/${filename}`;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const [content, coverImage] = await Promise.all([
|
|
||||||
getPageContent(path),
|
|
||||||
getCoverImage(slug)
|
|
||||||
]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
content,
|
|
||||||
title: slug.charAt(0).toUpperCase() + slug.slice(1),
|
|
||||||
slug,
|
|
||||||
coverImage
|
|
||||||
};
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`Error fetching ${filename} from Nextcloud`, e);
|
|
||||||
throw error(404, "Page not found");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
+8
-1
@@ -1,6 +1,13 @@
|
|||||||
|
import { paraglide } from '@inlang/paraglide-sveltekit/vite';
|
||||||
import { sveltekit } from '@sveltejs/kit/vite';
|
import { sveltekit } from '@sveltejs/kit/vite';
|
||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [sveltekit()]
|
plugins: [
|
||||||
|
paraglide({
|
||||||
|
project: './project.inlang',
|
||||||
|
outdir: './src/lib/paraglide'
|
||||||
|
}),
|
||||||
|
sveltekit()
|
||||||
|
]
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user