- Mise à jour astro@5.17, @astrojs/tailwind@6, @astrojs/check - Remplacement des content collections legacy par des loaders glob() - Déplacement src/content/config.ts → src/content.config.ts - entry.slug → entry.id, entry.render() → render(entry) - Ajout de generateId personnalisé pour préserver les points dans les IDs des fichiers multilingues (.en, .ar)
176 lines
No EOL
7.4 KiB
TypeScript
176 lines
No EOL
7.4 KiB
TypeScript
export const defaultLocale = 'fr';
|
|
export const locales = ['fr', 'en', 'ar'] as const;
|
|
export type Locale = typeof locales[number];
|
|
|
|
export function getLocaleFromUrl(url: URL): Locale {
|
|
const [, locale] = url.pathname.split('/');
|
|
if (locales.includes(locale as Locale)) {
|
|
return locale as Locale;
|
|
}
|
|
return defaultLocale;
|
|
}
|
|
|
|
export function getLocalizedPath(path: string, locale: Locale): string {
|
|
if (locale === defaultLocale) {
|
|
return path;
|
|
}
|
|
return `/${locale}${path}`;
|
|
}
|
|
|
|
export function removeLocaleFromPath(path: string): string {
|
|
const segments = path.split('/').filter(Boolean);
|
|
if (locales.includes(segments[0] as Locale)) {
|
|
segments.shift();
|
|
}
|
|
return '/' + segments.join('/');
|
|
}
|
|
|
|
export interface LocalizedContent {
|
|
fr: string;
|
|
en: string;
|
|
ar: string;
|
|
}
|
|
|
|
export const translations = {
|
|
nav: {
|
|
home: { fr: 'Accueil', en: 'Home', ar: 'الرئيسية' },
|
|
pro: { fr: 'Pro', en: 'Pro', ar: 'مهني' },
|
|
comedy: { fr: 'Comédie', en: 'Comedy', ar: 'كوميديا' },
|
|
photo: { fr: 'Photo', en: 'Photo', ar: 'تصوير' },
|
|
blog: { fr: 'Blog', en: 'Blog', ar: 'مدونة' },
|
|
about: { fr: 'À propos', en: 'About', ar: 'حول' },
|
|
projects: { fr: 'Projets', en: 'Projects', ar: 'مشاريع' },
|
|
talks: { fr: 'Talks', en: 'Talks', ar: 'محاضرات' },
|
|
gallery: { fr: 'Galerie', en: 'Gallery', ar: 'معرض' },
|
|
shows: { fr: 'Spectacles', en: 'Shows', ar: 'عروض' },
|
|
now: { fr: 'Maintenant', en: 'Now', ar: 'الآن' },
|
|
uses: { fr: 'Utilise', en: 'Uses', ar: 'يستخدم' },
|
|
},
|
|
common: {
|
|
siteName: { fr: 'Jalil Arfaoui', en: 'Jalil Arfaoui', ar: 'جليل عرفاوي' },
|
|
readMore: { fr: 'Lire la suite', en: 'Read more', ar: 'اقرأ المزيد' },
|
|
backToHome: { fr: 'Retour à l\'accueil', en: 'Back to home', ar: 'العودة إلى الرئيسية' },
|
|
publishedOn: { fr: 'Publié le', en: 'Published on', ar: 'نشر في' },
|
|
by: { fr: 'par', en: 'by', ar: 'بواسطة' },
|
|
allPosts: { fr: 'Tous les articles', en: 'All posts', ar: 'جميع المقالات' },
|
|
recentPosts: { fr: 'Articles récents', en: 'Recent posts', ar: 'المقالات الأخيرة' },
|
|
categories: { fr: 'Catégories', en: 'Categories', ar: 'الفئات' },
|
|
tags: { fr: 'Tags', en: 'Tags', ar: 'علامات' },
|
|
search: { fr: 'Rechercher', en: 'Search', ar: 'بحث' },
|
|
darkMode: { fr: 'Mode sombre', en: 'Dark mode', ar: 'الوضع الداكن' },
|
|
lightMode: { fr: 'Mode clair', en: 'Light mode', ar: 'الوضع الفاتح' },
|
|
},
|
|
categories: {
|
|
pro: { fr: 'Professionnel', en: 'Professional', ar: 'مهني' },
|
|
comedy: { fr: 'Comédie', en: 'Comedy', ar: 'كوميديا' },
|
|
photo: { fr: 'Photographie', en: 'Photography', ar: 'تصوير' },
|
|
dev: { fr: 'Développement', en: 'Development', ar: 'تطوير' },
|
|
},
|
|
photo: {
|
|
galleryTitle: { fr: 'Galerie Photo', en: 'Photo Gallery', ar: 'معرض الصور' },
|
|
blogTitle: { fr: 'Blog Photo', en: 'Photo Blog', ar: 'مدونة الصور' },
|
|
photoFeed: { fr: 'Fil Photo', en: 'Photo Feed', ar: 'سلسلة الصور' },
|
|
categories: { fr: 'Catégories', en: 'Categories', ar: 'الفئات' },
|
|
browseByTheme: { fr: 'Parcourir les photos par thème', en: 'Browse photos by theme', ar: 'تصفّح الصور حسب الموضوع' },
|
|
feedDescription: { fr: 'Parcourir les séries chronologiques, reportages et histoires en images', en: 'Browse chronological series, reports and stories in images', ar: 'تصفّح السلاسل الزمنية والتقارير والقصص المصوّرة' },
|
|
viewFeed: { fr: 'Voir le fil', en: 'View feed', ar: 'عرض السلسلة' },
|
|
featured: { fr: 'À la une', en: 'Featured', ar: 'مميّز' },
|
|
about: { fr: 'À propos', en: 'About', ar: 'نبذة' },
|
|
contact: { fr: 'Contact', en: 'Contact', ar: 'تواصل' },
|
|
fullscreen: { fr: 'Plein écran', en: 'Fullscreen', ar: 'شاشة كاملة' },
|
|
close: { fr: 'Fermer', en: 'Close', ar: 'إغلاق' },
|
|
previousImage: { fr: 'Image précédente', en: 'Previous image', ar: 'الصورة السابقة' },
|
|
nextImage: { fr: 'Image suivante', en: 'Next image', ar: 'الصورة التالية' },
|
|
goToImage: { fr: "Aller à l'image", en: 'Go to image', ar: 'انتقل إلى الصورة' },
|
|
},
|
|
pages: {
|
|
home: {
|
|
title: { fr: 'Jalil Arfaoui', en: 'Jalil Arfaoui', ar: 'جليل عرفاوي' },
|
|
subtitle: {
|
|
fr: 'Développeur • Comédien • Photographe',
|
|
en: 'Developer • Comedian • Photographer',
|
|
ar: 'مطور • ممثل كوميدي • مصور'
|
|
},
|
|
description: {
|
|
fr: 'Bienvenue dans mon univers créatif où le code rencontre l\'art',
|
|
en: 'Welcome to my creative universe where code meets art',
|
|
ar: 'مرحبًا بكم في عالمي الإبداعي حيث يلتقي الكود بالفن'
|
|
}
|
|
},
|
|
pro: {
|
|
title: { fr: 'Parcours Professionnel', en: 'Professional Journey', ar: 'المسار المهني' },
|
|
description: {
|
|
fr: 'Développeur passionné par le Software Craftsmanship',
|
|
en: 'Developer passionate about Software Craftsmanship',
|
|
ar: 'مطور شغوف بحرفية البرمجيات'
|
|
}
|
|
},
|
|
comedy: {
|
|
title: { fr: 'Univers Comédie', en: 'Comedy Universe', ar: 'عالم الكوميديا' },
|
|
description: {
|
|
fr: 'Acteur et créateur de contenus humoristiques',
|
|
en: 'Actor and creator of humorous content',
|
|
ar: 'ممثل ومنشئ محتوى فكاهي'
|
|
}
|
|
},
|
|
photo: {
|
|
title: { fr: 'Portfolio Photo', en: 'Photo Portfolio', ar: 'معرض الصور' },
|
|
description: {
|
|
fr: 'Capturer l\'instant, raconter une histoire',
|
|
en: 'Capturing the moment, telling a story',
|
|
ar: 'التقاط اللحظة، سرد قصة'
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/** Helper de traduction typé */
|
|
export function t(section: keyof typeof translations, key: string, locale: Locale): string {
|
|
const sectionData = translations[section] as Record<string, LocalizedContent>;
|
|
return sectionData?.[key]?.[locale] ?? sectionData?.[key]?.fr ?? key;
|
|
}
|
|
|
|
/** Chemin de base photo selon la langue */
|
|
export function getPhotoBasePath(locale: Locale): string {
|
|
if (locale === 'ar') return '/ar/تصوير';
|
|
if (locale === 'en') return '/en/photo';
|
|
return '/photo';
|
|
}
|
|
|
|
/** Chemin du blog photo selon la langue */
|
|
export function getPhotoBlogPath(locale: Locale): string {
|
|
return `${getPhotoBasePath(locale)}/${locale === 'ar' ? 'مدونة' : 'blog'}`;
|
|
}
|
|
|
|
/** Chemin des albums photo selon la langue */
|
|
export function getPhotoAlbumsPath(locale: Locale): string {
|
|
return `${getPhotoBasePath(locale)}/${locale === 'ar' ? 'ألبومات' : 'albums'}`;
|
|
}
|
|
|
|
/** Chemin "À propos" selon la langue */
|
|
export function getAboutPath(locale: Locale): string {
|
|
if (locale === 'en') return '/en/about';
|
|
if (locale === 'ar') return '/ar/نبذة-عني';
|
|
return '/a-propos';
|
|
}
|
|
|
|
/** Locale pour les dates */
|
|
export function getDateLocale(locale: Locale): string {
|
|
const dateLocales: Record<Locale, string> = { fr: 'fr-FR', en: 'en-US', ar: 'ar-SA' };
|
|
return dateLocales[locale];
|
|
}
|
|
|
|
/** ID de catégorie localisé (portraits → portraits.en pour EN) */
|
|
export function getCategoryEntryId(categoryId: string, locale: Locale): string {
|
|
return locale === 'fr' ? categoryId : `${categoryId}.${locale}`;
|
|
}
|
|
|
|
/** Chemin d'accueil selon la langue */
|
|
export function getHomePath(locale: Locale): string {
|
|
return locale === 'fr' ? '/' : `/${locale}`;
|
|
}
|
|
|
|
/** Slug de base d'un photo blog post depuis son id (ex: "2015/enigma.en" → "enigma") */
|
|
export function getPostBaseSlug(postId: string): string {
|
|
return postId.replace(/^\d{4}\//, '').replace(/\.(en|ar)(\.md)?$/, '');
|
|
} |