mirror of
https://github.com/BradNut/personal-website-sveltekit
synced 2025-09-08 23:20:18 +00:00
Creating lang route, moving files under, setting up the i18n with en and es, and starting the message transition.
This commit is contained in:
parent
5241572b76
commit
e2a6a38ef5
32 changed files with 201 additions and 26 deletions
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
|
@ -7,6 +7,7 @@
|
||||||
"Mullvad",
|
"Mullvad",
|
||||||
"nextjs",
|
"nextjs",
|
||||||
"Obispo",
|
"Obispo",
|
||||||
|
"paraglide",
|
||||||
"selfhosting",
|
"selfhosting",
|
||||||
"Syncthing",
|
"Syncthing",
|
||||||
"Wallabag"
|
"Wallabag"
|
||||||
|
|
|
||||||
18
languages/en.json
Normal file
18
languages/en.json
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://inlang.com/schema/inlang-message-format",
|
||||||
|
"home_title": "Hello! I'm Bradley Shellnut.",
|
||||||
|
"home_about": "I'm a full stack software engineer currently working on Java Spring, PostgreSQL, and React / Angular JS.",
|
||||||
|
"home_learning": "At home you can usually find me learning new things and working with SvelteKit, Next.js, and Gatsby.",
|
||||||
|
"nav_home": "Home",
|
||||||
|
"nav_about": "About",
|
||||||
|
"nav_about_link": "about",
|
||||||
|
"nav_uses": "Uses",
|
||||||
|
"nav_uses_link": "uses",
|
||||||
|
"nav_articles": "Articles",
|
||||||
|
"nav_articles_link": "articles",
|
||||||
|
"nav_portfolio": "Portfolio",
|
||||||
|
"nav_portfolio_link": "portfolio",
|
||||||
|
"nav_privacy": "Privacy",
|
||||||
|
"nav_privacy_link": "privacy",
|
||||||
|
"about_whoami": "Hey! My name is Bradley Shellnut."
|
||||||
|
}
|
||||||
18
languages/es.json
Normal file
18
languages/es.json
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://inlang.com/schema/inlang-message-format",
|
||||||
|
"home_title": "!Hola! Me llamo Bradley Shellnut.",
|
||||||
|
"home_about": "Soy un ingeniero de software de pila completa que actualmente trabaja en Java Spring, PostgreSQL y React / Angular JS.",
|
||||||
|
"home_learning": "En casa por lo general me puedes encontrar aprendiendo cosas nuevas y trabajando con SvelteKit, Next.js, y Gatsby.",
|
||||||
|
"nav_home": "Inicio",
|
||||||
|
"nav_about": "Acerca de",
|
||||||
|
"nav_about_link": "acerca-de",
|
||||||
|
"nav_uses": "Utiliza",
|
||||||
|
"nav_uses_link": "utiliza",
|
||||||
|
"nav_articles": "Artículos",
|
||||||
|
"nav_articles_link": "articulos",
|
||||||
|
"nav_portfolio": "Cartera",
|
||||||
|
"nav_portfolio_link": "cartera",
|
||||||
|
"nav_privacy": "Privacidad",
|
||||||
|
"nav_privacy_link": "privacidad",
|
||||||
|
"about_whoami": "¡Hola! Me llamo Bradley Shellnut."
|
||||||
|
}
|
||||||
|
|
@ -10,6 +10,8 @@
|
||||||
"test:ui": "svelte-kit sync && playwright test --ui",
|
"test:ui": "svelte-kit sync && playwright test --ui",
|
||||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||||
|
"compile": "paraglide-js compile --project ./project.inlang",
|
||||||
|
"watch": "paraglide-js compile --project ./project.inlang --watch",
|
||||||
"lint": "prettier --plugin-search-dir . --check . && eslint .",
|
"lint": "prettier --plugin-search-dir . --check . && eslint .",
|
||||||
"format": "prettier --plugin-search-dir . --write .",
|
"format": "prettier --plugin-search-dir . --write .",
|
||||||
"test:integration": "playwright test",
|
"test:integration": "playwright test",
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"$schema": "https://inlang.com/schema/project-settings",
|
"$schema": "https://inlang.com/schema/project-settings",
|
||||||
"sourceLanguageTag": "en",
|
"sourceLanguageTag": "en",
|
||||||
"languageTags": [
|
"languageTags": [
|
||||||
"en"
|
"en", "es"
|
||||||
],
|
],
|
||||||
"modules": [
|
"modules": [
|
||||||
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-empty-pattern@latest/dist/index.js",
|
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-empty-pattern@latest/dist/index.js",
|
||||||
|
|
@ -14,6 +14,6 @@
|
||||||
"https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@latest/dist/index.js"
|
"https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@latest/dist/index.js"
|
||||||
],
|
],
|
||||||
"plugin.inlang.messageFormat": {
|
"plugin.inlang.messageFormat": {
|
||||||
"pathPattern": "./messages/{languageTag}.json"
|
"pathPattern": "./languages/{languageTag}.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<!-- Added Placeholders for lang & dir attributes. These get replaced in `hooks.server.ts` -->
|
||||||
|
<html lang="%lang%" dir="%textDir%">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%sveltekit.assets%/b_shell_nut_favicon.gif" />
|
<link rel="icon" href="%sveltekit.assets%/b_shell_nut_favicon.gif" />
|
||||||
|
|
|
||||||
21
src/hooks.server.ts
Normal file
21
src/hooks.server.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { getTextDirection } from "$lib/i18n"
|
||||||
|
import { sourceLanguageTag, type AvailableLanguageTag } from "$paraglide/runtime"
|
||||||
|
|
||||||
|
/*
|
||||||
|
We set the `lang` and `dir` attributes on the `<html>` element using a hook.
|
||||||
|
the `app.html` file contains placeholders for these attributes, which we just find and replace.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export async function handle({ event, resolve }) {
|
||||||
|
const lang: AvailableLanguageTag =
|
||||||
|
(event.params.lang as AvailableLanguageTag) ?? sourceLanguageTag
|
||||||
|
const textDirection = getTextDirection(lang)
|
||||||
|
|
||||||
|
return await resolve(event, {
|
||||||
|
transformPageChunk({ done, html }) {
|
||||||
|
if (done) {
|
||||||
|
return html.replace("%lang%", lang).replace("%textDir%", textDirection)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
20
src/hooks.ts
Normal file
20
src/hooks.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
import type { Reroute } from '@sveltejs/kit';
|
||||||
|
|
||||||
|
const translated: Record<string, string> = {
|
||||||
|
'/en/about': '/en/about',
|
||||||
|
'/en/uses': '/en/uses',
|
||||||
|
'/en/articles': '/en/articles',
|
||||||
|
'/en/portfolio': '/en/portfolio',
|
||||||
|
'/en/privacy': '/en/privacy',
|
||||||
|
'/es/acerca-de': '/es/about',
|
||||||
|
'/es/utiliza': '/es/uses',
|
||||||
|
'/es/articulos': '/es/articles',
|
||||||
|
'/es/cartera': '/es/portfolio',
|
||||||
|
'/es/privacidad': '/es/privacy'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const reroute: Reroute = ({ url }) => {
|
||||||
|
if (url.pathname in translated) {
|
||||||
|
return translated[url.pathname];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,28 +1,40 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
import { translatePath } from '$lib/i18n';
|
||||||
|
import { availableLanguageTags, languageTag } from '$paraglide/runtime';
|
||||||
|
import * as m from "$paraglide/messages";
|
||||||
|
|
||||||
|
$: pathname = $page.url.pathname;
|
||||||
|
$: lang = languageTag();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<header aria-label="header navigation">
|
<header aria-label="header navigation">
|
||||||
<nav>
|
<nav>
|
||||||
<a href="/" class:active={$page.url.pathname === '/'}>Home</a>
|
<a href='/' class:active={pathname === '/'}>{m.nav_home()}</a>
|
||||||
<a
|
<a
|
||||||
href="/about"
|
href={`/${lang}/${m.nav_about_link()}`}
|
||||||
class:active={$page.url.pathname === '/about'}
|
class:active={pathname === `/${m.nav_about_link()}`}
|
||||||
>
|
>
|
||||||
About
|
{m.nav_about()}
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="/portfolio"
|
href={`/${lang}/${m.nav_portfolio_link()}`}
|
||||||
class:active={$page.url.pathname === '/portfolio'}
|
class:active={pathname === `/${m.nav_portfolio_link()}`}
|
||||||
>
|
>
|
||||||
Portfolio
|
{m.nav_portfolio()}
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="/uses"
|
href={`/${lang}/${m.nav_uses_link()}`}
|
||||||
class:active={$page.url.pathname === '/uses'}
|
class:active={pathname === `/${m.nav_uses_link()}`}
|
||||||
>
|
>
|
||||||
Uses
|
{m.nav_uses()}
|
||||||
</a>
|
</a>
|
||||||
|
<select on:change={(e) => goto(translatePath(pathname, e?.target?.value)) }>
|
||||||
|
{#each availableLanguageTags as lang}
|
||||||
|
<option value={lang} selected={lang === languageTag()}>{lang}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|
|
||||||
43
src/lib/i18n.ts
Normal file
43
src/lib/i18n.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
import {
|
||||||
|
sourceLanguageTag,
|
||||||
|
availableLanguageTags,
|
||||||
|
type AvailableLanguageTag,
|
||||||
|
} from "$paraglide/runtime"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes in a path with or without a language tag and returns the path with the given language tag.
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function translatePath(path: string, lang: AvailableLanguageTag) {
|
||||||
|
path = getPathWithoutLang(path)
|
||||||
|
|
||||||
|
// Don't prefix with the source language tag, that's the default
|
||||||
|
if (lang === sourceLanguageTag) return path
|
||||||
|
// Otherwise, prefix with the language tag
|
||||||
|
else return `/${lang}${path}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the language tag from the path, if it exists.
|
||||||
|
*/
|
||||||
|
function getPathWithoutLang(path: string) {
|
||||||
|
const [_, maybeLang, ...rest] = path.split("/")
|
||||||
|
if (availableLanguageTags.includes(maybeLang as any)) {
|
||||||
|
return `/${rest.join("/")}`;
|
||||||
|
} else {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Look up the text direction for a given locale.
|
||||||
|
* You could use a Polyfill for `Intl.Locale.prototype.getTextInfo` instead.
|
||||||
|
*/
|
||||||
|
export function getTextDirection(locale: AvailableLanguageTag) {
|
||||||
|
const directions: Record<AvailableLanguageTag, "ltr" | "rtl"> = {
|
||||||
|
en: "ltr",
|
||||||
|
es: "ltr",
|
||||||
|
}
|
||||||
|
|
||||||
|
return directions[locale]
|
||||||
|
}
|
||||||
2
src/params/lang.ts
Normal file
2
src/params/lang.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
import { isAvailableLanguageTag } from "$paraglide/runtime"
|
||||||
|
export const match = isAvailableLanguageTag
|
||||||
|
|
@ -7,9 +7,11 @@
|
||||||
import { PUBLIC_SITE_URL } from '$env/static/public';
|
import { PUBLIC_SITE_URL } from '$env/static/public';
|
||||||
import "nprogress/nprogress.css";
|
import "nprogress/nprogress.css";
|
||||||
import '$root/styles/styles.pcss';
|
import '$root/styles/styles.pcss';
|
||||||
|
import { setLanguageTag, sourceLanguageTag, type AvailableLanguageTag, availableLanguageTags } from "$paraglide/runtime";
|
||||||
import Header from '$lib/components/header/index.svelte';
|
import Header from '$lib/components/header/index.svelte';
|
||||||
import Footer from '$lib/components/footer/index.svelte';
|
import Footer from '$lib/components/footer/index.svelte';
|
||||||
import Analytics from '$lib/components/analytics/index.svelte';
|
import Analytics from '$lib/components/analytics/index.svelte';
|
||||||
|
import { getTextDirection, translatePath } from '$lib/i18n';
|
||||||
|
|
||||||
NProgress.configure({
|
NProgress.configure({
|
||||||
// Full list: https://github.com/rstacruz/nprogress#configuration
|
// Full list: https://github.com/rstacruz/nprogress#configuration
|
||||||
|
|
@ -37,6 +39,26 @@
|
||||||
],
|
],
|
||||||
...$page.data.metaTagsChild
|
...$page.data.metaTagsChild
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determine the current language from the URL. Fall back to the source language if none is specified.
|
||||||
|
$: lang = $page.params.lang as AvailableLanguageTag ?? sourceLanguageTag
|
||||||
|
|
||||||
|
console.log('lang', lang)
|
||||||
|
|
||||||
|
// Set the language tag in the Paraglide runtime.
|
||||||
|
// This determines which language the strings are translated to.
|
||||||
|
// You should only do this in the template, to avoid concurrent requests interfering with each other.
|
||||||
|
$: setLanguageTag(lang)
|
||||||
|
|
||||||
|
|
||||||
|
// Determine the text direction of the current language
|
||||||
|
$: textDirection = getTextDirection(lang)
|
||||||
|
|
||||||
|
// Keep the <html> lang and dir attributes in sync with the current language
|
||||||
|
$: if (browser) {
|
||||||
|
document.documentElement.dir = textDirection
|
||||||
|
document.documentElement.lang = lang
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if !dev}
|
{#if !dev}
|
||||||
|
|
@ -45,12 +67,20 @@
|
||||||
|
|
||||||
<MetaTags {...metaTags} />
|
<MetaTags {...metaTags} />
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
{#each availableLanguageTags as lang}
|
||||||
|
<link rel="alternate" hreflang={lang} href={translatePath($page.url.pathname, lang)} />
|
||||||
|
{/each}
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<Header />
|
{#key lang}
|
||||||
<main>
|
<Header />
|
||||||
<slot />
|
<main>
|
||||||
</main>
|
<slot />
|
||||||
<Footer />
|
</main>
|
||||||
|
<Footer />
|
||||||
|
{/key}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="postcss">
|
<style lang="postcss">
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from '../$types';
|
||||||
|
import * as m from "$paraglide/messages";
|
||||||
import Bandcamp from '$lib/components/bandcamp/index.svelte';
|
import Bandcamp from '$lib/components/bandcamp/index.svelte';
|
||||||
import Articles from '$lib/components/Articles.svelte';
|
import Articles from '$lib/components/Articles.svelte';
|
||||||
import type { Album } from '$lib/types/album';
|
import type { Album } from '$lib/types/album';
|
||||||
|
|
@ -22,14 +23,13 @@
|
||||||
|
|
||||||
<div class="home">
|
<div class="home">
|
||||||
<div>
|
<div>
|
||||||
<h1>Hello! I'm Bradley Shellnut.</h1>
|
<h1>{m.home_title()}</h1>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
I'm a full stack software engineer currently working on Java Spring, PostgreSQL, and React / Angular JS.
|
{m.home_about()}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
At home you can usually find me learning new things and working with
|
{m.home_learning()}
|
||||||
SvelteKit, Next.js, and Gatsby.
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Or you may find me jamming out to music 🎶, hiking ⛰️, making{' '}
|
Or you may find me jamming out to music 🎶, hiking ⛰️, making{' '}
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
import Remix from '@iconify-icons/simple-icons/remix';
|
import Remix from '@iconify-icons/simple-icons/remix';
|
||||||
import Svelte from '@iconify-icons/simple-icons/svelte';
|
import Svelte from '@iconify-icons/simple-icons/svelte';
|
||||||
import TypeScript from '@iconify-icons/simple-icons/typescript';
|
import TypeScript from '@iconify-icons/simple-icons/typescript';
|
||||||
|
import * as m from "$paraglide/messages";
|
||||||
import LazyImage from '$lib/components/LazyImage.svelte';
|
import LazyImage from '$lib/components/LazyImage.svelte';
|
||||||
import antarctica from '$lib/assets/images/antarctica.png?as=run:0';
|
import antarctica from '$lib/assets/images/antarctica.png?as=run:0';
|
||||||
import tortie_derp from '$lib/assets/images/tortie_derp.jpg?as=run';
|
import tortie_derp from '$lib/assets/images/tortie_derp.jpg?as=run';
|
||||||
|
|
@ -24,7 +25,7 @@
|
||||||
<div class="about">
|
<div class="about">
|
||||||
<div>
|
<div>
|
||||||
<h1>About</h1>
|
<h1>About</h1>
|
||||||
<p>Hey! My name is Bradley Shellnut.</p>
|
<p>{m.about_whoami()}</p>
|
||||||
<p>
|
<p>
|
||||||
I'm {new Date().getFullYear() - 1991} years old and I am a full stack
|
I'm {new Date().getFullYear() - 1991} years old and I am a full stack
|
||||||
software engineer who's interested in new tech and not afraid to
|
software engineer who's interested in new tech and not afraid to
|
||||||
|
|
@ -23,8 +23,14 @@ const config = {
|
||||||
adapter: adapter(),
|
adapter: adapter(),
|
||||||
alias: {
|
alias: {
|
||||||
$root: './src',
|
$root: './src',
|
||||||
$paraglide: './src/paraglide'
|
$paraglide: './src/paraglide/'
|
||||||
}
|
},
|
||||||
|
// Need for crawling to work until
|
||||||
|
// https://github.com/sveltejs/kit/issues/11133
|
||||||
|
// is fixed
|
||||||
|
prerender: {
|
||||||
|
entries: ["/"],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue