16 Commits

Author SHA1 Message Date
d160e6d1ca 🐛 Fix language not switching on dynamic pages
All checks were successful
Build and deploy website / build (push) Successful in 34s
2025-07-28 18:17:18 +00:00
3840766ad5 Fixed language not switching 2025-07-28 18:17:18 +00:00
e06ef027fd Update TODO
Signed-off-by: Martin Berg Alstad <git@martials.no>
2025-07-28 18:17:18 +00:00
e5a95e0c97 Start updating paraglide to v2 2025-07-28 18:17:18 +00:00
c9cb20cd38 🔥 Remove prettier 2025-07-28 18:17:18 +00:00
a77266b683 Update git domain 2025-07-28 18:17:18 +00:00
15440bb912 🎨 Fix lint errors using Biome 2025-07-28 18:17:18 +00:00
fafc48cfce 🎨 Format using Biome 2025-07-28 18:17:18 +00:00
cae5e408bd Adde Biome formatter and linter 2025-07-28 18:17:18 +00:00
869074dd89 ❄️ Add Nix shell 2025-07-28 18:17:18 +00:00
1892d38d18 📦 Update dependencies 2025-07-28 18:17:18 +00:00
d5ca576068 ✍️ Update TODO.md 2025-07-28 18:17:18 +00:00
b1061e56c6 🐛 Fix trailing slash 2025-07-28 18:17:18 +00:00
1fa2667deb 👷 Merge into single docker compose file
All checks were successful
Build and deploy website / build (push) Successful in 22s
2025-07-03 18:54:54 +02:00
19d78991c8 👷 Update ref keys and master to develop on develop step
All checks were successful
Build and deploy website / build (push) Successful in 1m3s
2025-07-03 18:39:42 +02:00
1dadaabe94 👷 Staging from develop branch
All checks were successful
Build and deploy website / build (push) Successful in 2s
2025-07-03 18:34:49 +02:00
17 changed files with 96 additions and 976 deletions

View File

@ -9,9 +9,10 @@
- [ ] Type slug of project - [ ] Type slug of project
## CI/CD ## CI/CD
- [ ] Staging environment - [x] Staging environment
- [ ] Deploy to staging environment on push to master - [x] Deploy to staging environment on push to develop
- [ ] Deploy to production environment on push tag to master - [x] Deploy to production environment on push to master
- [ ] Staging environment .env file
## SEO ## SEO
- [ ] Meta tags on each page - [ ] Meta tags on each page

View File

@ -1,6 +1,5 @@
// @ts-check // @ts-check
import { defineConfig, envField } from "astro/config" import { defineConfig, envField } from "astro/config"
import paraglide from "@inlang/paraglide-astro"
import tailwindcss from "@tailwindcss/vite" import tailwindcss from "@tailwindcss/vite"
import sitemap from "@astrojs/sitemap" import sitemap from "@astrojs/sitemap"
import svelte from "@astrojs/svelte" import svelte from "@astrojs/svelte"
@ -9,6 +8,7 @@ import mdx from "@astrojs/mdx"
import icon from "astro-icon" import icon from "astro-icon"
import { loadEnv } from "vite" import { loadEnv } from "vite"
import { paraglideVitePlugin } from "@inlang/paraglide-js"
const { URL } = process.env.NODE_ENV const { URL } = process.env.NODE_ENV
? loadEnv(process.env.NODE_ENV, process.cwd(), "") ? loadEnv(process.env.NODE_ENV, process.cwd(), "")
@ -20,35 +20,37 @@ export default defineConfig({
output: "server", output: "server",
i18n: { i18n: {
defaultLocale: "nb", defaultLocale: "nb",
locales: ["nb", "en"], locales: ["nb", "en"]
}, },
integrations: [ integrations: [
sitemap(), sitemap(),
mdx(), mdx(),
svelte(), svelte(),
icon(), icon()
paraglide({
project: "./project.inlang",
outdir: "./src/paraglide",
}),
], ],
adapter: node({ adapter: node({
mode: "standalone", mode: "standalone"
}), }),
vite: { vite: {
plugins: [tailwindcss()], plugins: [
tailwindcss(),
paraglideVitePlugin({
project: "./project.inlang",
outdir: "./src/paraglide"
})
]
}, },
markdown: { markdown: {
shikiConfig: { shikiConfig: {
theme: "catppuccin-mocha", theme: "catppuccin-mocha"
}, }
}, },
env: { env: {
schema: { schema: {
DOMAIN: envField.string({ context: "client", access: "public" }), DOMAIN: envField.string({ context: "client", access: "public" }),
URL: envField.string({ context: "client", access: "public" }), URL: envField.string({ context: "client", access: "public" }),
GIT_URL: envField.string({ context: "client", access: "public" }), GIT_URL: envField.string({ context: "client", access: "public" }),
STATUS_URL: envField.string({ context: "client", access: "public" }), STATUS_URL: envField.string({ context: "client", access: "public" })
}, }
}, }
}) })

View File

@ -22,7 +22,6 @@
"@astrojs/sitemap": "^3.4.1", "@astrojs/sitemap": "^3.4.1",
"@astrojs/svelte": "^7.1.0", "@astrojs/svelte": "^7.1.0",
"@iconify-json/pajamas": "^1.2.11", "@iconify-json/pajamas": "^1.2.11",
"@inlang/paraglide-astro": "^0.4.1",
"@inlang/paraglide-js": "2.1.0", "@inlang/paraglide-js": "2.1.0",
"@tailwindcss/typography": "^0.5.16", "@tailwindcss/typography": "^0.5.16",
"@tailwindcss/vite": "^4.1.11", "@tailwindcss/vite": "^4.1.11",

858
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,15 +1,15 @@
{ {
"$schema": "https://inlang.com/schema/project-settings", "$schema": "https://inlang.com/schema/project-settings",
"sourceLanguageTag": "nb", "baseLocale": "nb",
"languageTags": ["nb", "en"], "locales": [
"nb",
"en"
],
"modules": [ "modules": [
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-empty-pattern@latest/dist/index.js", "https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@4/dist/index.js",
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-missing-translation@latest/dist/index.js", "https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2/dist/index.js"
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-without-source@latest/dist/index.js",
"https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@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": "./messages/{locale}.json"
} }
} }

View File

@ -1,8 +1,9 @@
--- ---
import { type NavLink, resolvePathname } from "@/utils/linking" import { type NavLink } from "@/utils/linking"
import LocaleLink from "@/components/links/LocaleLink.astro" import LocaleLink from "@/components/links/LocaleLink.astro"
import { deLocalizeHref } from "@/paraglide/runtime.js"
const pathname = resolvePathname(Astro.originPathname) const pathname = deLocalizeHref(Astro.originPathname)
let paths: string[] let paths: string[]
if (pathname === "/") { if (pathname === "/") {
@ -25,13 +26,13 @@ function getLink(path: string): NavLink {
{ {
paths.map((path, index) => ( paths.map((path, index) => (
<span> <span>
{index != paths.length - 1 ? ( { index != paths.length - 1 ? (
<span> <span>
<LocaleLink to={getLink(path)}>{path}</LocaleLink>/ <LocaleLink to={ getLink(path) }>{ path }</LocaleLink>/
</span> </span>
) : ( ) : (
path path
)} ) }
</span> </span>
)) ))
} }

View File

@ -2,11 +2,12 @@
import GiteaLink from "./links/GiteaLink.astro" import GiteaLink from "./links/GiteaLink.astro"
import PajamasIcon from "./icons/PajamasIcon.astro" import PajamasIcon from "./icons/PajamasIcon.astro"
import ExternalLink from "./links/ExternalLink.astro" import ExternalLink from "./links/ExternalLink.astro"
import LanguageButtonGroup from "./LanguageButtonGroup.astro" import LanguageButtonGroup from "./LanguageButtonGroup.svelte"
import { GIT_URL, STATUS_URL } from "astro:env/client" import { GIT_URL, STATUS_URL } from "astro:env/client"
import * as m from "@/paraglide/messages" import * as m from "@/paraglide/messages"
const giteaLink = `${GIT_URL}/martials/martials.no` const giteaLink = `${GIT_URL}/martials/martials.no`
const pathname = Astro.url.pathname
--- ---
<div class="divider bg-inherit"></div> <div class="divider bg-inherit"></div>
@ -28,5 +29,5 @@ const giteaLink = `${GIT_URL}/martials/martials.no`
{m.status()} {m.status()}
</ExternalLink> </ExternalLink>
</div> </div>
<LanguageButtonGroup /> <LanguageButtonGroup client:load />
</div> </div>

View File

@ -1,9 +1,10 @@
--- ---
import LocaleLink from "./links/LocaleLink.astro" import LocaleLink from "./links/LocaleLink.astro"
import { type NavLink, resolvePathname } from "@/utils/linking" import { type NavLink } from "@/utils/linking"
import { deLocalizeHref } from "@/paraglide/runtime"
const pathname = Astro.url.pathname const pathname = Astro.url.pathname
const currentPath = resolvePathname(pathname) const currentPath = deLocalizeHref(pathname)
const isEnglish = pathname.startsWith("/en") const isEnglish = pathname.startsWith("/en")
--- ---
@ -14,7 +15,7 @@ const isEnglish = pathname.startsWith("/en")
class:list={[ class:list={[
"btn join-item !text-cat-text border-cat-surface0", "btn join-item !text-cat-text border-cat-surface0",
!isEnglish ? "bg-cat-mantle" : "bg-cat-base", !isEnglish ? "bg-cat-mantle" : "bg-cat-base",
]}>Norsk</LocaleLink ]} client:load>Norsk</LocaleLink
> >
<LocaleLink <LocaleLink
to={currentPath as NavLink} to={currentPath as NavLink}
@ -22,6 +23,6 @@ const isEnglish = pathname.startsWith("/en")
class:list={[ class:list={[
"btn join-item !text-cat-text border-cat-surface0", "btn join-item !text-cat-text border-cat-surface0",
isEnglish ? "bg-cat-mantle" : "bg-cat-base", isEnglish ? "bg-cat-mantle" : "bg-cat-base",
]}>English</LocaleLink ]} client:load>English</LocaleLink
> >
</div> </div>

View File

@ -0,0 +1,29 @@
<script lang="ts">
import { getLocale, type Locale, setLocale } from "@/paraglide/runtime"
const isEnglish = getLocale() === "en"
function updateLocale(lang: Locale) {
setLocale(lang)
}
</script>
<div class="join">
<button
onclick={() => updateLocale("nb")}
class={[
"btn join-item !text-cat-text border-cat-surface0",
!isEnglish ? "bg-cat-mantle" : "bg-cat-base",
]}>Norsk
</button
>
<button
onclick={() => updateLocale("en")}
class={[
"btn join-item !text-cat-text border-cat-surface0",
isEnglish ? "bg-cat-mantle" : "bg-cat-base",
]}>English
</button
>
</div>

View File

@ -3,9 +3,9 @@ import Navbar from "./Navbar.astro"
import NavbarDrawer from "./NavbarDrawer.astro" import NavbarDrawer from "./NavbarDrawer.astro"
import HamburgerMenuButton from "./HamburgerMenuButton.astro" import HamburgerMenuButton from "./HamburgerMenuButton.astro"
import Breadcrumb from "../Breadcrumb.astro" import Breadcrumb from "../Breadcrumb.astro"
import { resolvePathname } from "@/utils/linking" import { deLocalizeHref } from "@/paraglide/runtime"
const currentPath = `~${resolvePathname(Astro.originPathname)}` const currentPath = `~${deLocalizeHref(Astro.originPathname)}`
const drawerToggleId = "header-drawer" const drawerToggleId = "header-drawer"
--- ---

View File

@ -1,16 +1,17 @@
--- ---
import { languageTag, type AvailableLanguageTag } from "@/paraglide/runtime" import { localizeHref, getLocale, type Locale } from "@/paraglide/runtime"
import { localizePathname, type NavLink } from "@/utils/linking" import { type NavLink } from "@/utils/linking"
import type { ComponentProps } from "@/types/props" import type { ComponentProps } from "@/types/props"
interface Props extends ComponentProps { interface Props extends ComponentProps {
to: NavLink to: NavLink
lang?: AvailableLanguageTag lang?: Locale
} }
const { to, class: clazz, lang = languageTag() } = Astro.props const { to, class: clazz, lang = getLocale() } = Astro.props
--- ---
<a href={localizePathname(to, lang)} class={clazz}> <!-- TODO currently not working on Paraglide 2 https://github.com/opral/inlang-paraglide-js/issues/472 -->
<a href={localizeHref(to, { locale: lang })} class={clazz}>
<slot /> <slot />
</a> </a>

View File

@ -3,7 +3,7 @@ import * as m from "@/paraglide/messages"
import Layout from "@/layouts/Layout.astro" import Layout from "@/layouts/Layout.astro"
import BadgeList from "@/components/badge/BadgeList.astro" import BadgeList from "@/components/badge/BadgeList.astro"
import GiteaLink from "@/components/links/GiteaLink.astro" import GiteaLink from "@/components/links/GiteaLink.astro"
import { languageTag } from "@/paraglide/runtime" import { getLocale } from "@/paraglide/runtime"
import { getEntry, render } from "astro:content" import { getEntry, render } from "astro:content"
import { Image } from "astro:assets" import { Image } from "astro:assets"
import dayjs from "dayjs" import dayjs from "dayjs"
@ -35,10 +35,10 @@ const {
function localeDateString(isoString: string): string { function localeDateString(isoString: string): string {
let template = "DD-MM-YYYY" let template = "DD-MM-YYYY"
if (languageTag() === "nb") { if (getLocale() === "nb") {
template = "DD/MM/YYYY" template = "DD/MM/YYYY"
} }
return dayjs(isoString).locale(languageTag()).format(template) return dayjs(isoString).locale(getLocale()).format(template)
} }
--- ---

View File

@ -2,7 +2,7 @@
import Footer from "@/components/Footer.astro" import Footer from "@/components/Footer.astro"
import Header from "@/components/header/Header.astro" import Header from "@/components/header/Header.astro"
import Breadcrumb from "@/components/Breadcrumb.astro" import Breadcrumb from "@/components/Breadcrumb.astro"
import { languageTag } from "@/paraglide/runtime" import { getLocale } from "@/paraglide/runtime"
interface Props { interface Props {
title: string title: string
@ -16,7 +16,7 @@ const mainClass =
--- ---
<!doctype html> <!doctype html>
<html lang={languageTag()} dir={"ltr"}> <html lang={getLocale()} dir={"ltr"}>
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="author" content="Martin Berg Alstad" /> <meta name="author" content="Martin Berg Alstad" />

6
src/middleware.ts Normal file
View File

@ -0,0 +1,6 @@
import { paraglideMiddleware } from "@/paraglide/server"
import { defineMiddleware } from "astro/middleware"
export const onRequest = defineMiddleware((context, next) => {
return paraglideMiddleware(context.request, () => next());
});

View File

@ -1,17 +1,9 @@
--- ---
import ProjectPage from "@/components/projects/ProjectPage.astro" import ProjectPage from "@/components/projects/ProjectPage.astro"
import { type GetStaticPathsResult } from "astro"
import "@/styles/global.css" import "@/styles/global.css"
// Prerender the page as static HTML during build // Prerender must be false for paraglide js
export const prerender = true export const prerender = false
export function getStaticPaths(): GetStaticPathsResult {
return [
{ params: { project: "homepage" } },
{ params: { project: "sb1budget" } },
]
}
const { project } = Astro.params const { project } = Astro.params
--- ---

View File

@ -1,19 +1,11 @@
--- ---
import ProjectPage from "@/components/projects/ProjectPage.astro" import ProjectPage from "@/components/projects/ProjectPage.astro"
import { type GetStaticPathsResult } from "astro"
import "@/styles/global.css" import "@/styles/global.css"
// Prerender the page as static HTML during build // Prerender must be false for paraglide js
export const prerender = true export const prerender = false
export function getStaticPaths(): GetStaticPathsResult {
return [
{ params: { project: "homepage" } },
{ params: { project: "sb1budget" } },
]
}
const { project } = Astro.params const { project } = Astro.params
--- ---
<ProjectPage project={project as string} /> <ProjectPage project={ project as string } />

View File

@ -1,4 +1,3 @@
import type { AvailableLanguageTag } from "@/paraglide/runtime.js"
import type { AbsolutePathname, Project } from "@/types/types.ts" import type { AbsolutePathname, Project } from "@/types/types.ts"
interface TranslatedPathnames { interface TranslatedPathnames {
@ -22,8 +21,6 @@ const paths: Set<NavLink> = new Set([
"/uses", "/uses",
]) ])
const projectPaths: Set<string> = new Set<string>(["homepage", "sb1budget"])
/** /**
* Defines the localized pathnames for the site. * Defines the localized pathnames for the site.
* The key must be used to navigate to the correct path. * The key must be used to navigate to the correct path.
@ -38,49 +35,3 @@ for (const path of paths) {
en: `/en${path}`, en: `/en${path}`,
} }
} }
export function localizePathname(
pathname: NavLink,
locale: AvailableLanguageTag,
): string {
const pathnameParts = pathname.split("/")
const firstSegment: AbsolutePathname = `/${pathnameParts[1]}`
if (pathnames[firstSegment]) {
const localizedPathname = pathnames[firstSegment][locale]
const rest = pathnameParts.slice(2)
if (rest.length > 0) {
return `${localizedPathname}/${rest.join("/")}`
}
return localizedPathname
}
return pathname
}
export function resolvePathname(pathname: string): AbsolutePathname {
if (pathname.startsWith("/en")) {
return pathname.slice(3) as AbsolutePathname
}
return pathname as AbsolutePathname
}
export function isAbsolutePathname(path: string): path is AbsolutePathname {
return path.startsWith("/")
}
export function isNavLink(path: string): path is NavLink {
let basePath = path
if (path.startsWith("/en")) {
basePath = path.slice(2)
}
if (paths.has(basePath as NavLink)) {
return true
}
const pathSplit = basePath.split("/").slice(1)
return (
pathSplit.length === 2 &&
pathSplit[0] === "projects" &&
projectPaths.has(pathSplit[1])
)
}