Add internationalization support with astro-i18next integration

- Implemented astro-i18next for multi-language support, including English, Dutch, and Italian.
- Configured default locale and language fallback settings.
- Defined routes for localized content in the configuration.
- Updated package.json and package-lock.json to include new dependencies for i18next and related plugins.
This commit is contained in:
becarta
2025-05-23 15:10:00 +02:00
parent 8a3507dce0
commit 3168826fa8
581 changed files with 88691 additions and 494 deletions

View File

@@ -1,24 +1,26 @@
---
import { SITE } from '../site.config';
import { getCurrentLocale } from '../utils/i18n';
import i18next, { t } from "i18next";
import { Trans, HeadHrefLangs } from "astro-i18next/components";
import { localizePath } from "astro-i18next";
import '../styles/global.css';
export interface Props {
title?: string;
description?: string;
image?: string;
lang?: string;
keywords?: string;
}
const {
title = SITE.title,
description = SITE.description,
title = t('site.title'),
description = t('site.description'),
image = SITE.ogImage,
lang = getCurrentLocale(),
keywords = ''
keywords = t('meta.keywords')
} = Astro.props;
const lang = Astro.currentLocale || 'en';
// Safe URL creation with fallbacks
let canonicalURL;
let ogImageURL;
@@ -37,7 +39,10 @@ try {
twitterImageURL = new URL(image, fallbackSite);
}
const fullTitle = title === SITE.title ? title : `${title} | ${SITE.title}`;
const fullTitle = title === t('site.title') ? title : `${title} | ${t('site.title')}`;
// Get all available locales
const locales = ["en", "nl", "it"];
---
<!doctype html>
@@ -73,7 +78,7 @@ const fullTitle = title === SITE.title ? title : `${title} | ${SITE.title}`;
<meta property="og:title" content={fullTitle} />
<meta property="og:description" content={description} />
<meta property="og:image" content={ogImageURL} />
<meta property="og:site_name" content={SITE.title} />
<meta property="og:site_name" content={t('site.title')} />
<meta property="og:locale" content={lang === 'en' ? 'en_US' : lang === 'nl' ? 'nl_NL' : 'it_IT'} />
<!-- Twitter -->
@@ -87,61 +92,14 @@ const fullTitle = title === SITE.title ? title : `${title} | ${SITE.title}`;
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="manifest" href="/manifest.json" />
<!-- Preconnect to external domains -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<!-- Content Security Policy -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self'; frame-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self';" />
<!-- Structured Data -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "Tiber365",
"url": "https://tiber365.it",
"logo": "https://tiber365.it/images/logo.png",
"description": "Professional IT services for freelancers and small businesses. Microsoft 365 support, networking solutions, web hosting, and custom IT projects.",
"contactPoint": {
"@type": "ContactPoint",
"telephone": "+39-123-456-7890",
"contactType": "customer service",
"email": "info@tiber365.it"
},
"sameAs": [
"https://blog.tiber365.it",
"https://support.tiber365.it"
],
"address": {
"@type": "PostalAddress",
"addressCountry": "IT"
}
}
</script>
<!-- Initialize theme before page load -->
<script is:inline>
(function() {
function getThemePreference() {
if (typeof localStorage !== 'undefined' && localStorage.getItem('theme')) {
return localStorage.getItem('theme');
}
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
function reflectPreference(theme) {
document.documentElement.setAttribute('data-theme', theme);
const themeColorMeta = document.querySelector('meta[name="theme-color"]');
if (themeColorMeta) {
themeColorMeta.setAttribute('content', theme === 'dark' ? '#0f172a' : '#ffffff');
}
}
const theme = getThemePreference();
reflectPreference(theme);
})();
</script>
<!-- Language alternates -->
{locales.map(locale => (
<link
rel="alternate"
hreflang={locale}
href={new URL(localizePath(Astro.url.pathname, locale), Astro.site).toString()}
/>
))}
</head>
<body class="min-h-screen bg-background text-foreground">
<slot />