@zoyth/simple-site-framework 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +572 -0
- package/bin/create-simple-site.js +390 -0
- package/bin/simple-site.js +664 -0
- package/dist/client.js +135 -0
- package/dist/client.js.map +1 -0
- package/dist/client.mjs +107 -0
- package/dist/client.mjs.map +1 -0
- package/dist/components/index.d.mts +3936 -0
- package/dist/components/index.d.ts +3936 -0
- package/dist/components/index.js +38265 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/index.mjs +38173 -0
- package/dist/components/index.mjs.map +1 -0
- package/dist/config/index.d.mts +298 -0
- package/dist/config/index.d.ts +298 -0
- package/dist/config/index.js +19 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/index.mjs +1 -0
- package/dist/config/index.mjs.map +1 -0
- package/dist/index.d.mts +2184 -0
- package/dist/index.d.ts +2184 -0
- package/dist/index.js +1713 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1605 -0
- package/dist/index.mjs.map +1 -0
- package/dist/lib/i18n/index.js +665 -0
- package/dist/lib/i18n/index.js.map +1 -0
- package/dist/lib/i18n/index.mjs +621 -0
- package/dist/lib/i18n/index.mjs.map +1 -0
- package/docs/DOCUMENTATION-STRUCTURE.md +1156 -0
- package/docs/EXPORTS.md +125 -0
- package/docs/PERFORMANCE.md +757 -0
- package/docs/POLICY-PAGES.md +867 -0
- package/docs/ROADMAP.md +334 -0
- package/docs/SEO.md +455 -0
- package/docs/SITEMAP.md +708 -0
- package/docs/STRUCTURED-DATA.md +671 -0
- package/docs/accessibility/common-patterns.md +529 -0
- package/docs/accessibility/keyboard-navigation.md +263 -0
- package/docs/accessibility/overview.md +122 -0
- package/docs/accessibility/screen-readers.md +311 -0
- package/docs/accessibility/wcag-compliance.md +159 -0
- package/docs/api/README.md +164 -0
- package/docs/api/components/Accessibility.md +356 -0
- package/docs/api/components/Button.md +240 -0
- package/docs/api/components/HeroSection.md +306 -0
- package/docs/architecture/decisions.md +449 -0
- package/docs/components/AnalyticsTracker.md +58 -0
- package/docs/components/AnimatedCounter.md +48 -0
- package/docs/components/AnimatedSection.md +56 -0
- package/docs/components/BlogCard.md +42 -0
- package/docs/components/Checkbox.md +56 -0
- package/docs/components/CodeBlock.md +52 -0
- package/docs/components/ComparisonTable.md +40 -0
- package/docs/components/ComponentDemo.md +38 -0
- package/docs/components/CountdownTimer.md +51 -0
- package/docs/components/ExitIntentModal.md +56 -0
- package/docs/components/FAQAccordion.md +66 -0
- package/docs/components/FeaturesGrid.md +55 -0
- package/docs/components/FileUpload.md +54 -0
- package/docs/components/I18nMetaTags.md +55 -0
- package/docs/components/Icon.md +53 -0
- package/docs/components/LazySection.md +46 -0
- package/docs/components/LiveProof.md +53 -0
- package/docs/components/LoadingSpinner.md +46 -0
- package/docs/components/MultiStepForm.md +48 -0
- package/docs/components/PolicyLayout.md +55 -0
- package/docs/components/PricingTable.md +49 -0
- package/docs/components/Radio.md +59 -0
- package/docs/components/SEOMetaTags.md +58 -0
- package/docs/components/ScriptInjector.md +50 -0
- package/docs/components/Select.md +72 -0
- package/docs/components/Skeleton.md +47 -0
- package/docs/components/StatsSection.md +48 -0
- package/docs/components/StickyBar.md +62 -0
- package/docs/components/StructuredData.md +99 -0
- package/docs/components/StyleGuide.md +46 -0
- package/docs/components/TableOfContents.md +47 -0
- package/docs/components/TestimonialCarousel.md +42 -0
- package/docs/components/Timeline.md +51 -0
- package/docs/components/Toast.md +59 -0
- package/docs/components/TrackedLink.md +62 -0
- package/docs/components/TrustBadges.md +44 -0
- package/docs/components/conversion/MobileCTA.md +363 -0
- package/docs/components/forms/ContactForm.md +75 -0
- package/docs/components/forms/FormField.md +74 -0
- package/docs/components/layout/Footer.md +601 -0
- package/docs/components/layout/Header.md +549 -0
- package/docs/components/layout/LanguageSelector.md +54 -0
- package/docs/components/layout/LanguageSwitcher.md +24 -0
- package/docs/components/overview.md +447 -0
- package/docs/components/sections/AboutSection.md +48 -0
- package/docs/components/sections/CTASection.md +596 -0
- package/docs/components/sections/CaseStudySection.md +47 -0
- package/docs/components/sections/ContactSection.md +599 -0
- package/docs/components/sections/FeatureSection.md +44 -0
- package/docs/components/sections/HeroSection.md +404 -0
- package/docs/components/sections/LogosSection.md +47 -0
- package/docs/components/sections/PersonalTaxesSection.md +23 -0
- package/docs/components/sections/RecruitingSection.md +23 -0
- package/docs/components/sections/SecurePortalSection.md +23 -0
- package/docs/components/sections/ServicePageLayout.md +52 -0
- package/docs/components/sections/ServicesSection.md +49 -0
- package/docs/components/sections/TestimonialSection.md +44 -0
- package/docs/components/sections/WhyChooseUsSection.md +54 -0
- package/docs/components/ui/Breadcrumb.md +70 -0
- package/docs/components/ui/Button.md +514 -0
- package/docs/components/ui/Card.md +501 -0
- package/docs/components/ui/Input.md +54 -0
- package/docs/components/ui/MobileLinks.md +43 -0
- package/docs/components/ui/Modal.md +60 -0
- package/docs/components/ui/Tabs.md +62 -0
- package/docs/components/ui/Textarea.md +52 -0
- package/docs/core-concepts/configuration-driven.md +552 -0
- package/docs/core-concepts/overview.md +351 -0
- package/docs/features/accessibility/README.md +73 -0
- package/docs/features/accessibility/aria-support.md +177 -0
- package/docs/features/accessibility/color-contrast.md +155 -0
- package/docs/features/accessibility/focus-management.md +187 -0
- package/docs/features/accessibility/testing.md +196 -0
- package/docs/features/analytics/README.md +51 -0
- package/docs/features/analytics/ab-testing.md +171 -0
- package/docs/features/analytics/conversion-tracking.md +207 -0
- package/docs/features/analytics/custom-events.md +219 -0
- package/docs/features/analytics/privacy.md +198 -0
- package/docs/features/analytics/setup.md +114 -0
- package/docs/features/analytics/tracking-events.md +224 -0
- package/docs/features/i18n/README.md +51 -0
- package/docs/features/i18n/best-practices.md +273 -0
- package/docs/features/i18n/configuration.md +84 -0
- package/docs/features/i18n/formatting.md +133 -0
- package/docs/features/i18n/locale-detection.md +122 -0
- package/docs/features/i18n/routing.md +99 -0
- package/docs/features/i18n/rtl-support.md +191 -0
- package/docs/features/i18n/translations.md +129 -0
- package/docs/features/internationalization.md +595 -0
- package/docs/features/performance/README.md +77 -0
- package/docs/features/performance/bundle-size.md +134 -0
- package/docs/features/performance/caching.md +131 -0
- package/docs/features/performance/code-splitting.md +121 -0
- package/docs/features/performance/image-optimization.md +110 -0
- package/docs/features/performance/lazy-loading.md +92 -0
- package/docs/features/performance/monitoring.md +148 -0
- package/docs/features/seo/README.md +51 -0
- package/docs/features/seo/best-practices.md +184 -0
- package/docs/features/seo/canonical-urls.md +182 -0
- package/docs/features/seo/meta-tags.md +126 -0
- package/docs/features/seo/open-graph.md +166 -0
- package/docs/features/seo/robots-txt.md +146 -0
- package/docs/features/seo/sitemaps.md +162 -0
- package/docs/features/seo/structured-data.md +166 -0
- package/docs/getting-started/installation.md +292 -0
- package/docs/getting-started/introduction.md +195 -0
- package/docs/getting-started/quick-start.md +460 -0
- package/docs/guides/analytics-setup.md +616 -0
- package/docs/i18n/CONFIGURATION.md +353 -0
- package/docs/i18n/EXAMPLES.md +402 -0
- package/docs/i18n/MIGRATION.md +260 -0
- package/docs/i18n/SEO.md +392 -0
- package/docs/i18n/STATIC-GENERATION-FIX.md +71 -0
- package/docs/migration/changelog.md +136 -0
- package/docs/migration/overview.md +233 -0
- package/docs/recipes/adding-animations.md +475 -0
- package/docs/recipes/forms-with-validation.md +393 -0
- package/package.json +152 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/i18n/config.ts","../src/lib/theme/generate-css.ts","../src/lib/theme/load-fonts.ts","../src/lib/content/utils.ts","../src/lib/content/policies.ts","../src/lib/navigation/utils.ts","../src/lib/i18n/index.ts","../src/lib/i18n/locale-cookie.ts","../src/lib/i18n/slug-translations.ts","../src/lib/i18n/formatters.ts","../src/lib/i18n/middleware.ts","../src/lib/i18n/utils.ts","../src/lib/utils/cn.ts","../src/lib/analytics.ts","../src/lib/utils/forms.tsx","../src/lib/forms/schemas.ts","../src/lib/forms/errors.ts","../src/lib/seo/metadata.ts","../src/lib/seo/structured-data.ts","../src/lib/seo/sitemap.ts","../src/components/TrackedLink.tsx","../src/components/FeaturesGrid.tsx"],"sourcesContent":["// ABOUTME: Dynamic i18n configuration system for flexible multi-language support\n// ABOUTME: Projects must call setI18nConfig() to initialize their locale configuration\n\nimport type { I18nConfig, LocalePrefix } from './types';\n\n/**\n * Global i18n configuration state\n * Initialized by calling setI18nConfig()\n */\nlet globalI18nConfig: I18nConfig | null = null;\n\n/**\n * Initialize the i18n configuration\n * Must be called before using any i18n features\n *\n * @param config - Complete i18n configuration\n *\n * @example\n * ```typescript\n * import { setI18nConfig } from 'simple-site-framework/lib/i18n';\n * import { i18nConfig } from './config/i18n';\n *\n * setI18nConfig(i18nConfig);\n * ```\n */\nexport function setI18nConfig(config: I18nConfig): void {\n // Validate configuration\n if (!config.locales || config.locales.length === 0) {\n throw new Error('i18n config must define at least one locale');\n }\n\n if (!config.defaultLocale) {\n throw new Error('i18n config must define a default locale');\n }\n\n if (!config.locales.includes(config.defaultLocale)) {\n throw new Error(\n `Default locale \"${config.defaultLocale}\" must be included in locales array: [${config.locales.join(', ')}]`\n );\n }\n\n // Set defaults for optional fields\n const configWithDefaults: I18nConfig = {\n ...config,\n localePrefix: config.localePrefix || 'as-needed',\n localeDetection: config.localeDetection !== false, // Default to true\n localeNames: config.localeNames || {},\n localeLabels: config.localeLabels || {},\n rtlLocales: config.rtlLocales || [],\n localeCookie: {\n name: config.localeCookie?.name || 'NEXT_LOCALE',\n maxAge: config.localeCookie?.maxAge || 365 * 24 * 60 * 60, // 1 year\n sameSite: config.localeCookie?.sameSite || 'lax',\n },\n slugTranslations: config.slugTranslations || {},\n };\n\n globalI18nConfig = configWithDefaults;\n}\n\n/**\n * Get the current i18n configuration\n * Returns legacy default config if not initialized (for build compatibility)\n *\n * @deprecated Calling without initialization - will throw in v1.0.0\n */\nexport function getI18nConfig(): I18nConfig {\n if (!globalI18nConfig) {\n // During static generation, config may not be initialized yet\n // Return legacy defaults to allow build to complete\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n '⚠️ i18n configuration not initialized. Using legacy defaults.\\n' +\n 'Call setI18nConfig() in your layout before using i18n features.\\n' +\n 'See docs/i18n/MIGRATION.md for setup instructions.'\n );\n }\n\n // Return legacy hardcoded config for backward compatibility\n return {\n locales: ['fr', 'en'],\n defaultLocale: 'fr',\n localePrefix: 'as-needed',\n localeDetection: true,\n localeNames: { fr: 'Français', en: 'English' },\n localeLabels: { fr: 'FR', en: 'EN' },\n rtlLocales: [],\n localeCookie: {\n name: 'NEXT_LOCALE',\n maxAge: 365 * 24 * 60 * 60,\n sameSite: 'lax',\n },\n slugTranslations: {},\n };\n }\n\n return globalI18nConfig;\n}\n\n/**\n * Check if i18n config has been initialized\n */\nexport function isI18nConfigInitialized(): boolean {\n return globalI18nConfig !== null;\n}\n\n/**\n * Reset i18n configuration (for testing only)\n * @internal\n */\nexport function __resetI18nConfig__(): void {\n globalI18nConfig = null;\n}\n\n/**\n * Get supported locales array\n */\nexport function getLocales(): readonly string[] {\n return getI18nConfig().locales;\n}\n\n/**\n * Get default locale\n */\nexport function getDefaultLocale(): string {\n return getI18nConfig().defaultLocale;\n}\n\n/**\n * Get locale prefix mode\n */\nexport function getLocalePrefix(): LocalePrefix {\n return getI18nConfig().localePrefix || 'as-needed';\n}\n\n/**\n * Get locale names mapping\n */\nexport function getLocaleNames(): Record<string, string> {\n return getI18nConfig().localeNames || {};\n}\n\n/**\n * Get locale labels mapping\n */\nexport function getLocaleLabels(): Record<string, string> {\n return getI18nConfig().localeLabels || {};\n}\n\n/**\n * Get RTL locales array\n */\nexport function getRtlLocales(): readonly string[] {\n return getI18nConfig().rtlLocales || [];\n}\n\n/**\n * Check if locale detection is enabled\n */\nexport function isLocaleDetectionEnabled(): boolean {\n return getI18nConfig().localeDetection !== false;\n}\n\n/**\n * Get locale cookie configuration\n */\nexport function getLocaleCookieConfig() {\n const config = getI18nConfig();\n return {\n name: config.localeCookie?.name || 'NEXT_LOCALE',\n maxAge: config.localeCookie?.maxAge || 365 * 24 * 60 * 60,\n sameSite: config.localeCookie?.sameSite || 'lax',\n };\n}\n\n/**\n * Check if a locale is supported\n */\nexport function isSupportedLocale(locale: string): boolean {\n return getLocales().includes(locale);\n}\n\n/**\n * Get full locale name for display\n */\nexport function getLocaleName(locale: string): string {\n const names = getLocaleNames();\n return names[locale] || locale;\n}\n\n/**\n * Get short locale label for compact display\n */\nexport function getLocaleLabel(locale: string): string {\n const labels = getLocaleLabels();\n return labels[locale] || locale.toUpperCase();\n}\n\n// ====================================\n// Legacy exports for backward compatibility\n// These will be removed in a future major version\n// ====================================\n\n/**\n * @deprecated Use getLocales() instead. This export will be removed in v1.0.0\n */\nexport const locales = ['fr', 'en'] as const;\n\n/**\n * @deprecated Import from config schemas instead\n */\nexport type Locale = 'fr' | 'en';\n\n/**\n * @deprecated Use getDefaultLocale() instead. This export will be removed in v1.0.0\n */\nexport const defaultLocale: Locale = 'fr';\n\n/**\n * @deprecated Use getLocaleNames() instead. This export will be removed in v1.0.0\n */\nexport const localeNames: Record<Locale, string> = {\n fr: 'Français',\n en: 'English',\n};\n\n/**\n * @deprecated Use getLocaleLabels() instead. This export will be removed in v1.0.0\n */\nexport const localeLabels: Record<Locale, string> = {\n fr: 'FR',\n en: 'EN',\n};\n","// ABOUTME: CSS generation utility for theme system\n// ABOUTME: Converts theme configuration to CSS custom properties\n\nimport { type ThemeConfig } from '../../config/theme.schema';\n\nexport function generateThemeCSS(theme: ThemeConfig): string {\n return `\n /* Brand Colors */\n --color-primary: ${theme.brand.colors.primary};\n --color-primary-hover: ${theme.brand.colors.primaryHover};\n --color-primary-light: ${theme.brand.colors.primaryLight};\n --color-primary-dark: ${theme.brand.colors.primaryDark};\n --color-primary-gradient-start: ${theme.brand.colors.primaryGradientStart};\n --color-primary-gradient-end: ${theme.brand.colors.primaryGradientEnd};\n --color-hero-gradient-start: ${theme.brand.colors.heroGradientStart};\n --color-hero-gradient-end: ${theme.brand.colors.heroGradientEnd};\n --color-footer-gradient-start: ${theme.brand.colors.footerGradientStart};\n --color-footer-gradient-end: ${theme.brand.colors.footerGradientEnd};\n\n /* Fonts */\n --font-heading: ${theme.brand.fonts.heading.family}, ${theme.brand.fonts.heading.fallback};\n --font-body: ${theme.brand.fonts.body.family}, ${theme.brand.fonts.body.fallback};\n --font-serif: ${theme.brand.fonts.heading.family}, ${theme.brand.fonts.heading.fallback};\n --font-sans: ${theme.brand.fonts.body.family}, ${theme.brand.fonts.body.fallback};\n\n /* Slate Colors */\n --color-slate-50: ${theme.colors.slate[50]};\n --color-slate-100: ${theme.colors.slate[100]};\n --color-slate-200: ${theme.colors.slate[200]};\n --color-slate-300: ${theme.colors.slate[300]};\n --color-slate-400: ${theme.colors.slate[400]};\n --color-slate-500: ${theme.colors.slate[500]};\n --color-slate-600: ${theme.colors.slate[600]};\n --color-slate-700: ${theme.colors.slate[700]};\n --color-slate-800: ${theme.colors.slate[800]};\n --color-slate-900: ${theme.colors.slate[900]};\n `.trim();\n}\n\nexport function generateDesignTokens(theme: ThemeConfig): string {\n // Border radius mapping\n const radiusMap: Record<ThemeConfig['design']['borderRadius'], string> = {\n sharp: '0',\n rounded: '0.375rem',\n pill: '9999px',\n };\n\n // Shadow mapping\n const shadowMap: Record<ThemeConfig['design']['shadows'], string> = {\n flat: 'none',\n subtle: '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',\n prominent: '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',\n };\n\n // Spacing mapping\n const spacingMap: Record<ThemeConfig['design']['spacing'], { section: string; element: string }> = {\n compact: {\n section: '3rem',\n element: '1rem',\n },\n comfortable: {\n section: '6rem',\n element: '1.5rem',\n },\n spacious: {\n section: '8rem',\n element: '2rem',\n },\n };\n\n const spacing = spacingMap[theme.design.spacing];\n\n return `\n /* Design Tokens */\n --radius-default: ${radiusMap[theme.design.borderRadius]};\n --shadow-default: ${shadowMap[theme.design.shadows]};\n --space-section: ${spacing.section};\n --space-element: ${spacing.element};\n `.trim();\n}\n","// ABOUTME: Dynamic font loading utility for theme system\n// ABOUTME: Loads Google Fonts based on theme configuration\n\nimport { type ThemeConfig } from '../../config/theme.schema';\n\n/**\n * Gets the font import configuration for Next.js\n * This prepares the data needed to dynamically import fonts\n */\nexport function getFontConfig(theme: ThemeConfig) {\n return {\n heading: {\n family: theme.brand.fonts.heading.family,\n weights: theme.brand.fonts.heading.weights.map(String),\n variable: '--font-heading',\n },\n body: {\n family: theme.brand.fonts.body.family,\n weights: theme.brand.fonts.body.weights.map(String),\n variable: '--font-body',\n },\n };\n}\n\n/**\n * Generates CSS variable names from theme fonts\n */\nexport function getFontVariables(theme: ThemeConfig): string {\n const headingFamily = theme.brand.fonts.heading.family.replace(/\\s+/g, '-').toLowerCase();\n const bodyFamily = theme.brand.fonts.body.family.replace(/\\s+/g, '-').toLowerCase();\n\n return `var(--font-${headingFamily}) var(--font-${bodyFamily})`;\n}\n","// ABOUTME: Content utility functions\n// ABOUTME: Helpers for working with localized content\n\n/**\n * Helper to get localized string from content\n */\nexport function getLocalizedString(\n localizedString: { [locale: string]: string },\n locale: string\n): string {\n return localizedString[locale] || localizedString['en'] || '';\n}\n","// ABOUTME: Utility functions for loading and processing policy markdown files\n// ABOUTME: Supports static generation of policy pages from markdown with frontmatter\n\nimport fs from 'fs';\nimport path from 'path';\nimport { compileMDX } from 'next-mdx-remote/rsc';\nimport rehypeSlug from 'rehype-slug';\n\nexport interface PolicyMetadata {\n /** Policy title */\n title: string;\n /** Last updated date (ISO string) */\n lastUpdated: string;\n /** Optional description for SEO */\n description?: string;\n /** Additional custom fields */\n [key: string]: string | undefined;\n}\n\nexport interface Policy {\n /** Compiled MDX content */\n content: JSX.Element;\n /** Frontmatter metadata */\n metadata: PolicyMetadata;\n /** Policy slug (filename without locale/extension) */\n slug: string;\n /** Locale */\n locale: string;\n}\n\n/**\n * Load a policy markdown file and compile it to React\n *\n * @param slug - Policy slug (e.g., 'privacy-policy', 'terms-of-service')\n * @param locale - Locale code (e.g., 'en', 'fr')\n * @param contentDir - Directory containing policy files @default 'src/content/policies'\n * @returns Compiled policy with content and metadata\n *\n * @example\n * ```typescript\n * const { content, metadata } = await loadPolicy('privacy-policy', 'en');\n *\n * return (\n * <PolicyLayout\n * title={metadata.title}\n * lastUpdated={metadata.lastUpdated}\n * >\n * {content}\n * </PolicyLayout>\n * );\n * ```\n */\nexport async function loadPolicy(\n slug: string,\n locale: string,\n contentDir = 'src/content/policies'\n): Promise<Policy> {\n const filePath = path.join(process.cwd(), contentDir, `${slug}.${locale}.md`);\n\n // Check if file exists\n if (!fs.existsSync(filePath)) {\n throw new Error(\n `Policy file not found: ${slug}.${locale}.md in ${contentDir}\\n` +\n `Looking for: ${filePath}`\n );\n }\n\n // Read file\n const source = fs.readFileSync(filePath, 'utf8');\n\n // Compile MDX with frontmatter\n const { content, frontmatter } = await compileMDX<PolicyMetadata>({\n source,\n options: {\n parseFrontmatter: true,\n mdxOptions: {\n rehypePlugins: [rehypeSlug],\n },\n },\n });\n\n // Validate required frontmatter fields\n if (!frontmatter.title) {\n throw new Error(`Policy ${slug}.${locale}.md is missing required frontmatter field: title`);\n }\n if (!frontmatter.lastUpdated) {\n throw new Error(\n `Policy ${slug}.${locale}.md is missing required frontmatter field: lastUpdated`\n );\n }\n\n return {\n content,\n metadata: frontmatter,\n slug,\n locale,\n };\n}\n\n/**\n * Get all unique policy slugs (without locale suffix)\n *\n * @param contentDir - Directory containing policy files @default 'src/content/policies'\n * @returns Array of policy slugs\n *\n * @example\n * ```typescript\n * const slugs = getPolicySlugs(); // ['privacy-policy', 'terms-of-service']\n * ```\n */\nexport function getPolicySlugs(contentDir = 'src/content/policies'): string[] {\n const policiesDir = path.join(process.cwd(), contentDir);\n\n // Check if directory exists\n if (!fs.existsSync(policiesDir)) {\n console.warn(`Policies directory not found: ${policiesDir}`);\n return [];\n }\n\n const files = fs.readdirSync(policiesDir);\n\n // Extract unique slugs (remove locale and extension)\n const slugs = Array.from(\n new Set(\n files\n .filter((file) => file.endsWith('.md') || file.endsWith('.mdx'))\n .map((file) => {\n // Remove .{locale}.md or .{locale}.mdx\n return file.replace(/\\.[a-z]{2}(-[A-Z]{2})?\\.mdx?$/, '');\n })\n )\n );\n\n return slugs;\n}\n\n/**\n * Get all policies for a specific locale with their metadata\n *\n * @param locale - Locale code\n * @param contentDir - Directory containing policy files @default 'src/content/policies'\n * @returns Array of policies with metadata\n *\n * @example\n * ```typescript\n * const policies = await getAllPolicies('en');\n *\n * // Render policy list\n * policies.map(policy => (\n * <Link key={policy.slug} href={`/policies/${policy.slug}`}>\n * {policy.metadata.title}\n * </Link>\n * ));\n * ```\n */\nexport async function getAllPolicies(\n locale: string,\n contentDir = 'src/content/policies'\n): Promise<Omit<Policy, 'content'>[]> {\n const slugs = getPolicySlugs(contentDir);\n\n const policies = await Promise.all(\n slugs.map(async (slug) => {\n try {\n const policy = await loadPolicy(slug, locale, contentDir);\n // Return without content to keep response size small\n return {\n slug: policy.slug,\n locale: policy.locale,\n metadata: policy.metadata,\n };\n } catch (error) {\n // Skip if policy doesn't exist for this locale\n console.warn(`Skipping ${slug} for locale ${locale}:`, error);\n return null;\n }\n })\n );\n\n // Filter out null values (policies that don't exist for this locale)\n return policies.filter((p): p is Omit<Policy, 'content'> => p !== null);\n}\n\n/**\n * Get available locales for a specific policy\n *\n * @param slug - Policy slug\n * @param contentDir - Directory containing policy files @default 'src/content/policies'\n * @returns Array of locale codes\n *\n * @example\n * ```typescript\n * const locales = getPolicyLocales('privacy-policy'); // ['en', 'fr']\n * ```\n */\nexport function getPolicyLocales(slug: string, contentDir = 'src/content/policies'): string[] {\n const policiesDir = path.join(process.cwd(), contentDir);\n\n if (!fs.existsSync(policiesDir)) {\n return [];\n }\n\n const files = fs.readdirSync(policiesDir);\n\n // Find all files matching the slug pattern\n const locales = files\n .filter((file) => {\n const pattern = new RegExp(`^${slug}\\\\.[a-z]{2}(-[A-Z]{2})?\\\\.mdx?$`);\n return pattern.test(file);\n })\n .map((file) => {\n // Extract locale from filename\n const match = file.match(/\\.([a-z]{2}(-[A-Z]{2})?)\\.mdx?$/);\n return match ? match[1] : null;\n })\n .filter((locale): locale is string => locale !== null);\n\n return locales;\n}\n","// ABOUTME: Navigation utility functions\n// ABOUTME: Helpers for working with localized navigation and string placeholders\n\n/**\n * Helper to get localized string from navigation\n */\nexport function getNavigationString(\n localizedString: { [locale: string]: string },\n locale: string\n): string {\n return localizedString[locale] || localizedString['en'] || '';\n}\n\n/**\n * Helper to replace placeholders in strings\n */\nexport function replaceVariables(\n text: string,\n variables: Record<string, string>\n): string {\n let result = text;\n Object.entries(variables).forEach(([key, value]) => {\n result = result.replace(`{${key}}`, value);\n });\n return result;\n}\n","// ABOUTME: i18n configuration export\n// ABOUTME: Export i18n types and configuration\n\n// Core types\nexport type {\n I18nConfig,\n LocalePrefix,\n LocaleCookieConfig,\n LanguagePreference,\n ValidLocale,\n SlugTranslations,\n} from './types';\n\n// Configuration management\nexport {\n setI18nConfig,\n getI18nConfig,\n isI18nConfigInitialized,\n getLocales,\n getDefaultLocale,\n getLocalePrefix,\n getLocaleNames,\n getLocaleLabels,\n getRtlLocales,\n isLocaleDetectionEnabled,\n getLocaleCookieConfig,\n isSupportedLocale,\n getLocaleName,\n getLocaleLabel,\n} from './config';\n\n// Legacy exports (deprecated, will be removed in v1.0.0)\nexport { locales, defaultLocale } from './config';\nexport type { Locale } from './config';\n\n// Cookie management\nexport { getLocaleFromCookie, setLocaleCookie, LOCALE_COOKIE_NAME } from './locale-cookie';\n\n// Slug translations\nexport { translateSlug, defaultSlugTranslations } from './slug-translations';\n\n// Locale-aware formatters\nexport {\n formatDate,\n formatNumber,\n formatCurrency,\n formatRelativeTime,\n formatDateRange,\n formatList,\n formatFileSize,\n getRelativeTime,\n} from './formatters';\n\n// Middleware for routing\nexport { createI18nMiddleware } from './middleware';\n\n// Utilities\nexport {\n isRtlLocale,\n getTextDirection,\n validateLocale,\n getAlternateLocales,\n normalizeLocale,\n matchLocale,\n getLocaleAutonym,\n formatLocaleDisplay,\n} from './utils';\n","// ABOUTME: Utilities for persisting user locale preference via cookies\n// ABOUTME: Allows remembering language choice across sessions\n\nimport { getLocaleCookieConfig, isSupportedLocale } from './config';\n\nexport const LOCALE_COOKIE_NAME = 'NEXT_LOCALE';\n\n/**\n * Get locale from cookie\n * Returns null if cookie not found or locale not supported\n */\nexport function getLocaleFromCookie(): string | null {\n if (typeof document === 'undefined') return null;\n\n // Safe: only runs in browser, but check config anyway for build safety\n try {\n const cookieConfig = getLocaleCookieConfig();\n const cookieName = cookieConfig.name;\n\n const cookie = document.cookie\n .split('; ')\n .find((row) => row.startsWith(`${cookieName}=`));\n\n if (!cookie) return null;\n\n const value = cookie.split('=')[1];\n\n // Validate that locale is supported\n if (isSupportedLocale(value)) {\n return value;\n }\n\n return null;\n } catch (error) {\n // Config not initialized - return null\n if (process.env.NODE_ENV !== 'production') {\n console.warn('getLocaleFromCookie: i18n config not initialized');\n }\n return null;\n }\n}\n\n/**\n * Set locale cookie\n * Uses configuration from i18n config\n */\nexport function setLocaleCookie(locale: string) {\n if (typeof document === 'undefined') return;\n\n try {\n // Validate locale is supported before setting\n if (!isSupportedLocale(locale)) {\n console.warn(`Attempted to set unsupported locale: ${locale}`);\n return;\n }\n\n const cookieConfig = getLocaleCookieConfig();\n\n document.cookie = `${cookieConfig.name}=${locale}; max-age=${cookieConfig.maxAge}; path=/; SameSite=${cookieConfig.sameSite}`;\n } catch (error) {\n // Config not initialized - fail silently\n if (process.env.NODE_ENV !== 'production') {\n console.warn('setLocaleCookie: i18n config not initialized');\n }\n }\n}\n","// ABOUTME: Slug translation mappings for bilingual routing\n// ABOUTME: Maps French slugs to English equivalents and vice versa\n\nimport type { SlugTranslations } from './types';\n\n// Re-export for backward compatibility\nexport type { SlugTranslations };\n\n// Default slug translations - can be overridden by apps\nexport const defaultSlugTranslations: SlugTranslations = {\n fr: {\n '/marketing-par-courriel': '/email-marketing',\n '/courriels-transactionnels': '/transactional-emails',\n '/forfaits': '/pricing',\n '/nous-contacter': '/contact',\n '/a-propos': '/about',\n '/politique-de-confidentialite': '/privacy-policy',\n '/conditions-dutilisation': '/terms-of-service',\n '/politique-anti-pourriel': '/anti-spam-policy',\n },\n en: {\n '/email-marketing': '/marketing-par-courriel',\n '/transactional-emails': '/courriels-transactionnels',\n '/pricing': '/forfaits',\n '/contact': '/nous-contacter',\n '/about': '/a-propos',\n '/privacy-policy': '/politique-de-confidentialite',\n '/terms-of-service': '/conditions-dutilisation',\n '/anti-spam-policy': '/politique-anti-pourriel',\n },\n};\n\n/**\n * Translates a path from one locale to another\n *\n * Merges translations in this priority order:\n * 1. Custom translations passed as parameter (highest priority)\n * 2. Translations from i18n config (if configured)\n * 3. Default translations (lowest priority)\n *\n * @param path - The current path (without locale prefix)\n * @param fromLocale - The current locale\n * @param toLocale - The target locale\n * @param customTranslations - Optional custom translations to merge with defaults\n * @returns The translated path\n *\n * @example\n * ```typescript\n * // With config translations\n * translateSlug('/about', 'en', 'fr')\n * // Returns: '/a-propos' (from config or defaults)\n *\n * // With custom override\n * translateSlug('/about', 'en', 'fr', {\n * en: { '/about': '/notre-equipe' }\n * })\n * // Returns: '/notre-equipe' (custom override)\n *\n * // Nested paths\n * translateSlug('/about/team', 'en', 'fr')\n * // Returns: '/a-propos/team' (translates base, keeps rest)\n * ```\n */\nexport function translateSlug(\n path: string,\n fromLocale: string,\n toLocale: string,\n customTranslations?: SlugTranslations\n): string {\n // Try to get config translations (may not be initialized)\n let configTranslations: SlugTranslations = {};\n try {\n const { getI18nConfig } = require('./config');\n const config = getI18nConfig();\n configTranslations = config.slugTranslations || {};\n } catch {\n // Config not initialized, use empty\n }\n\n // Merge translations: custom > config > defaults\n const translations = mergeTranslations(\n defaultSlugTranslations,\n configTranslations,\n customTranslations || {}\n );\n\n // Get the translation map for the source locale\n const translationMap = translations[fromLocale];\n\n if (!translationMap) {\n return path; // No translations for this locale, return original\n }\n\n // Check if we have a direct translation\n if (translationMap[path]) {\n return translationMap[path];\n }\n\n // Check for nested paths (e.g., /marketing-par-courriel/feature)\n for (const [fromSlug, toSlug] of Object.entries(translationMap)) {\n if (path.startsWith(fromSlug + '/')) {\n // Replace the base slug and keep the rest of the path\n return path.replace(fromSlug, toSlug);\n }\n }\n\n // No translation found, return original path\n return path;\n}\n\n/**\n * Merge multiple slug translation objects\n * Later objects take precedence over earlier ones\n */\nfunction mergeTranslations(...translations: SlugTranslations[]): SlugTranslations {\n const merged: SlugTranslations = {};\n\n for (const trans of translations) {\n for (const [locale, slugs] of Object.entries(trans)) {\n if (!merged[locale]) {\n merged[locale] = {};\n }\n Object.assign(merged[locale], slugs);\n }\n }\n\n return merged;\n}\n","// ABOUTME: Locale-aware formatting utilities using native Intl API\n// ABOUTME: Date, number, currency, and relative time formatters for internationalization\n\n/**\n * Format a date according to locale\n *\n * @param date - Date to format\n * @param locale - Locale code (e.g., 'en', 'fr', 'es')\n * @param options - Intl.DateTimeFormat options\n *\n * @example\n * ```typescript\n * formatDate(new Date('2026-02-01'), 'fr', { dateStyle: 'long' })\n * // \"1 février 2026\"\n *\n * formatDate(new Date('2026-02-01'), 'en', { dateStyle: 'long' })\n * // \"February 1, 2026\"\n *\n * formatDate(new Date(), 'fr', {\n * weekday: 'long',\n * year: 'numeric',\n * month: 'long',\n * day: 'numeric'\n * })\n * // \"samedi 1 février 2026\"\n * ```\n */\nexport function formatDate(\n date: Date,\n locale: string,\n options?: Intl.DateTimeFormatOptions\n): string {\n return new Intl.DateTimeFormat(locale, options).format(date);\n}\n\n/**\n * Format a number according to locale\n *\n * @param value - Number to format\n * @param locale - Locale code\n * @param options - Intl.NumberFormat options\n *\n * @example\n * ```typescript\n * formatNumber(1234567.89, 'en')\n * // \"1,234,567.89\"\n *\n * formatNumber(1234567.89, 'fr')\n * // \"1 234 567,89\"\n *\n * formatNumber(0.42, 'en', { style: 'percent' })\n * // \"42%\"\n *\n * formatNumber(1234, 'en', { minimumFractionDigits: 2 })\n * // \"1,234.00\"\n * ```\n */\nexport function formatNumber(\n value: number,\n locale: string,\n options?: Intl.NumberFormatOptions\n): string {\n return new Intl.NumberFormat(locale, options).format(value);\n}\n\n/**\n * Format a currency amount according to locale\n *\n * @param amount - Amount to format\n * @param locale - Locale code\n * @param currency - ISO 4217 currency code (e.g., 'USD', 'EUR', 'CAD')\n * @param options - Additional Intl.NumberFormat options\n *\n * @example\n * ```typescript\n * formatCurrency(1299.99, 'en', 'USD')\n * // \"$1,299.99\"\n *\n * formatCurrency(1299.99, 'fr', 'EUR')\n * // \"1 299,99 €\"\n *\n * formatCurrency(1299.99, 'fr-CA', 'CAD')\n * // \"1 299,99 $\"\n *\n * formatCurrency(1299.99, 'ja', 'JPY')\n * // \"¥1,300\"\n *\n * formatCurrency(0.99, 'en', 'USD', { currencyDisplay: 'name' })\n * // \"0.99 US dollars\"\n * ```\n */\nexport function formatCurrency(\n amount: number,\n locale: string,\n currency: string,\n options?: Intl.NumberFormatOptions\n): string {\n return new Intl.NumberFormat(locale, {\n style: 'currency',\n currency,\n ...options,\n }).format(amount);\n}\n\n/**\n * Format relative time (e.g., \"2 hours ago\", \"in 3 days\")\n *\n * @param value - Numeric value (negative for past, positive for future)\n * @param unit - Time unit\n * @param locale - Locale code\n * @param options - Intl.RelativeTimeFormat options\n *\n * @example\n * ```typescript\n * formatRelativeTime(-2, 'hour', 'en')\n * // \"2 hours ago\"\n *\n * formatRelativeTime(-2, 'hour', 'fr')\n * // \"il y a 2 heures\"\n *\n * formatRelativeTime(3, 'day', 'en')\n * // \"in 3 days\"\n *\n * formatRelativeTime(-1, 'week', 'es')\n * // \"hace 1 semana\"\n *\n * formatRelativeTime(-5, 'minute', 'en', { numeric: 'auto' })\n * // \"5 minutes ago\"\n * ```\n */\nexport function formatRelativeTime(\n value: number,\n unit: Intl.RelativeTimeFormatUnit,\n locale: string,\n options?: Intl.RelativeTimeFormatOptions\n): string {\n return new Intl.RelativeTimeFormat(locale, options).format(value, unit);\n}\n\n/**\n * Format a date range according to locale\n * Note: formatRange requires newer TypeScript/Node versions\n *\n * @param startDate - Start date\n * @param endDate - End date\n * @param locale - Locale code\n * @param options - Intl.DateTimeFormat options\n *\n * @example\n * ```typescript\n * formatDateRange(\n * new Date('2026-02-01'),\n * new Date('2026-02-15'),\n * 'en',\n * { month: 'long', day: 'numeric' }\n * )\n * // \"February 1 – 15\"\n * ```\n */\nexport function formatDateRange(\n startDate: Date,\n endDate: Date,\n locale: string,\n options?: Intl.DateTimeFormatOptions\n): string {\n const formatter = new Intl.DateTimeFormat(locale, options);\n // Use formatRange if available, otherwise format separately\n if ('formatRange' in formatter) {\n return (formatter as any).formatRange(startDate, endDate);\n }\n return `${formatter.format(startDate)} – ${formatter.format(endDate)}`;\n}\n\n/**\n * Format a list of items according to locale\n * Falls back to simple comma-separated list if Intl.ListFormat not available\n *\n * @param items - Array of items to format\n * @param locale - Locale code\n *\n * @example\n * ```typescript\n * formatList(['apples', 'oranges', 'bananas'], 'en')\n * // \"apples, oranges, and bananas\"\n *\n * formatList(['pommes', 'oranges', 'bananes'], 'fr')\n * // \"pommes, oranges et bananes\"\n * ```\n */\nexport function formatList(\n items: string[],\n locale: string\n): string {\n // Check if Intl.ListFormat is available\n if (typeof Intl !== 'undefined' && 'ListFormat' in Intl) {\n return new (Intl as any).ListFormat(locale).format(items);\n }\n\n // Fallback for older environments\n if (items.length === 0) return '';\n if (items.length === 1) return items[0];\n if (items.length === 2) {\n const connector = locale === 'fr' ? ' et ' : ' and ';\n return items.join(connector);\n }\n\n const last = items[items.length - 1];\n const rest = items.slice(0, -1);\n const connector = locale === 'fr' ? ' et ' : ', and ';\n return rest.join(', ') + connector + last;\n}\n\n/**\n * Format file size in bytes to human-readable format\n * Not using Intl, but useful for internationalized apps\n *\n * @param bytes - File size in bytes\n * @param locale - Locale code\n * @param decimals - Number of decimal places @default 2\n *\n * @example\n * ```typescript\n * formatFileSize(1024, 'en')\n * // \"1.00 KB\"\n *\n * formatFileSize(1048576, 'fr')\n * // \"1,00 Mo\"\n *\n * formatFileSize(1073741824, 'en', 1)\n * // \"1.0 GB\"\n * ```\n */\nexport function formatFileSize(\n bytes: number,\n locale: string,\n decimals: number = 2\n): string {\n if (bytes === 0) return '0 B';\n\n const k = 1024;\n const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n const value = bytes / Math.pow(k, i);\n\n return `${formatNumber(value, locale, {\n minimumFractionDigits: decimals,\n maximumFractionDigits: decimals,\n })} ${sizes[i]}`;\n}\n\n/**\n * Get relative time from a date (auto-selects best unit)\n *\n * @param date - Date to compare against now\n * @param locale - Locale code\n * @param options - Intl.RelativeTimeFormat options\n *\n * @example\n * ```typescript\n * // If date is 2 hours ago\n * getRelativeTime(pastDate, 'en')\n * // \"2 hours ago\"\n *\n * // If date is 3 days from now\n * getRelativeTime(futureDate, 'en')\n * // \"in 3 days\"\n *\n * // If date is 45 seconds ago\n * getRelativeTime(recentDate, 'fr')\n * // \"il y a 45 secondes\"\n * ```\n */\nexport function getRelativeTime(\n date: Date,\n locale: string,\n options?: Intl.RelativeTimeFormatOptions\n): string {\n const now = new Date();\n const diffMs = date.getTime() - now.getTime();\n const diffSecs = Math.round(diffMs / 1000);\n const diffMins = Math.round(diffSecs / 60);\n const diffHours = Math.round(diffMins / 60);\n const diffDays = Math.round(diffHours / 24);\n const diffWeeks = Math.round(diffDays / 7);\n const diffMonths = Math.round(diffDays / 30);\n const diffYears = Math.round(diffDays / 365);\n\n if (Math.abs(diffSecs) < 60) {\n return formatRelativeTime(diffSecs, 'second', locale, options);\n } else if (Math.abs(diffMins) < 60) {\n return formatRelativeTime(diffMins, 'minute', locale, options);\n } else if (Math.abs(diffHours) < 24) {\n return formatRelativeTime(diffHours, 'hour', locale, options);\n } else if (Math.abs(diffDays) < 7) {\n return formatRelativeTime(diffDays, 'day', locale, options);\n } else if (Math.abs(diffWeeks) < 4) {\n return formatRelativeTime(diffWeeks, 'week', locale, options);\n } else if (Math.abs(diffMonths) < 12) {\n return formatRelativeTime(diffMonths, 'month', locale, options);\n } else {\n return formatRelativeTime(diffYears, 'year', locale, options);\n }\n}\n","// ABOUTME: Middleware factory for automatic locale detection and routing\n// ABOUTME: Handles URL rewriting, redirects, and cookie persistence based on prefix mode\n\nimport { NextRequest, NextResponse } from 'next/server';\nimport type { I18nConfig } from './types';\n\n/**\n * Create Next.js middleware for i18n routing\n *\n * @param config - i18n configuration\n * @returns Middleware function\n *\n * @example\n * ```typescript\n * // middleware.ts\n * import { createI18nMiddleware } from 'simple-site-framework/lib/i18n';\n * import { i18nConfig } from './src/config/i18n';\n *\n * export default createI18nMiddleware(i18nConfig);\n *\n * export const config = {\n * matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],\n * };\n * ```\n */\nexport function createI18nMiddleware(config: I18nConfig) {\n return function middleware(request: NextRequest): NextResponse {\n const { pathname } = request.nextUrl;\n\n // 1. Extract locale from URL pathname\n const pathnameLocale = getLocaleFromPathname(pathname, config);\n\n // 2. If locale found in URL, validate it\n if (pathnameLocale) {\n if (!config.locales.includes(pathnameLocale)) {\n // Invalid locale in URL, redirect to default\n return redirectToLocale(request, config.defaultLocale, pathname, config);\n }\n\n // Valid locale in URL, set cookie and continue\n const response = NextResponse.next();\n setLocaleCookie(response, pathnameLocale, config);\n return response;\n }\n\n // 3. No locale in URL, detect from browser/cookie\n const detectedLocale = detectLocale(request, config);\n\n // 4. Handle based on prefix mode\n const prefixMode = config.localePrefix || 'as-needed';\n\n if (prefixMode === 'always') {\n // Always redirect to add locale prefix\n return redirectToLocale(request, detectedLocale, pathname, config);\n } else if (prefixMode === 'as-needed') {\n // Only redirect if detected locale is NOT the default\n if (detectedLocale !== config.defaultLocale) {\n return redirectToLocale(request, detectedLocale, pathname, config);\n }\n // Default locale, continue without redirect but set cookie\n const response = NextResponse.next();\n setLocaleCookie(response, config.defaultLocale, config);\n return response;\n } else {\n // 'never' mode: no redirects, just set cookie\n const response = NextResponse.next();\n setLocaleCookie(response, detectedLocale, config);\n return response;\n }\n };\n}\n\n/**\n * Extract locale from URL pathname\n * Returns null if no locale found\n */\nfunction getLocaleFromPathname(pathname: string, config: I18nConfig): string | null {\n const segments = pathname.split('/').filter(Boolean);\n\n if (segments.length === 0) {\n return null;\n }\n\n const firstSegment = segments[0];\n\n // Check if first segment matches a supported locale\n if (config.locales.includes(firstSegment)) {\n return firstSegment;\n }\n\n return null;\n}\n\n/**\n * Detect locale from cookie and Accept-Language header\n * Falls back to default locale\n */\nfunction detectLocale(request: NextRequest, config: I18nConfig): string {\n // 1. Check cookie first (user's explicit preference)\n const cookieName = config.localeCookie?.name || 'NEXT_LOCALE';\n const cookieLocale = request.cookies.get(cookieName)?.value;\n\n if (cookieLocale && config.locales.includes(cookieLocale)) {\n return cookieLocale;\n }\n\n // 2. Check Accept-Language header (browser preference)\n if (config.localeDetection !== false) {\n const acceptLanguage = request.headers.get('accept-language');\n const browserLocale = negotiateLanguage(acceptLanguage, config.locales);\n\n if (browserLocale) {\n return browserLocale;\n }\n }\n\n // 3. Fall back to default locale\n return config.defaultLocale;\n}\n\n/**\n * Negotiate language from Accept-Language header\n * Returns best match from supported locales, or null if no match\n *\n * @example\n * Accept-Language: fr-CA,fr;q=0.9,en;q=0.8\n * Supported: ['en', 'fr', 'es']\n * Result: 'fr' (highest quality match)\n */\nfunction negotiateLanguage(\n acceptLanguage: string | null,\n supportedLocales: readonly string[]\n): string | null {\n if (!acceptLanguage) {\n return null;\n }\n\n // Parse Accept-Language header into preferences\n const preferences = parseAcceptLanguage(acceptLanguage);\n\n // Find best match\n for (const pref of preferences) {\n // Exact match (e.g., 'en' matches 'en')\n if (supportedLocales.includes(pref.locale)) {\n return pref.locale;\n }\n\n // Language family match (e.g., 'en-US' matches 'en')\n const languageCode = pref.locale.split('-')[0];\n if (supportedLocales.includes(languageCode)) {\n return languageCode;\n }\n\n // Check if any supported locale starts with this language\n const match = supportedLocales.find(\n (locale) => locale.startsWith(languageCode + '-') || locale === languageCode\n );\n if (match) {\n return match;\n }\n }\n\n return null;\n}\n\n/**\n * Parse Accept-Language header into sorted preferences\n * Returns array sorted by quality value (highest first)\n */\ninterface LanguagePreference {\n locale: string;\n quality: number;\n}\n\nfunction parseAcceptLanguage(header: string): LanguagePreference[] {\n const preferences: LanguagePreference[] = [];\n\n // Split by comma and parse each preference\n const parts = header.split(',').map((p) => p.trim());\n\n for (const part of parts) {\n const [locale, ...rest] = part.split(';').map((p) => p.trim());\n\n // Extract quality value (default to 1.0)\n let quality = 1.0;\n const qParam = rest.find((p) => p.startsWith('q='));\n if (qParam) {\n const qValue = parseFloat(qParam.substring(2));\n if (!isNaN(qValue)) {\n quality = qValue;\n }\n }\n\n preferences.push({\n locale: locale.toLowerCase(),\n quality,\n });\n }\n\n // Sort by quality (highest first)\n preferences.sort((a, b) => b.quality - a.quality);\n\n return preferences;\n}\n\n/**\n * Redirect to URL with locale prefix\n */\nfunction redirectToLocale(\n request: NextRequest,\n locale: string,\n pathname: string,\n config: I18nConfig\n): NextResponse {\n const url = request.nextUrl.clone();\n\n // Remove any existing locale prefix\n const pathWithoutLocale = removeLocalePrefix(pathname, config);\n\n // Build new pathname with locale\n url.pathname = `/${locale}${pathWithoutLocale}`;\n\n const response = NextResponse.redirect(url);\n setLocaleCookie(response, locale, config);\n\n return response;\n}\n\n/**\n * Remove locale prefix from pathname if present\n */\nfunction removeLocalePrefix(pathname: string, config: I18nConfig): string {\n const segments = pathname.split('/').filter(Boolean);\n\n if (segments.length === 0) {\n return '/';\n }\n\n const firstSegment = segments[0];\n\n if (config.locales.includes(firstSegment)) {\n // Remove locale segment\n const remaining = segments.slice(1);\n return remaining.length > 0 ? `/${remaining.join('/')}` : '/';\n }\n\n return pathname;\n}\n\n/**\n * Set locale cookie on response\n */\nfunction setLocaleCookie(\n response: NextResponse,\n locale: string,\n config: I18nConfig\n): void {\n const cookieConfig = config.localeCookie || {};\n\n response.cookies.set({\n name: cookieConfig.name || 'NEXT_LOCALE',\n value: locale,\n maxAge: cookieConfig.maxAge || 365 * 24 * 60 * 60, // 1 year\n path: '/',\n sameSite: cookieConfig.sameSite || 'lax',\n });\n}\n","// ABOUTME: i18n utility functions for locale management and text direction\n// ABOUTME: RTL support, validation, and enhanced localized string handling\n\nimport { getI18nConfig, getRtlLocales, isSupportedLocale as isSupported } from './config';\n\n/**\n * Check if a locale uses right-to-left text direction\n *\n * @param locale - Locale code to check\n * @returns true if locale is RTL, false otherwise\n *\n * @example\n * ```typescript\n * isRtlLocale('ar') // true (Arabic)\n * isRtlLocale('he') // true (Hebrew)\n * isRtlLocale('en') // false (English)\n * isRtlLocale('fr') // false (French)\n * ```\n */\nexport function isRtlLocale(locale: string): boolean {\n const rtlLocales = getRtlLocales();\n return rtlLocales.includes(locale);\n}\n\n/**\n * Get text direction for a locale\n *\n * @param locale - Locale code\n * @returns 'rtl' for right-to-left locales, 'ltr' for left-to-right\n *\n * @example\n * ```tsx\n * // In layout\n * <html lang={locale} dir={getTextDirection(locale)}>\n *\n * // Results:\n * getTextDirection('ar') // 'rtl'\n * getTextDirection('en') // 'ltr'\n * ```\n */\nexport function getTextDirection(locale: string): 'ltr' | 'rtl' {\n return isRtlLocale(locale) ? 'rtl' : 'ltr';\n}\n\n// Note: getLocalizedString is exported from lib/content/utils\n// Use that version for accessing localized content strings\n\n/**\n * Validate if a locale is supported\n *\n * @param locale - Locale code to validate\n * @returns true if locale is supported, false otherwise\n *\n * @example\n * ```typescript\n * // With config: locales: ['en', 'fr', 'es']\n * validateLocale('en') // true\n * validateLocale('fr') // true\n * validateLocale('de') // false\n * validateLocale('invalid') // false\n * ```\n */\nexport function validateLocale(locale: string): boolean {\n return isSupported(locale);\n}\n\n/**\n * Get all supported locales except the current one\n *\n * @param currentLocale - Current locale to exclude\n * @returns Array of alternate locales\n *\n * @example\n * ```typescript\n * // With config: locales: ['en', 'fr', 'es', 'de']\n * getAlternateLocales('en') // ['fr', 'es', 'de']\n * getAlternateLocales('fr') // ['en', 'es', 'de']\n * ```\n */\nexport function getAlternateLocales(currentLocale: string): string[] {\n try {\n const config = getI18nConfig();\n return config.locales.filter((l) => l !== currentLocale);\n } catch (error) {\n // Config not initialized - return empty array\n if (process.env.NODE_ENV !== 'production') {\n console.warn('getAlternateLocales: i18n config not initialized, returning []');\n }\n return [];\n }\n}\n\n/**\n * Normalize locale code to lowercase\n * Handles variants like 'en-US' -> 'en', 'zh-CN' -> 'zh'\n *\n * @param locale - Locale code (can include region)\n * @returns Normalized locale code\n *\n * @example\n * ```typescript\n * normalizeLocale('en-US') // 'en'\n * normalizeLocale('zh-CN') // 'zh'\n * normalizeLocale('FR') // 'fr'\n * normalizeLocale('es') // 'es'\n * ```\n */\nexport function normalizeLocale(locale: string): string {\n return locale.toLowerCase().split('-')[0];\n}\n\n/**\n * Check if a locale code matches a supported locale\n * Handles locale variants (e.g., 'en-US' matches 'en')\n *\n * @param locale - Locale code to check (can include region)\n * @returns Matching supported locale or null\n *\n * @example\n * ```typescript\n * // With config: locales: ['en', 'fr', 'es']\n * matchLocale('en-US') // 'en'\n * matchLocale('fr-CA') // 'fr'\n * matchLocale('de-DE') // null\n * ```\n */\nexport function matchLocale(locale: string): string | null {\n try {\n const config = getI18nConfig();\n const normalized = normalizeLocale(locale);\n\n // Exact match first\n if (config.locales.includes(locale)) {\n return locale;\n }\n\n // Match normalized (e.g., 'en-US' -> 'en')\n if (config.locales.includes(normalized)) {\n return normalized;\n }\n\n // Check if any supported locale starts with this language code\n const match = config.locales.find((l) => l.startsWith(normalized));\n if (match) {\n return match;\n }\n\n return null;\n } catch (error) {\n // Config not initialized - return null\n if (process.env.NODE_ENV !== 'production') {\n console.warn('matchLocale: i18n config not initialized, returning null');\n }\n return null;\n }\n}\n\n/**\n * Get locale display name in its own language (autonym)\n *\n * @param locale - Locale code\n * @returns Display name or locale code if not found\n *\n * @example\n * ```typescript\n * getLocaleAutonym('en') // 'English'\n * getLocaleAutonym('fr') // 'Français'\n * getLocaleAutonym('es') // 'Español'\n * ```\n */\nexport function getLocaleAutonym(locale: string): string {\n // Common language autonyms\n const autonyms: Record<string, string> = {\n en: 'English',\n fr: 'Français',\n es: 'Español',\n de: 'Deutsch',\n it: 'Italiano',\n pt: 'Português',\n ru: 'Русский',\n ja: '日本語',\n ko: '한국어',\n zh: '中文',\n ar: 'العربية',\n he: 'עברית',\n hi: 'हिन्दी',\n tr: 'Türkçe',\n pl: 'Polski',\n nl: 'Nederlands',\n sv: 'Svenska',\n da: 'Dansk',\n no: 'Norsk',\n fi: 'Suomi',\n cs: 'Čeština',\n el: 'Ελληνικά',\n th: 'ไทย',\n vi: 'Tiếng Việt',\n id: 'Bahasa Indonesia',\n ms: 'Bahasa Melayu',\n uk: 'Українська',\n };\n\n // Try to get from config first\n try {\n const { getLocaleNames } = require('./config');\n const names = getLocaleNames();\n if (names[locale]) {\n return names[locale];\n }\n } catch {\n // Config not available\n }\n\n // Fall back to autonym map\n return autonyms[locale] || locale.toUpperCase();\n}\n\n/**\n * Format locale for display (e.g., for language selector)\n *\n * @param locale - Locale code\n * @param format - Display format\n * @returns Formatted locale string\n *\n * @example\n * ```typescript\n * formatLocaleDisplay('en', 'name') // 'English'\n * formatLocaleDisplay('fr', 'label') // 'FR'\n * formatLocaleDisplay('es', 'code') // 'es'\n * ```\n */\nexport function formatLocaleDisplay(\n locale: string,\n format: 'name' | 'label' | 'code' = 'name'\n): string {\n if (format === 'code') {\n return locale;\n }\n\n if (format === 'label') {\n try {\n const { getLocaleLabel } = require('./config');\n return getLocaleLabel(locale);\n } catch {\n return locale.toUpperCase();\n }\n }\n\n // format === 'name'\n return getLocaleAutonym(locale);\n}\n","// ABOUTME: Utility for conditionally joining class names\n// ABOUTME: Useful for combining Tailwind classes with conditional logic\n\nimport { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\n/**\n * Combines class names using clsx and merges Tailwind classes intelligently\n * @param inputs - Class names to combine\n * @returns Merged class string\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","// ABOUTME: Comprehensive analytics tracking utility for GA4 via GTM\n// ABOUTME: Provides type-safe event tracking for conversions, CTAs, forms, and user interactions\n\nimport type { AnalyticsEvent } from '../types/analytics';\n\ndeclare global {\n interface Window {\n dataLayer: unknown[];\n }\n}\n\n// Re-export types for convenience\nexport type * from '../types/analytics';\n\n/**\n * Push event to Google Tag Manager dataLayer\n */\nfunction pushToDataLayer(data: AnalyticsEvent) {\n if (typeof window === 'undefined') return;\n\n window.dataLayer = window.dataLayer || [];\n window.dataLayer.push(data);\n\n // Log in development\n if (process.env.NODE_ENV === 'development') {\n console.log('[Analytics]', data);\n }\n}\n\n/**\n * Generic event tracking function\n * Use this for custom events not covered by specific tracking functions\n *\n * @param eventName - Name of the event\n * @param properties - Additional event properties\n *\n * @example\n * ```typescript\n * trackEvent('button_click', {\n * button_text: 'Sign Up',\n * button_location: 'hero',\n * button_variant: 'primary'\n * });\n * ```\n */\nexport function trackEvent(eventName: string, properties?: Record<string, unknown>) {\n pushToDataLayer({\n event: eventName,\n ...properties,\n });\n}\n\n/**\n * Track CTA click events\n * @param ctaLocation - Where the CTA appears (e.g., 'hero', 'pricing', 'mobile_sticky')\n * @param ctaText - The text on the CTA button\n * @param ctaType - Type of CTA (e.g., 'signup', 'trial', 'contact')\n */\nexport function trackCTAClick(\n ctaLocation: string,\n ctaText: string,\n ctaType: 'signup' | 'trial' | 'contact' | 'download' | 'other' = 'other'\n) {\n pushToDataLayer({\n event: 'cta_click',\n event_category: 'cta',\n event_label: `${ctaLocation}_${ctaType}`,\n cta_location: ctaLocation,\n cta_text: ctaText,\n cta_type: ctaType,\n });\n}\n\n/**\n * Track form interactions\n * @param formName - Identifier for the form\n * @param action - Form action (start, submit, error, abandon)\n */\nexport function trackFormEvent(\n formName: string,\n action: 'start' | 'submit' | 'error' | 'abandon',\n metadata?: Record<string, unknown>\n) {\n pushToDataLayer({\n event: `form_${action}`,\n event_category: 'form',\n event_label: formName,\n form_name: formName,\n form_action: action,\n ...metadata,\n });\n}\n\n/**\n * Track page view (for SPAs and custom tracking)\n * @param pagePath - The page path\n * @param pageTitle - The page title\n */\nexport function trackPageView(pagePath: string, pageTitle: string) {\n pushToDataLayer({\n event: 'page_view',\n page_path: pagePath,\n page_title: pageTitle,\n });\n}\n\n/**\n * Track pricing page interactions\n * @param action - Type of interaction\n */\nexport function trackPricingEvent(\n action: 'view' | 'calculate' | 'plan_select' | 'currency_change',\n metadata?: Record<string, unknown>\n) {\n pushToDataLayer({\n event: 'pricing_interaction',\n event_category: 'engagement',\n event_label: action,\n pricing_action: action,\n ...metadata,\n });\n}\n\n/**\n * Track feature page engagement\n * @param featureName - Name of the feature being viewed\n * @param action - Type of engagement\n */\nexport function trackFeatureEngagement(\n featureName: string,\n action: 'view' | 'video_play' | 'video_complete' | 'cta_click',\n metadata?: Record<string, unknown>\n) {\n pushToDataLayer({\n event: 'feature_engagement',\n event_category: 'engagement',\n event_label: `${featureName}_${action}`,\n feature_name: featureName,\n feature_action: action,\n ...metadata,\n });\n}\n\n/**\n * Track resource downloads\n * @param resourceName - Name of the resource\n * @param resourceType - Type of resource (pdf, video, etc.)\n */\nexport function trackResourceDownload(\n resourceName: string,\n resourceType: string\n) {\n pushToDataLayer({\n event: 'resource_download',\n event_category: 'engagement',\n event_label: resourceName,\n resource_name: resourceName,\n resource_type: resourceType,\n });\n}\n\n/**\n * Track video interactions\n * @param videoTitle - Title of the video\n * @param action - Video action\n */\nexport function trackVideoEvent(\n videoTitle: string,\n action: 'play' | 'pause' | 'complete' | '25%' | '50%' | '75%',\n metadata?: Record<string, unknown>\n) {\n pushToDataLayer({\n event: 'video_interaction',\n event_category: 'engagement',\n event_label: `${videoTitle}_${action}`,\n video_title: videoTitle,\n video_action: action,\n ...metadata,\n });\n}\n\n/**\n * Track navigation events\n * @param linkText - Text of the link clicked\n * @param linkUrl - URL of the link\n * @param linkLocation - Where the link appears (header, footer, content)\n */\nexport function trackNavigation(\n linkText: string,\n linkUrl: string,\n linkLocation: 'header' | 'footer' | 'content' | 'mobile'\n) {\n pushToDataLayer({\n event: 'navigation_click',\n event_category: 'navigation',\n event_label: linkText,\n link_text: linkText,\n link_url: linkUrl,\n link_location: linkLocation,\n });\n}\n\n/**\n * Track A/B test variant assignment and events\n * @param testId - ID of the A/B test\n * @param variant - Variant assigned (A or B)\n * @param eventName - Name of the event\n */\nexport function trackABTestEvent(\n testId: string,\n variant: 'A' | 'B',\n eventName: string,\n metadata?: Record<string, unknown>\n) {\n pushToDataLayer({\n event: 'ab_test_event',\n event_category: 'ab_test',\n event_label: `${testId}_${variant}_${eventName}`,\n test_id: testId,\n variant: variant,\n test_event: eventName,\n ...metadata,\n });\n}\n\n/**\n * Track conversion events (trial signups, purchases, etc.)\n * @param conversionType - Type of conversion\n * @param value - Monetary value (optional)\n */\nexport function trackConversion(\n conversionType: 'trial_signup' | 'contact' | 'newsletter' | 'other',\n value?: number,\n metadata?: Record<string, unknown>\n) {\n pushToDataLayer({\n event: 'conversion',\n event_category: 'conversion',\n event_label: conversionType,\n conversion_type: conversionType,\n value: value,\n ...metadata,\n });\n}\n\n/**\n * Track scroll depth\n * @param percentage - Scroll depth percentage (25, 50, 75, 100)\n * @param pagePath - The page path\n */\nexport function trackScrollDepth(\n percentage: 25 | 50 | 75 | 100,\n pagePath: string\n) {\n pushToDataLayer({\n event: 'scroll_depth',\n event_category: 'engagement',\n event_label: `${percentage}%`,\n scroll_percentage: percentage,\n page_path: pagePath,\n });\n}\n\n/**\n * Track search events\n * @param searchTerm - The search term\n * @param resultCount - Number of results (optional)\n */\nexport function trackSearch(searchTerm: string, resultCount?: number) {\n pushToDataLayer({\n event: 'search',\n event_category: 'engagement',\n event_label: searchTerm,\n search_term: searchTerm,\n result_count: resultCount,\n });\n}\n\n/**\n * Track outbound link clicks\n * @param url - The external URL\n * @param linkText - Text of the link\n */\nexport function trackOutboundLink(url: string, linkText: string) {\n pushToDataLayer({\n event: 'outbound_link',\n event_category: 'navigation',\n event_label: url,\n outbound_url: url,\n link_text: linkText,\n });\n}\n\n/**\n * Track error events\n * @param errorType - Type of error\n * @param errorMessage - Error message or description\n */\nexport function trackError(errorType: string, errorMessage: string) {\n pushToDataLayer({\n event: 'error',\n event_category: 'engagement',\n event_label: errorType,\n error_type: errorType,\n error_message: errorMessage,\n });\n}\n","// ABOUTME: Utilities for optional react-hook-form and zod integration\n// ABOUTME: Provides basic HTML5 validation fallbacks when libraries not installed\n\nimport * as React from 'react'\n\n/**\n * Check if react-hook-form is available\n */\nexport function hasReactHookForm(): boolean {\n try {\n require.resolve('react-hook-form')\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Check if zod is available\n */\nexport function hasZod(): boolean {\n try {\n require.resolve('zod')\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Get react-hook-form hooks or fallback\n */\nexport function getFormHooks() {\n if (hasReactHookForm()) {\n const rhf = require('react-hook-form')\n return {\n useForm: rhf.useForm,\n useFormContext: rhf.useFormContext,\n FormProvider: rhf.FormProvider,\n Controller: rhf.Controller,\n available: true\n }\n }\n\n // Fallback implementations using basic React state\n const useForm = (config?: any) => {\n const [values, setValues] = React.useState<Record<string, any>>({})\n const [errors, setErrors] = React.useState<Record<string, any>>({})\n\n const register = (name: string) => ({\n name,\n onChange: (e: any) => {\n const value = e.target.value\n setValues(prev => ({ ...prev, [name]: value }))\n },\n onBlur: () => {},\n ref: () => {}\n })\n\n const handleSubmit = (onValid: (data: any) => void) => (e: React.FormEvent) => {\n e.preventDefault()\n onValid(values)\n }\n\n const setValue = (name: string, value: any) => {\n setValues(prev => ({ ...prev, [name]: value }))\n }\n\n const watch = (name?: string) => {\n if (name) return values[name]\n return values\n }\n\n return {\n register,\n handleSubmit,\n formState: { errors, isSubmitting: false },\n control: {},\n setValue,\n watch,\n reset: () => setValues({})\n }\n }\n\n const FormProvider = ({ children, ...props }: any) => <>{children}</>\n const useFormContext = () => ({ register: () => ({}), formState: { errors: {} } })\n const Controller = ({ render, name }: any) => render({ field: { name, onChange: () => {}, value: '' } })\n\n return {\n useForm,\n useFormContext,\n FormProvider,\n Controller,\n available: false\n }\n}\n\n/**\n * Get zodResolver or fallback\n */\nexport function getZodResolver() {\n if (hasReactHookForm() && hasZod()) {\n try {\n const { zodResolver } = require('@hookform/resolvers/zod')\n return zodResolver\n } catch {\n return undefined\n }\n }\n return undefined\n}\n\n/**\n * Get zod or fallback\n * Returns null if not available\n */\nexport function getZod() {\n if (hasZod()) {\n return require('zod')\n }\n return null\n}\n","// ABOUTME: Common Zod validation schemas for forms\n// ABOUTME: Pre-built schemas for email, phone, URLs, passwords, etc.\n\nimport { getZod } from '../utils/forms'\n\nconst z = getZod()\n\n// Note: These schemas require zod to be installed\n// They will be undefined if zod is not available\n\n/**\n * Email validation schema\n * @example\n * const schema = z.object({ email: emailSchema })\n */\nexport const emailSchema = z?.string().email()\n\n/**\n * Phone number validation (international format)\n * Accepts formats: +1234567890, +1 (234) 567-8900, etc.\n */\nexport const phoneSchema = z?.string().regex(\n /^\\+?[1-9]\\d{1,14}$/,\n 'Invalid phone number format'\n)\n\n/**\n * US/Canada postal code validation\n * Accepts: 12345, 12345-6789, A1A 1A1, A1A1A1\n */\nexport const postalCodeSchema = z?.string().regex(\n /^(\\d{5}(-\\d{4})?|[A-Z]\\d[A-Z] ?\\d[A-Z]\\d)$/i,\n 'Invalid postal code'\n)\n\n/**\n * Password strength validation\n * Requires: min 8 chars, 1 uppercase, 1 lowercase, 1 number\n */\nexport const passwordSchema = z\n ?.string()\n .min(8, 'Password must be at least 8 characters')\n .regex(/[A-Z]/, 'Password must contain at least one uppercase letter')\n .regex(/[a-z]/, 'Password must contain at least one lowercase letter')\n .regex(/[0-9]/, 'Password must contain at least one number')\n\n/**\n * URL validation schema\n */\nexport const urlSchema = z?.string().url()\n\n/**\n * Credit card number validation (Luhn algorithm)\n */\nexport const creditCardSchema = z?.string().refine((value: string) => {\n const digits = value.replace(/\\D/g, '')\n if (digits.length < 13 || digits.length > 19) return false\n\n let sum = 0\n let isEven = false\n\n for (let i = digits.length - 1; i >= 0; i--) {\n let digit = parseInt(digits[i])\n\n if (isEven) {\n digit *= 2\n if (digit > 9) digit -= 9\n }\n\n sum += digit\n isEven = !isEven\n }\n\n return sum % 10 === 0\n}, 'Invalid credit card number')\n\n/**\n * File upload schema with type and size validation\n * @param acceptedTypes - MIME types (e.g., ['image/jpeg', 'image/png'])\n * @param maxSizeMB - Maximum file size in megabytes\n */\nexport function fileSchema(acceptedTypes: string[], maxSizeMB: number) {\n return z\n ?.instanceof(File)\n .refine(\n (file: File) => acceptedTypes.includes(file.type),\n `File must be one of: ${acceptedTypes.join(', ')}`\n )\n .refine(\n (file: File) => file.size <= maxSizeMB * 1024 * 1024,\n `File size must be less than ${maxSizeMB}MB`\n )\n}\n\n/**\n * Image file schema (JPEG, PNG, WebP)\n * @param maxSizeMB - Maximum file size in megabytes (default: 5)\n */\nexport function imageSchema(maxSizeMB = 5) {\n return fileSchema(['image/jpeg', 'image/png', 'image/webp'], maxSizeMB)\n}\n\n/**\n * Required string field (non-empty)\n */\nexport const requiredString = z?.string().min(1, 'This field is required')\n\n/**\n * Optional string field (empty string converted to undefined)\n */\nexport const optionalString = z?.string().optional().or(z?.literal(''))\n\n/**\n * Numeric string (e.g., for phone inputs)\n */\nexport const numericString = z?.string().regex(/^\\d+$/, 'Must contain only numbers')\n\n/**\n * Date string in ISO format\n */\nexport const dateString = z?.string().datetime()\n\n/**\n * Checkbox boolean (must be true)\n * Useful for \"I agree to terms\" checkboxes\n */\nexport const mustBeTrue = z?.literal(true, {\n errorMap: () => ({ message: 'You must accept to continue' })\n})\n","// ABOUTME: Bilingual error messages for form validation\n// ABOUTME: Provides French and English translations for common validation errors\n\nimport type { Locale } from '../i18n/config'\n\nexport const formErrorMessages = {\n required: {\n en: 'This field is required',\n fr: 'Ce champ est obligatoire'\n },\n email: {\n en: 'Please enter a valid email address',\n fr: 'Veuillez entrer une adresse e-mail valide'\n },\n phone: {\n en: 'Please enter a valid phone number',\n fr: 'Veuillez entrer un numéro de téléphone valide'\n },\n url: {\n en: 'Please enter a valid URL',\n fr: 'Veuillez entrer une URL valide'\n },\n password: {\n tooShort: {\n en: 'Password must be at least 8 characters',\n fr: 'Le mot de passe doit contenir au moins 8 caractères'\n },\n noUppercase: {\n en: 'Password must contain at least one uppercase letter',\n fr: 'Le mot de passe doit contenir au moins une lettre majuscule'\n },\n noLowercase: {\n en: 'Password must contain at least one lowercase letter',\n fr: 'Le mot de passe doit contenir au moins une lettre minuscule'\n },\n noNumber: {\n en: 'Password must contain at least one number',\n fr: 'Le mot de passe doit contenir au moins un chiffre'\n }\n },\n postalCode: {\n en: 'Invalid postal code',\n fr: 'Code postal invalide'\n },\n creditCard: {\n en: 'Invalid credit card number',\n fr: 'Numéro de carte de crédit invalide'\n },\n file: {\n tooLarge: {\n en: (maxSize: number) => `File size must be less than ${maxSize}MB`,\n fr: (maxSize: number) => `La taille du fichier doit être inférieure à ${maxSize}Mo`\n },\n invalidType: {\n en: (types: string[]) => `File must be one of: ${types.join(', ')}`,\n fr: (types: string[]) => `Le fichier doit être de type : ${types.join(', ')}`\n }\n },\n mustAccept: {\n en: 'You must accept to continue',\n fr: 'Vous devez accepter pour continuer'\n },\n minLength: {\n en: (min: number) => `Must be at least ${min} characters`,\n fr: (min: number) => `Doit contenir au moins ${min} caractères`\n },\n maxLength: {\n en: (max: number) => `Must be no more than ${max} characters`,\n fr: (max: number) => `Ne doit pas dépasser ${max} caractères`\n },\n min: {\n en: (min: number) => `Must be at least ${min}`,\n fr: (min: number) => `Doit être au moins ${min}`\n },\n max: {\n en: (max: number) => `Must be no more than ${max}`,\n fr: (max: number) => `Ne doit pas dépasser ${max}`\n },\n invalidFormat: {\n en: 'Invalid format',\n fr: 'Format invalide'\n }\n} as const\n\n/**\n * Get localized error message\n * @param key - Error message key\n * @param locale - Current locale\n * @param params - Optional parameters for dynamic messages\n */\nexport function getErrorMessage(\n key: keyof typeof formErrorMessages,\n locale: Locale = 'en',\n params?: any\n): string {\n const message = formErrorMessages[key] as any\n if (!message) return ''\n\n const localized = message[locale]\n if (typeof localized === 'function') {\n return localized(params)\n }\n\n return localized || ''\n}\n","// ABOUTME: Next.js metadata generation utilities for SEO optimization\n// ABOUTME: Type-safe helpers for creating Open Graph, Twitter Card, and other meta tags\n\nimport type { Metadata } from 'next'\n\nexport interface MetadataOptions {\n /** Page title */\n title: string\n /** Page description */\n description: string\n /** Canonical URL */\n url?: string\n /** Open Graph image */\n image?: string\n /** Image alt text */\n imageAlt?: string\n /** Page type @default 'website' */\n type?: 'website' | 'article'\n /** Article specific metadata */\n article?: {\n publishedTime?: string\n modifiedTime?: string\n author?: string\n tags?: string[]\n }\n /** Twitter card type @default 'summary_large_image' */\n twitterCard?: 'summary' | 'summary_large_image' | 'app' | 'player'\n /** Twitter handle (without @) */\n twitterSite?: string\n /** Twitter creator handle (without @) */\n twitterCreator?: string\n /** Locale @default 'en' */\n locale?: string\n /** Alternate locales */\n alternateLocales?: string[]\n /** Site name */\n siteName?: string\n /** Robots directives */\n robots?: {\n index?: boolean\n follow?: boolean\n googleBot?: {\n index?: boolean\n follow?: boolean\n }\n }\n /** Keywords */\n keywords?: string[]\n /** Author */\n author?: string\n /** Additional metadata */\n other?: Record<string, string>\n}\n\n/**\n * Generate Next.js metadata object for SEO\n *\n * Creates a complete Metadata object with Open Graph, Twitter Card,\n * and other SEO metadata. Compatible with Next.js 13+ App Router.\n *\n * @example\n * // In app/page.tsx\n * export const metadata = generateMetadata({\n * title: 'Home - My Company',\n * description: 'Leading provider of professional services',\n * url: 'https://example.com',\n * image: 'https://example.com/og-image.jpg',\n * siteName: 'My Company',\n * twitterSite: 'mycompany'\n * })\n *\n * @example\n * // Article page\n * export const metadata = generateMetadata({\n * title: 'Blog Post Title',\n * description: 'Article description...',\n * type: 'article',\n * article: {\n * publishedTime: '2024-01-15T00:00:00Z',\n * author: 'Jane Doe',\n * tags: ['JavaScript', 'React']\n * },\n * image: '/blog/post-image.jpg'\n * })\n *\n * @example\n * // With alternates for i18n\n * export const metadata = generateMetadata({\n * title: 'Welcome',\n * description: 'Description',\n * locale: 'en',\n * alternateLocales: ['fr', 'es'],\n * url: 'https://example.com'\n * })\n */\nexport function generateMetadata({\n title,\n description,\n url,\n image,\n imageAlt,\n type = 'website',\n article,\n twitterCard = 'summary_large_image',\n twitterSite,\n twitterCreator,\n locale = 'en',\n alternateLocales,\n siteName,\n robots,\n keywords,\n author,\n other\n}: MetadataOptions): Metadata {\n const metadata: Metadata = {\n title,\n description,\n ...(keywords && { keywords: keywords.join(', ') }),\n ...(author && { authors: [{ name: author }] }),\n\n // Open Graph\n openGraph: {\n title,\n description,\n type,\n ...(url && { url }),\n ...(siteName && { siteName }),\n ...(locale && { locale }),\n ...(image && {\n images: [\n {\n url: image,\n ...(imageAlt && { alt: imageAlt })\n }\n ]\n }),\n ...(type === 'article' &&\n article && {\n publishedTime: article.publishedTime,\n modifiedTime: article.modifiedTime,\n authors: article.author ? [article.author] : undefined,\n tags: article.tags\n })\n },\n\n // Twitter Card\n twitter: {\n card: twitterCard,\n title,\n description,\n ...(twitterSite && { site: `@${twitterSite}` }),\n ...(twitterCreator && { creator: `@${twitterCreator}` }),\n ...(image && {\n images: [image]\n })\n },\n\n // Robots\n ...(robots && { robots }),\n\n // Alternates for i18n\n ...(url &&\n alternateLocales && {\n alternates: {\n canonical: url,\n languages: Object.fromEntries(\n alternateLocales.map((loc) => [\n loc,\n url.replace(`/${locale}`, `/${loc}`)\n ])\n )\n }\n }),\n\n // Additional metadata\n ...(other && { other })\n }\n\n return metadata\n}\n\n/**\n * Generate metadata for a blog post or article\n *\n * Specialized helper for article pages with publication metadata.\n *\n * @example\n * export const metadata = generateArticleMetadata({\n * title: 'My Blog Post',\n * description: 'Post description',\n * image: '/blog/post.jpg',\n * publishedTime: '2024-01-15T00:00:00Z',\n * author: 'Jane Doe',\n * tags: ['JavaScript', 'Web Development']\n * })\n */\nexport function generateArticleMetadata({\n title,\n description,\n image,\n publishedTime,\n modifiedTime,\n author,\n tags,\n url,\n siteName,\n twitterSite\n}: {\n title: string\n description: string\n image?: string\n publishedTime?: string\n modifiedTime?: string\n author?: string\n tags?: string[]\n url?: string\n siteName?: string\n twitterSite?: string\n}): Metadata {\n return generateMetadata({\n title,\n description,\n image,\n url,\n siteName,\n twitterSite,\n type: 'article',\n article: {\n publishedTime,\n modifiedTime,\n author,\n tags\n }\n })\n}\n","// ABOUTME: JSON-LD structured data utilities for schema.org markup\n// ABOUTME: Generates type-safe structured data for rich search results\n\n/**\n * Base schema.org Thing type\n * @see https://schema.org/Thing\n */\nexport interface Thing {\n '@type': string;\n '@id'?: string;\n name?: string;\n description?: string;\n image?: string | string[];\n url?: string;\n [key: string]: unknown;\n}\n\n/**\n * Organization schema\n * @see https://schema.org/Organization\n */\nexport interface Organization extends Thing {\n '@type': 'Organization';\n name: string;\n url?: string;\n logo?: string;\n description?: string;\n email?: string;\n telephone?: string;\n address?: PostalAddress;\n sameAs?: string[]; // Social media profiles\n contactPoint?: ContactPoint[];\n foundingDate?: string;\n founders?: Person[];\n}\n\n/**\n * Postal address schema\n * @see https://schema.org/PostalAddress\n */\nexport interface PostalAddress extends Thing {\n '@type': 'PostalAddress';\n streetAddress?: string;\n addressLocality?: string; // City\n addressRegion?: string; // State/Province\n postalCode?: string;\n addressCountry?: string;\n}\n\n/**\n * Contact point schema\n * @see https://schema.org/ContactPoint\n */\nexport interface ContactPoint extends Thing {\n '@type': 'ContactPoint';\n telephone?: string;\n email?: string;\n contactType?: string; // e.g., 'customer service', 'sales'\n availableLanguage?: string[];\n areaServed?: string[]; // Countries/regions served\n}\n\n/**\n * Person schema\n * @see https://schema.org/Person\n */\nexport interface Person extends Thing {\n '@type': 'Person';\n name: string;\n email?: string;\n jobTitle?: string;\n image?: string;\n sameAs?: string[]; // Social profiles\n url?: string;\n}\n\n/**\n * WebSite schema with search action\n * @see https://schema.org/WebSite\n */\nexport interface WebSite extends Thing {\n '@type': 'WebSite';\n name: string;\n url: string;\n description?: string;\n publisher?: Organization;\n potentialAction?: SearchAction;\n}\n\n/**\n * Search action for site search\n * @see https://schema.org/SearchAction\n */\nexport interface SearchAction {\n '@type': 'SearchAction';\n target: {\n '@type': 'EntryPoint';\n urlTemplate: string; // e.g., \"https://example.com/search?q={search_term_string}\"\n };\n 'query-input': string; // e.g., \"required name=search_term_string\"\n}\n\n/**\n * Product schema\n * @see https://schema.org/Product\n */\nexport interface Product extends Thing {\n '@type': 'Product' | 'SoftwareApplication';\n name: string;\n description?: string;\n image?: string | string[];\n brand?: Organization | string;\n offers?: Offer | Offer[];\n aggregateRating?: AggregateRating;\n review?: Review[];\n}\n\n/**\n * Offer schema for products/services\n * @see https://schema.org/Offer\n */\nexport interface Offer extends Thing {\n '@type': 'Offer';\n price: string | number;\n priceCurrency: string; // ISO 4217 (e.g., 'USD', 'EUR')\n priceValidUntil?: string; // ISO 8601 date\n availability?: 'InStock' | 'OutOfStock' | 'PreOrder' | 'Discontinued';\n url?: string;\n seller?: Organization;\n}\n\n/**\n * Aggregate rating schema\n * @see https://schema.org/AggregateRating\n */\nexport interface AggregateRating extends Thing {\n '@type': 'AggregateRating';\n ratingValue: number | string;\n reviewCount?: number;\n bestRating?: number | string;\n worstRating?: number | string;\n}\n\n/**\n * Review schema\n * @see https://schema.org/Review\n */\nexport interface Review extends Thing {\n '@type': 'Review';\n author: Person | Organization | string;\n datePublished?: string;\n reviewBody?: string;\n reviewRating?: Rating;\n}\n\n/**\n * Rating schema\n * @see https://schema.org/Rating\n */\nexport interface Rating extends Thing {\n '@type': 'Rating';\n ratingValue: number | string;\n bestRating?: number | string;\n worstRating?: number | string;\n}\n\n/**\n * FAQ Page schema\n * @see https://schema.org/FAQPage\n */\nexport interface FAQPage extends Thing {\n '@type': 'FAQPage';\n mainEntity: Question[];\n}\n\n/**\n * Question schema for FAQ\n * @see https://schema.org/Question\n */\nexport interface Question extends Thing {\n '@type': 'Question';\n name: string; // Question text\n acceptedAnswer: Answer;\n}\n\n/**\n * Answer schema for FAQ\n * @see https://schema.org/Answer\n */\nexport interface Answer extends Thing {\n '@type': 'Answer';\n text: string; // Answer text\n}\n\n/**\n * Article schema\n * @see https://schema.org/Article\n */\nexport interface Article extends Thing {\n '@type': 'Article' | 'BlogPosting' | 'NewsArticle';\n headline: string;\n description?: string;\n image?: string | string[];\n author: Person | Organization | string;\n publisher: Organization;\n datePublished: string; // ISO 8601\n dateModified?: string; // ISO 8601\n mainEntityOfPage?: string; // Canonical URL\n}\n\n/**\n * Breadcrumb list schema\n * @see https://schema.org/BreadcrumbList\n */\nexport interface BreadcrumbList extends Thing {\n '@type': 'BreadcrumbList';\n itemListElement: ListItem[];\n}\n\n/**\n * List item for breadcrumbs\n * @see https://schema.org/ListItem\n */\nexport interface ListItem extends Thing {\n '@type': 'ListItem';\n position: number;\n name: string;\n item?: string; // URL\n}\n\n/**\n * Create Organization structured data\n *\n * @example\n * ```tsx\n * const org = createOrganization({\n * name: 'Acme Inc',\n * url: 'https://acme.com',\n * logo: 'https://acme.com/logo.png',\n * sameAs: [\n * 'https://twitter.com/acme',\n * 'https://linkedin.com/company/acme'\n * ]\n * });\n * ```\n */\nexport function createOrganization(data: Omit<Organization, '@type'>): Organization {\n return {\n '@type': 'Organization',\n ...data,\n } as Organization;\n}\n\n/**\n * Create WebSite structured data with search action\n *\n * @example\n * ```tsx\n * const website = createWebSite({\n * name: 'Acme',\n * url: 'https://acme.com',\n * searchUrlTemplate: 'https://acme.com/search?q={search_term_string}'\n * });\n * ```\n */\nexport function createWebSite(data: {\n name: string;\n url: string;\n description?: string;\n publisher?: Organization;\n searchUrlTemplate?: string;\n}): WebSite {\n const website: WebSite = {\n '@type': 'WebSite',\n name: data.name,\n url: data.url,\n description: data.description,\n publisher: data.publisher,\n };\n\n if (data.searchUrlTemplate) {\n website.potentialAction = {\n '@type': 'SearchAction',\n target: {\n '@type': 'EntryPoint',\n urlTemplate: data.searchUrlTemplate,\n },\n 'query-input': 'required name=search_term_string',\n };\n }\n\n return website;\n}\n\n/**\n * Create Product structured data\n *\n * @example\n * ```tsx\n * const product = createProduct({\n * name: 'Premium Email Plan',\n * description: 'Unlimited emails, advanced features',\n * image: 'https://acme.com/premium.jpg',\n * offers: {\n * price: '99.00',\n * priceCurrency: 'USD',\n * availability: 'InStock'\n * }\n * });\n * ```\n */\nexport function createProduct(data: Omit<Product, '@type'>): Product {\n return {\n '@type': 'Product',\n ...data,\n } as Product;\n}\n\n/**\n * Create FAQ Page structured data\n *\n * @example\n * ```tsx\n * const faq = createFAQPage([\n * {\n * question: 'What is your refund policy?',\n * answer: 'We offer a 30-day money-back guarantee.'\n * },\n * {\n * question: 'Do you offer support?',\n * answer: 'Yes, 24/7 email and chat support is included.'\n * }\n * ]);\n * ```\n */\nexport function createFAQPage(\n faqs: Array<{ question: string; answer: string }>\n): FAQPage {\n return {\n '@type': 'FAQPage',\n mainEntity: faqs.map(({ question, answer }) => ({\n '@type': 'Question',\n name: question,\n acceptedAnswer: {\n '@type': 'Answer',\n text: answer,\n },\n })),\n };\n}\n\n/**\n * Create Article structured data\n *\n * @example\n * ```tsx\n * const article = createArticle({\n * headline: '10 Email Marketing Tips',\n * description: 'Learn how to improve your email campaigns',\n * author: { name: 'Jane Doe' },\n * publisher: {\n * name: 'Acme',\n * logo: 'https://acme.com/logo.png'\n * },\n * datePublished: '2024-01-15T10:00:00Z',\n * image: 'https://acme.com/blog/tips.jpg'\n * });\n * ```\n */\nexport function createArticle(data: {\n headline: string;\n description?: string;\n image?: string | string[];\n author: Person | string;\n publisher: Organization;\n datePublished: string;\n dateModified?: string;\n mainEntityOfPage?: string;\n type?: 'Article' | 'BlogPosting' | 'NewsArticle';\n}): Article {\n return {\n '@type': data.type || 'Article',\n headline: data.headline,\n description: data.description,\n image: data.image,\n author: typeof data.author === 'string' ? data.author : data.author,\n publisher: data.publisher,\n datePublished: data.datePublished,\n dateModified: data.dateModified,\n mainEntityOfPage: data.mainEntityOfPage,\n };\n}\n\n/**\n * Create Breadcrumb List structured data\n *\n * @example\n * ```tsx\n * const breadcrumbs = createBreadcrumbList([\n * { name: 'Home', url: 'https://acme.com' },\n * { name: 'Blog', url: 'https://acme.com/blog' },\n * { name: 'Email Tips', url: 'https://acme.com/blog/email-tips' }\n * ]);\n * ```\n */\nexport function createBreadcrumbList(\n items: Array<{ name: string; url?: string }>\n): BreadcrumbList {\n return {\n '@type': 'BreadcrumbList',\n itemListElement: items.map((item, index) => ({\n '@type': 'ListItem',\n position: index + 1,\n name: item.name,\n item: item.url,\n })),\n };\n}\n\n/**\n * Serialize structured data to JSON-LD string\n * Safe for use in script tags\n */\nexport function serializeStructuredData(data: Thing | Thing[]): string {\n const jsonLd = Array.isArray(data)\n ? { '@context': 'https://schema.org', '@graph': data }\n : { '@context': 'https://schema.org', ...data };\n\n return JSON.stringify(jsonLd);\n}\n","// ABOUTME: Sitemap generation utilities for SEO\n// ABOUTME: Creates XML sitemaps with multi-language support and hreflang\n\n/**\n * Change frequency for sitemap entries\n * @see https://www.sitemaps.org/protocol.html\n */\nexport type ChangeFrequency =\n | 'always'\n | 'hourly'\n | 'daily'\n | 'weekly'\n | 'monthly'\n | 'yearly'\n | 'never';\n\n/**\n * A single URL entry in the sitemap\n */\nexport interface SitemapEntry {\n /** Absolute URL of the page */\n url: string;\n /** Last modification date (ISO 8601 format) */\n lastModified?: string | Date;\n /** How frequently the page is likely to change */\n changeFrequency?: ChangeFrequency;\n /** Priority of this URL relative to other URLs (0.0 to 1.0) */\n priority?: number;\n /** Alternate language versions of this URL */\n alternates?: Array<{\n /** Language/locale code (e.g., 'en', 'fr', 'en-US') */\n hreflang: string;\n /** Absolute URL for this language version */\n href: string;\n }>;\n}\n\n/**\n * Configuration for sitemap generation\n */\nexport interface SitemapConfig {\n /** Base URL of the website (e.g., 'https://example.com') */\n baseUrl: string;\n /** Array of sitemap entries */\n entries: SitemapEntry[];\n /** Pretty print the XML output (default: false) */\n prettyPrint?: boolean;\n}\n\n/**\n * Generate XML sitemap from entries\n *\n * Creates a valid sitemap.xml following the sitemaps.org protocol with\n * support for multi-language pages using hreflang annotations.\n *\n * @param config - Sitemap configuration\n * @returns XML string for sitemap.xml\n *\n * @example Basic sitemap\n * ```tsx\n * const sitemap = generateSitemap({\n * baseUrl: 'https://example.com',\n * entries: [\n * { url: 'https://example.com', priority: 1.0, changeFrequency: 'weekly' },\n * { url: 'https://example.com/about', priority: 0.8 },\n * { url: 'https://example.com/products', priority: 0.9, changeFrequency: 'daily' }\n * ]\n * });\n * ```\n *\n * @example Multi-language sitemap\n * ```tsx\n * const sitemap = generateSitemap({\n * baseUrl: 'https://example.com',\n * entries: [\n * {\n * url: 'https://example.com/en/about',\n * alternates: [\n * { hreflang: 'en', href: 'https://example.com/en/about' },\n * { hreflang: 'fr', href: 'https://example.com/fr/about' },\n * { hreflang: 'x-default', href: 'https://example.com/en/about' }\n * ]\n * }\n * ]\n * });\n * ```\n */\nexport function generateSitemap(config: SitemapConfig): string {\n const { entries, prettyPrint = false } = config;\n const indent = prettyPrint ? ' ' : '';\n const newline = prettyPrint ? '\\n' : '';\n\n const urlEntries = entries\n .map((entry) => {\n const parts: string[] = [];\n\n parts.push(`${indent}<url>${newline}`);\n parts.push(`${indent}${indent}<loc>${escapeXml(entry.url)}</loc>${newline}`);\n\n if (entry.lastModified) {\n const date =\n entry.lastModified instanceof Date\n ? entry.lastModified.toISOString()\n : entry.lastModified;\n parts.push(`${indent}${indent}<lastmod>${date}</lastmod>${newline}`);\n }\n\n if (entry.changeFrequency) {\n parts.push(`${indent}${indent}<changefreq>${entry.changeFrequency}</changefreq>${newline}`);\n }\n\n if (entry.priority !== undefined) {\n const priority = Math.max(0, Math.min(1, entry.priority)).toFixed(1);\n parts.push(`${indent}${indent}<priority>${priority}</priority>${newline}`);\n }\n\n // Add hreflang alternates\n if (entry.alternates && entry.alternates.length > 0) {\n entry.alternates.forEach((alternate) => {\n parts.push(\n `${indent}${indent}<xhtml:link rel=\"alternate\" hreflang=\"${escapeXml(\n alternate.hreflang\n )}\" href=\"${escapeXml(alternate.href)}\" />${newline}`\n );\n });\n }\n\n parts.push(`${indent}</url>${newline}`);\n\n return parts.join('');\n })\n .join('');\n\n const xml = [\n '<?xml version=\"1.0\" encoding=\"UTF-8\"?>',\n newline,\n '<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\" xmlns:xhtml=\"http://www.w3.org/1999/xhtml\">',\n newline,\n urlEntries,\n '</urlset>',\n ].join('');\n\n return xml;\n}\n\n/**\n * Create sitemap entries for multi-language pages\n *\n * Helper to generate sitemap entries with proper hreflang annotations\n * for pages that exist in multiple languages.\n *\n * @param baseUrl - Base URL of the website\n * @param path - Page path without locale prefix (e.g., '/about')\n * @param locales - Array of locale codes (e.g., ['en', 'fr', 'es'])\n * @param defaultLocale - Default locale for x-default (optional)\n * @param options - Additional sitemap entry options\n * @returns Array of sitemap entries, one per locale\n *\n * @example\n * ```tsx\n * const entries = createMultiLanguageEntries(\n * 'https://example.com',\n * '/about',\n * ['en', 'fr', 'es'],\n * 'en',\n * { priority: 0.8, changeFrequency: 'monthly' }\n * );\n * // Creates entries for:\n * // - /en/about (with alternates to fr, es, x-default)\n * // - /fr/about (with alternates to en, es, x-default)\n * // - /es/about (with alternates to en, fr, x-default)\n * ```\n */\nexport function createMultiLanguageEntries(\n baseUrl: string,\n path: string,\n locales: string[],\n defaultLocale?: string,\n options?: Partial<Omit<SitemapEntry, 'url' | 'alternates'>>\n): SitemapEntry[] {\n // Clean up path\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n\n return locales.map((locale) => {\n // Build all alternates\n const alternates = locales.map((altLocale) => ({\n hreflang: altLocale,\n href: `${baseUrl}/${altLocale}${cleanPath}`,\n }));\n\n // Add x-default if specified\n if (defaultLocale) {\n alternates.push({\n hreflang: 'x-default',\n href: `${baseUrl}/${defaultLocale}${cleanPath}`,\n });\n }\n\n return {\n url: `${baseUrl}/${locale}${cleanPath}`,\n alternates,\n ...options,\n };\n });\n}\n\n/**\n * Create a single-language sitemap entry\n *\n * @param baseUrl - Base URL of the website\n * @param path - Page path (e.g., '/about')\n * @param options - Sitemap entry options\n * @returns Sitemap entry\n *\n * @example\n * ```tsx\n * const entry = createSitemapEntry(\n * 'https://example.com',\n * '/about',\n * { priority: 0.8, changeFrequency: 'monthly' }\n * );\n * ```\n */\nexport function createSitemapEntry(\n baseUrl: string,\n path: string,\n options?: Partial<Omit<SitemapEntry, 'url'>>\n): SitemapEntry {\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n const url = path === '/' ? baseUrl : `${baseUrl}${cleanPath}`;\n\n return {\n url,\n ...options,\n };\n}\n\n/**\n * Escape special XML characters\n */\nfunction escapeXml(unsafe: string): string {\n return unsafe\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\n/**\n * Validate sitemap entry\n *\n * Checks for common issues in sitemap entries.\n *\n * @param entry - Sitemap entry to validate\n * @returns Array of error messages (empty if valid)\n */\nexport function validateSitemapEntry(entry: SitemapEntry): string[] {\n const errors: string[] = [];\n\n // Check URL is absolute\n if (!entry.url.startsWith('http://') && !entry.url.startsWith('https://')) {\n errors.push(`URL must be absolute: ${entry.url}`);\n }\n\n // Check priority range\n if (entry.priority !== undefined && (entry.priority < 0 || entry.priority > 1)) {\n errors.push(`Priority must be between 0 and 1: ${entry.priority}`);\n }\n\n // Check alternates are absolute\n if (entry.alternates) {\n entry.alternates.forEach((alt, index) => {\n if (!alt.href.startsWith('http://') && !alt.href.startsWith('https://')) {\n errors.push(`Alternate ${index} href must be absolute: ${alt.href}`);\n }\n });\n }\n\n return errors;\n}\n\n/**\n * Validate entire sitemap configuration\n *\n * @param config - Sitemap configuration to validate\n * @returns Object with isValid flag and array of errors\n */\nexport function validateSitemap(config: SitemapConfig): {\n isValid: boolean;\n errors: Array<{ entry?: SitemapEntry; messages: string[] }>;\n} {\n const errors: Array<{ entry?: SitemapEntry; messages: string[] }> = [];\n\n // Check base URL\n if (!config.baseUrl.startsWith('http://') && !config.baseUrl.startsWith('https://')) {\n errors.push({ messages: ['Base URL must be absolute'] });\n }\n\n // Check each entry\n config.entries.forEach((entry) => {\n const entryErrors = validateSitemapEntry(entry);\n if (entryErrors.length > 0) {\n errors.push({ entry, messages: entryErrors });\n }\n });\n\n return {\n isValid: errors.length === 0,\n errors,\n };\n}\n","// ABOUTME: Reusable link component with analytics tracking\n// ABOUTME: Automatically tracks clicks based on link type (internal, external, CTA)\n'use client';\n\nimport { trackCTAClick, trackOutboundLink, trackNavigation } from '../lib/analytics';\n\ninterface TrackedLinkProps {\n href: string;\n children: React.ReactNode;\n className?: string;\n trackingType?: 'cta' | 'navigation' | 'outbound' | 'auto';\n ctaLocation?: string;\n ctaType?: 'signup' | 'trial' | 'contact' | 'download' | 'other';\n navLocation?: 'header' | 'footer' | 'content' | 'mobile';\n onClick?: () => void;\n [key: string]: unknown;\n}\n\n/**\n * Link component with built-in analytics tracking\n * Automatically determines tracking type if set to 'auto'\n */\nexport function TrackedLink({\n href,\n children,\n className,\n trackingType = 'auto',\n ctaLocation,\n ctaType = 'other',\n navLocation = 'content',\n onClick,\n ...props\n}: TrackedLinkProps) {\n const handleClick = () => {\n const linkText = typeof children === 'string' ? children : href;\n\n // Auto-detect tracking type\n let type = trackingType;\n if (type === 'auto') {\n if (href.startsWith('http') && !href.includes('courrielleur.com')) {\n type = 'outbound';\n } else if (href.includes('signup') || href.includes('trial')) {\n type = 'cta';\n } else {\n type = 'navigation';\n }\n }\n\n // Track based on type\n switch (type) {\n case 'cta':\n trackCTAClick(ctaLocation || 'unknown', linkText, ctaType);\n break;\n case 'outbound':\n trackOutboundLink(href, linkText);\n break;\n case 'navigation':\n trackNavigation(linkText, href, navLocation);\n break;\n }\n\n // Call custom onClick if provided\n onClick?.();\n };\n\n return (\n <a href={href} className={className} onClick={handleClick} {...props}>\n {children}\n </a>\n );\n}\n","// ABOUTME: Reusable features grid component for showcasing product capabilities\n// ABOUTME: Displays features in categories with icons, descriptions, and benefits\n\n'use client';\n\nimport { ReactNode } from 'react';\n\nexport interface Feature {\n id: string;\n icon?: ReactNode;\n name: string;\n description: string;\n benefits?: string[];\n useCases?: string[];\n learnMoreHref?: string;\n}\n\nexport interface FeatureCategory {\n id: string;\n name: string;\n description: string;\n features: Feature[];\n}\n\ninterface FeaturesGridProps {\n categories: FeatureCategory[];\n className?: string;\n locale?: 'fr' | 'en';\n}\n\nconst labels = {\n keyBenefits: {\n fr: 'Avantages clés :',\n en: 'Key Benefits:',\n },\n useCases: {\n fr: 'Cas d\\'usage :',\n en: 'Use Cases:',\n },\n learnMore: {\n fr: 'En savoir plus',\n en: 'Learn more',\n },\n};\n\nexport function FeaturesGrid({ categories, className = '', locale = 'en' }: FeaturesGridProps) {\n return (\n <div className={`py-20 ${className}`}>\n {categories.map((category, categoryIndex) => (\n <section\n key={category.id}\n className={`${categoryIndex % 2 === 0 ? 'bg-white' : 'bg-warm-gray'} py-16`}\n >\n <div className=\"container mx-auto px-6\">\n <div className=\"max-w-3xl mb-12\">\n <h2 className=\"text-3xl md:text-4xl font-bold text-charcoal mb-4\">\n {category.name}\n </h2>\n <p className=\"text-lg text-charcoal/80\">{category.description}</p>\n </div>\n\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8\">\n {category.features.map((feature) => (\n <div\n key={feature.id}\n className=\"bg-white rounded-lg p-6 shadow-sm hover:shadow-md transition-shadow duration-200\"\n >\n {feature.icon && (\n <div className=\"w-12 h-12 mb-4 text-primary\">{feature.icon}</div>\n )}\n <h3 className=\"text-xl font-semibold text-charcoal mb-3\">\n {feature.name}\n </h3>\n <p className=\"text-charcoal/80 mb-4\">{feature.description}</p>\n\n {feature.benefits && feature.benefits.length > 0 && (\n <div className=\"mb-4\">\n <p className=\"text-sm font-semibold text-charcoal/60 mb-2\">\n {labels.keyBenefits[locale]}\n </p>\n <ul className=\"space-y-1\">\n {feature.benefits.map((benefit, idx) => (\n <li\n key={idx}\n className=\"text-sm text-charcoal/70 flex items-start gap-2\"\n >\n <span className=\"text-primary mt-1\">✓</span>\n <span>{benefit}</span>\n </li>\n ))}\n </ul>\n </div>\n )}\n\n {feature.useCases && feature.useCases.length > 0 && (\n <div className=\"mb-4\">\n <p className=\"text-sm font-semibold text-charcoal/60 mb-2\">\n {labels.useCases[locale]}\n </p>\n <ul className=\"space-y-1\">\n {feature.useCases.map((useCase, idx) => (\n <li\n key={idx}\n className=\"text-sm text-charcoal/70 flex items-start gap-2\"\n >\n <span className=\"text-charcoal/40\">•</span>\n <span>{useCase}</span>\n </li>\n ))}\n </ul>\n </div>\n )}\n\n {feature.learnMoreHref && (\n <a\n href={feature.learnMoreHref}\n className=\"text-primary hover:text-primary-hover text-sm font-medium inline-flex items-center gap-1 group\"\n >\n {labels.learnMore[locale]}\n <span className=\"group-hover:translate-x-1 transition-transform duration-200\">\n →\n </span>\n </a>\n )}\n </div>\n ))}\n </div>\n </div>\n </section>\n ))}\n </div>\n );\n}\n"],"mappings":"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","names":["labels","locales","path","getI18nConfig","connector","setLocaleCookie","getLocaleNames","getLocaleLabel","path","locales","defaultLocale","jsx","jsx"]}
|