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",
|
||||
"nextjs",
|
||||
"Obispo",
|
||||
"paraglide",
|
||||
"selfhosting",
|
||||
"Syncthing",
|
||||
"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",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"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 .",
|
||||
"format": "prettier --plugin-search-dir . --write .",
|
||||
"test:integration": "playwright test",
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"$schema": "https://inlang.com/schema/project-settings",
|
||||
"sourceLanguageTag": "en",
|
||||
"languageTags": [
|
||||
"en"
|
||||
"en", "es"
|
||||
],
|
||||
"modules": [
|
||||
"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"
|
||||
],
|
||||
"plugin.inlang.messageFormat": {
|
||||
"pathPattern": "./messages/{languageTag}.json"
|
||||
"pathPattern": "./languages/{languageTag}.json"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!-- Added Placeholders for lang & dir attributes. These get replaced in `hooks.server.ts` -->
|
||||
<html lang="%lang%" dir="%textDir%">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<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">
|
||||
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>
|
||||
|
||||
<header aria-label="header navigation">
|
||||
<nav>
|
||||
<a href="/" class:active={$page.url.pathname === '/'}>Home</a>
|
||||
<a href='/' class:active={pathname === '/'}>{m.nav_home()}</a>
|
||||
<a
|
||||
href="/about"
|
||||
class:active={$page.url.pathname === '/about'}
|
||||
href={`/${lang}/${m.nav_about_link()}`}
|
||||
class:active={pathname === `/${m.nav_about_link()}`}
|
||||
>
|
||||
About
|
||||
{m.nav_about()}
|
||||
</a>
|
||||
<a
|
||||
href="/portfolio"
|
||||
class:active={$page.url.pathname === '/portfolio'}
|
||||
href={`/${lang}/${m.nav_portfolio_link()}`}
|
||||
class:active={pathname === `/${m.nav_portfolio_link()}`}
|
||||
>
|
||||
Portfolio
|
||||
{m.nav_portfolio()}
|
||||
</a>
|
||||
<a
|
||||
href="/uses"
|
||||
class:active={$page.url.pathname === '/uses'}
|
||||
href={`/${lang}/${m.nav_uses_link()}`}
|
||||
class:active={pathname === `/${m.nav_uses_link()}`}
|
||||
>
|
||||
Uses
|
||||
{m.nav_uses()}
|
||||
</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>
|
||||
</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 "nprogress/nprogress.css";
|
||||
import '$root/styles/styles.pcss';
|
||||
import { setLanguageTag, sourceLanguageTag, type AvailableLanguageTag, availableLanguageTags } from "$paraglide/runtime";
|
||||
import Header from '$lib/components/header/index.svelte';
|
||||
import Footer from '$lib/components/footer/index.svelte';
|
||||
import Analytics from '$lib/components/analytics/index.svelte';
|
||||
import { getTextDirection, translatePath } from '$lib/i18n';
|
||||
|
||||
NProgress.configure({
|
||||
// Full list: https://github.com/rstacruz/nprogress#configuration
|
||||
|
|
@ -37,6 +39,26 @@
|
|||
],
|
||||
...$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>
|
||||
|
||||
{#if !dev}
|
||||
|
|
@ -45,12 +67,20 @@
|
|||
|
||||
<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">
|
||||
<Header />
|
||||
<main>
|
||||
<slot />
|
||||
</main>
|
||||
<Footer />
|
||||
{#key lang}
|
||||
<Header />
|
||||
<main>
|
||||
<slot />
|
||||
</main>
|
||||
<Footer />
|
||||
{/key}
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<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 Articles from '$lib/components/Articles.svelte';
|
||||
import type { Album } from '$lib/types/album';
|
||||
|
|
@ -22,14 +23,13 @@
|
|||
|
||||
<div class="home">
|
||||
<div>
|
||||
<h1>Hello! I'm Bradley Shellnut.</h1>
|
||||
<h1>{m.home_title()}</h1>
|
||||
</div>
|
||||
<p>
|
||||
I'm a full stack software engineer currently working on Java Spring, PostgreSQL, and React / Angular JS.
|
||||
{m.home_about()}
|
||||
</p>
|
||||
<p>
|
||||
At home you can usually find me learning new things and working with
|
||||
SvelteKit, Next.js, and Gatsby.
|
||||
{m.home_learning()}
|
||||
</p>
|
||||
<p>
|
||||
Or you may find me jamming out to music 🎶, hiking ⛰️, making{' '}
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
import Remix from '@iconify-icons/simple-icons/remix';
|
||||
import Svelte from '@iconify-icons/simple-icons/svelte';
|
||||
import TypeScript from '@iconify-icons/simple-icons/typescript';
|
||||
import * as m from "$paraglide/messages";
|
||||
import LazyImage from '$lib/components/LazyImage.svelte';
|
||||
import antarctica from '$lib/assets/images/antarctica.png?as=run:0';
|
||||
import tortie_derp from '$lib/assets/images/tortie_derp.jpg?as=run';
|
||||
|
|
@ -24,7 +25,7 @@
|
|||
<div class="about">
|
||||
<div>
|
||||
<h1>About</h1>
|
||||
<p>Hey! My name is Bradley Shellnut.</p>
|
||||
<p>{m.about_whoami()}</p>
|
||||
<p>
|
||||
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
|
||||
|
|
@ -23,8 +23,14 @@ const config = {
|
|||
adapter: adapter(),
|
||||
alias: {
|
||||
$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