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:
@@ -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 />
|
||||
|
Reference in New Issue
Block a user