diff --git a/packages/polymech/src/components/sidebar/Sidebar.astro b/packages/polymech/src/components/sidebar/Sidebar.astro index 4f284f4..0ee75bc 100644 --- a/packages/polymech/src/components/sidebar/Sidebar.astro +++ b/packages/polymech/src/components/sidebar/Sidebar.astro @@ -6,6 +6,7 @@ import type { SidebarGroup as SidebarGroupType } from './types'; import type { MarkdownHeading } from 'astro'; import Translate from "@polymech/astro-base/components/i18n.astro"; import SidebarPersister from './SidebarPersister.astro'; +import { I18N_SOURCE_LANGUAGE } from "config/config.js"; interface Props { config: SidebarGroupType[]; @@ -17,14 +18,24 @@ interface Props { const { config, currentUrl, headings = [], pageNavigation = [] } = Astro.props; const currentPath = currentUrl ? getCurrentPath(currentUrl) : ''; +// Extract locale from URL path (e.g., /es/resources/ -> 'es') +let locale: string = I18N_SOURCE_LANGUAGE; // Default to source language +if (currentPath) { + const pathSegments = currentPath.split('/').filter(segment => segment); + // Check if first segment is a language code (2 letters) + if (pathSegments.length > 0 && /^[a-z]{2}$/.test(pathSegments[0])) { + locale = pathSegments[0]; + } +} + // Process all sidebar groups const processedGroups = await Promise.all( - config.map(group => processSidebarGroup(group, currentPath)) + config.map(group => processSidebarGroup(group, currentPath, locale)) ); // Process page-level navigation const processedPageNav = await Promise.all( - pageNavigation.map(group => processSidebarGroup({...group, isPageLevel: true}, currentPath)) + pageNavigation.map(group => processSidebarGroup({...group, isPageLevel: true}, currentPath, locale)) ); --- diff --git a/packages/polymech/src/components/sidebar/utils.ts b/packages/polymech/src/components/sidebar/utils.ts index 54a91d3..6bd2dd6 100644 --- a/packages/polymech/src/components/sidebar/utils.ts +++ b/packages/polymech/src/components/sidebar/utils.ts @@ -2,6 +2,7 @@ import { getCollection } from 'astro:content'; import type { SidebarGroup, SidebarLink, SortFunction } from './types.js'; import { filterCollection, defaultFilters, type CollectionFilter } from '../../base/collections.js'; import { z } from 'zod'; +import { I18N_SOURCE_LANGUAGE } from "config/config.js"; interface DirectoryStructure { [key: string]: { @@ -19,6 +20,7 @@ export const SidebarGenerationOptionsSchema = z.object({ sortBy: z.enum(['alphabetical', 'date', 'custom']).default('alphabetical'), customSort: z.any().optional(), // We'll validate this at runtime filters: z.array(z.any()).optional(), // We'll validate this at runtime + locale: z.string().default(I18N_SOURCE_LANGUAGE), // Language code for URL generation, defaults to source language }).strict(); // Define the proper TypeScript types @@ -30,6 +32,7 @@ export interface SidebarGenerationOptions { sortBy: 'alphabetical' | 'date' | 'custom'; customSort?: (a: SidebarLink | SidebarGroup, b: SidebarLink | SidebarGroup) => number; filters?: CollectionFilter[]; + locale: string; // Required, defaults to I18N_SOURCE_LANGUAGE } /** @@ -84,7 +87,8 @@ export async function generateLinksFromDirectory( * maxDepth: 3, * collapsedByDefault: true, * sortBy: 'date', - * filters: [hasTitle, isNotDraft] + * filters: [hasTitle, isNotDraft], + * locale: 'es' // Include language code in URLs * }); * * // Using the helper function @@ -119,7 +123,8 @@ export async function generateLinksFromDirectoryWithConfig( validatedOptions.collapsedByDefault, validatedOptions.sortBy, validatedOptions.customSort, - entries + entries, + validatedOptions.locale ); } catch (error) { console.warn(`Could not load collection "${directory}":`, error); @@ -176,14 +181,15 @@ function buildSidebarFromStructure( collapsedByDefault: boolean = false, sortBy: SortFunction = 'alphabetical', customSort?: (a: SidebarLink | SidebarGroup, b: SidebarLink | SidebarGroup) => number, - entries?: any[] + entries?: any[], + locale: string ): (SidebarLink | SidebarGroup)[] { const items: (SidebarLink | SidebarGroup)[] = []; // Process root level files first if (structure['']?.files) { const rootFiles = structure[''].files .filter(entry => !isPageHidden(entry)) - .map(entry => createSidebarLink(entry, baseDirectory, currentPath)); + .map(entry => createSidebarLink(entry, baseDirectory, locale, currentPath)); // Add root files without sorting (will be sorted at the end) items.push(...rootFiles); @@ -201,7 +207,7 @@ function buildSidebarFromStructure( if (dirData.files.length > 0) { const subFiles = dirData.files .filter(entry => !isPageHidden(entry)) - .map(entry => createSidebarLink(entry, baseDirectory, currentPath)); + .map(entry => createSidebarLink(entry, baseDirectory, locale, currentPath)); subItems.push(...subFiles); } @@ -217,7 +223,8 @@ function buildSidebarFromStructure( collapsedByDefault, sortBy, customSort, - entries + entries, + locale ); subItems.push(...nestedItems); } @@ -249,7 +256,7 @@ function isPageHidden(entry: any): boolean { /** * Create a sidebar link from an entry */ -function createSidebarLink(entry: any, baseDirectory: string, currentPath?: string): SidebarLink { +function createSidebarLink(entry: any, baseDirectory: string, locale: string, currentPath?: string): SidebarLink { // Handle different collection schemas let label = entry.id; if (entry.data.title) { @@ -268,9 +275,12 @@ function createSidebarLink(entry: any, baseDirectory: string, currentPath?: stri // Remove file extension from entry.id for clean URLs const cleanId = entry.id.replace(/\.(md|mdx)$/, ''); + // Generate href with locale if provided + const href = locale ? `/${locale}/${baseDirectory}/${cleanId}/` : `/${baseDirectory}/${cleanId}/`; + return { label, - href: `/${baseDirectory}/${cleanId}/`, + href, isCurrent: currentPath?.includes(`/${baseDirectory}/${cleanId}`) || false, }; } @@ -349,7 +359,7 @@ function applySorting( /** * Process a sidebar group and generate links */ -export async function processSidebarGroup(group: SidebarGroup, currentPath?: string): Promise { +export async function processSidebarGroup(group: SidebarGroup, currentPath?: string, locale?: string): Promise { const processedGroup: SidebarGroup = { label: group.label, collapsed: group.collapsed, @@ -368,7 +378,8 @@ export async function processSidebarGroup(group: SidebarGroup, currentPath?: str collapsedByDefault: group.autogenerate.collapsed ?? false, currentDepth: 0, sortBy, - customSort: group.autogenerate.customSort + customSort: group.autogenerate.customSort, + locale } ); processedGroup.items = items;