📄 Huge intl feature

This commit is contained in:
RJ
2025-12-03 00:17:34 +02:00
parent 6e5d641c06
commit 78cdc7b539
48 changed files with 955 additions and 138 deletions

View File

@@ -33,7 +33,7 @@ export function calculateReadingTime(content: string): number {
return Math.ceil(words / wordsPerMinute)
}
export function validateFrontmatter(data: any): FrontMatter {
export function validateFrontmatter(data: any, locale?: string): FrontMatter {
if (!data.title || typeof data.title !== 'string') {
throw new Error('Invalid title')
}
@@ -60,15 +60,19 @@ export function validateFrontmatter(data: any): FrontMatter {
author: data.author,
category: data.category,
tags: data.tags,
locale: data.locale || locale || 'en',
image: data.image,
draft: data.draft || false,
}
}
export async function getPostBySlug(slug: string | string[]): Promise<Post | null> {
export async function getPostBySlug(
slug: string | string[],
locale: string = 'en'
): Promise<Post | null> {
const slugArray = Array.isArray(slug) ? slug : slug.split('/')
const sanitized = slugArray.map(s => sanitizePath(s))
const fullPath = path.join(POSTS_PATH, ...sanitized) + '.md'
const fullPath = path.join(POSTS_PATH, locale, ...sanitized) + '.md'
if (!fs.existsSync(fullPath)) {
return null
@@ -76,7 +80,7 @@ export async function getPostBySlug(slug: string | string[]): Promise<Post | nul
const fileContents = fs.readFileSync(fullPath, 'utf8')
const { data, content } = matter(fileContents)
const frontmatter = validateFrontmatter(data)
const frontmatter = validateFrontmatter(data, locale)
const processed = await remark()
.use(remarkGfm)
@@ -85,13 +89,14 @@ export async function getPostBySlug(slug: string | string[]): Promise<Post | nul
publicDir: 'public/blog',
currentSlug: sanitized.join('/'),
})
.use(remarkInternalLinks)
.use(remarkInternalLinks, { locale })
.process(content)
const processedContent = processed.toString()
return {
slug: sanitized.join('/'),
locale,
frontmatter,
content: processedContent,
readingTime: calculateReadingTime(processedContent),
@@ -99,8 +104,14 @@ export async function getPostBySlug(slug: string | string[]): Promise<Post | nul
}
}
export async function getAllPosts(includeContent = false): Promise<Post[]> {
export async function getAllPosts(locale: string = 'en', includeContent = false): Promise<Post[]> {
const posts: Post[] = []
const localeDir = path.join(POSTS_PATH, locale)
if (!fs.existsSync(localeDir)) {
console.warn(`Locale directory not found: ${localeDir}`)
return []
}
async function walkDir(dir: string, prefix = ''): Promise<void> {
const files = fs.readdirSync(dir)
@@ -114,7 +125,7 @@ export async function getAllPosts(includeContent = false): Promise<Post[]> {
} else if (file.endsWith('.md')) {
const slug = prefix ? `${prefix}/${file.replace(/\.md$/, '')}` : file.replace(/\.md$/, '')
try {
const post = await getPostBySlug(slug.split('/'))
const post = await getPostBySlug(slug.split('/'), locale)
if (post && !post.frontmatter.draft) {
posts.push(includeContent ? post : { ...post, content: '' })
}
@@ -125,20 +136,22 @@ export async function getAllPosts(includeContent = false): Promise<Post[]> {
}
}
if (fs.existsSync(POSTS_PATH)) {
await walkDir(POSTS_PATH)
}
await walkDir(localeDir)
return posts.sort(
(a, b) => new Date(b.frontmatter.date).getTime() - new Date(a.frontmatter.date).getTime()
)
}
export async function getRelatedPosts(currentSlug: string, limit = 3): Promise<Post[]> {
const currentPost = await getPostBySlug(currentSlug)
export async function getRelatedPosts(
currentSlug: string,
locale: string = 'en',
limit = 3
): Promise<Post[]> {
const currentPost = await getPostBySlug(currentSlug, locale)
if (!currentPost) return []
const allPosts = await getAllPosts(false)
const allPosts = await getAllPosts(locale, false)
const { category, tags } = currentPost.frontmatter
const scored = allPosts
@@ -155,8 +168,13 @@ export async function getRelatedPosts(currentSlug: string, limit = 3): Promise<P
return scored.slice(0, limit).map(({ post }) => post)
}
export function getAllPostSlugs(): string[][] {
export function getAllPostSlugs(locale: string = 'en'): string[][] {
const slugs: string[][] = []
const localeDir = path.join(POSTS_PATH, locale)
if (!fs.existsSync(localeDir)) {
return []
}
function walkDir(dir: string, prefix: string[] = []): void {
const files = fs.readdirSync(dir)
@@ -173,9 +191,26 @@ export function getAllPostSlugs(): string[][] {
}
}
if (fs.existsSync(POSTS_PATH)) {
walkDir(POSTS_PATH)
}
walkDir(localeDir)
return slugs
}
export async function getAvailableLocales(slug: string): Promise<string[]> {
const locales = ['en', 'ro']
const available: string[] = []
for (const locale of locales) {
const post = await getPostBySlug(slug, locale)
if (post) {
available.push(locale)
}
}
return available
}
export async function getPostCount(locale: string): Promise<number> {
const posts = await getAllPosts(locale, false)
return posts.length
}