@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
package/dist/index.js
ADDED
|
@@ -0,0 +1,1713 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
11
|
+
var __export = (target, all) => {
|
|
12
|
+
for (var name in all)
|
|
13
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
14
|
+
};
|
|
15
|
+
var __copyProps = (to, from, except, desc) => {
|
|
16
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
17
|
+
for (let key of __getOwnPropNames(from))
|
|
18
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
19
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
20
|
+
}
|
|
21
|
+
return to;
|
|
22
|
+
};
|
|
23
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
24
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
25
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
26
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
27
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
28
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
29
|
+
mod
|
|
30
|
+
));
|
|
31
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
|
+
|
|
33
|
+
// src/lib/i18n/config.ts
|
|
34
|
+
var config_exports = {};
|
|
35
|
+
__export(config_exports, {
|
|
36
|
+
__resetI18nConfig__: () => __resetI18nConfig__,
|
|
37
|
+
defaultLocale: () => defaultLocale,
|
|
38
|
+
getDefaultLocale: () => getDefaultLocale,
|
|
39
|
+
getI18nConfig: () => getI18nConfig,
|
|
40
|
+
getLocaleCookieConfig: () => getLocaleCookieConfig,
|
|
41
|
+
getLocaleLabel: () => getLocaleLabel,
|
|
42
|
+
getLocaleLabels: () => getLocaleLabels,
|
|
43
|
+
getLocaleName: () => getLocaleName,
|
|
44
|
+
getLocaleNames: () => getLocaleNames,
|
|
45
|
+
getLocalePrefix: () => getLocalePrefix,
|
|
46
|
+
getLocales: () => getLocales,
|
|
47
|
+
getRtlLocales: () => getRtlLocales,
|
|
48
|
+
isI18nConfigInitialized: () => isI18nConfigInitialized,
|
|
49
|
+
isLocaleDetectionEnabled: () => isLocaleDetectionEnabled,
|
|
50
|
+
isSupportedLocale: () => isSupportedLocale,
|
|
51
|
+
localeLabels: () => localeLabels,
|
|
52
|
+
localeNames: () => localeNames,
|
|
53
|
+
locales: () => locales,
|
|
54
|
+
setI18nConfig: () => setI18nConfig
|
|
55
|
+
});
|
|
56
|
+
function setI18nConfig(config) {
|
|
57
|
+
if (!config.locales || config.locales.length === 0) {
|
|
58
|
+
throw new Error("i18n config must define at least one locale");
|
|
59
|
+
}
|
|
60
|
+
if (!config.defaultLocale) {
|
|
61
|
+
throw new Error("i18n config must define a default locale");
|
|
62
|
+
}
|
|
63
|
+
if (!config.locales.includes(config.defaultLocale)) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
`Default locale "${config.defaultLocale}" must be included in locales array: [${config.locales.join(", ")}]`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
const configWithDefaults = {
|
|
69
|
+
...config,
|
|
70
|
+
localePrefix: config.localePrefix || "as-needed",
|
|
71
|
+
localeDetection: config.localeDetection !== false,
|
|
72
|
+
// Default to true
|
|
73
|
+
localeNames: config.localeNames || {},
|
|
74
|
+
localeLabels: config.localeLabels || {},
|
|
75
|
+
rtlLocales: config.rtlLocales || [],
|
|
76
|
+
localeCookie: {
|
|
77
|
+
name: config.localeCookie?.name || "NEXT_LOCALE",
|
|
78
|
+
maxAge: config.localeCookie?.maxAge || 365 * 24 * 60 * 60,
|
|
79
|
+
// 1 year
|
|
80
|
+
sameSite: config.localeCookie?.sameSite || "lax"
|
|
81
|
+
},
|
|
82
|
+
slugTranslations: config.slugTranslations || {}
|
|
83
|
+
};
|
|
84
|
+
globalI18nConfig = configWithDefaults;
|
|
85
|
+
}
|
|
86
|
+
function getI18nConfig() {
|
|
87
|
+
if (!globalI18nConfig) {
|
|
88
|
+
if (process.env.NODE_ENV !== "production") {
|
|
89
|
+
console.warn(
|
|
90
|
+
"\u26A0\uFE0F i18n configuration not initialized. Using legacy defaults.\nCall setI18nConfig() in your layout before using i18n features.\nSee docs/i18n/MIGRATION.md for setup instructions."
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
locales: ["fr", "en"],
|
|
95
|
+
defaultLocale: "fr",
|
|
96
|
+
localePrefix: "as-needed",
|
|
97
|
+
localeDetection: true,
|
|
98
|
+
localeNames: { fr: "Fran\xE7ais", en: "English" },
|
|
99
|
+
localeLabels: { fr: "FR", en: "EN" },
|
|
100
|
+
rtlLocales: [],
|
|
101
|
+
localeCookie: {
|
|
102
|
+
name: "NEXT_LOCALE",
|
|
103
|
+
maxAge: 365 * 24 * 60 * 60,
|
|
104
|
+
sameSite: "lax"
|
|
105
|
+
},
|
|
106
|
+
slugTranslations: {}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
return globalI18nConfig;
|
|
110
|
+
}
|
|
111
|
+
function isI18nConfigInitialized() {
|
|
112
|
+
return globalI18nConfig !== null;
|
|
113
|
+
}
|
|
114
|
+
function __resetI18nConfig__() {
|
|
115
|
+
globalI18nConfig = null;
|
|
116
|
+
}
|
|
117
|
+
function getLocales() {
|
|
118
|
+
return getI18nConfig().locales;
|
|
119
|
+
}
|
|
120
|
+
function getDefaultLocale() {
|
|
121
|
+
return getI18nConfig().defaultLocale;
|
|
122
|
+
}
|
|
123
|
+
function getLocalePrefix() {
|
|
124
|
+
return getI18nConfig().localePrefix || "as-needed";
|
|
125
|
+
}
|
|
126
|
+
function getLocaleNames() {
|
|
127
|
+
return getI18nConfig().localeNames || {};
|
|
128
|
+
}
|
|
129
|
+
function getLocaleLabels() {
|
|
130
|
+
return getI18nConfig().localeLabels || {};
|
|
131
|
+
}
|
|
132
|
+
function getRtlLocales() {
|
|
133
|
+
return getI18nConfig().rtlLocales || [];
|
|
134
|
+
}
|
|
135
|
+
function isLocaleDetectionEnabled() {
|
|
136
|
+
return getI18nConfig().localeDetection !== false;
|
|
137
|
+
}
|
|
138
|
+
function getLocaleCookieConfig() {
|
|
139
|
+
const config = getI18nConfig();
|
|
140
|
+
return {
|
|
141
|
+
name: config.localeCookie?.name || "NEXT_LOCALE",
|
|
142
|
+
maxAge: config.localeCookie?.maxAge || 365 * 24 * 60 * 60,
|
|
143
|
+
sameSite: config.localeCookie?.sameSite || "lax"
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function isSupportedLocale(locale) {
|
|
147
|
+
return getLocales().includes(locale);
|
|
148
|
+
}
|
|
149
|
+
function getLocaleName(locale) {
|
|
150
|
+
const names = getLocaleNames();
|
|
151
|
+
return names[locale] || locale;
|
|
152
|
+
}
|
|
153
|
+
function getLocaleLabel(locale) {
|
|
154
|
+
const labels2 = getLocaleLabels();
|
|
155
|
+
return labels2[locale] || locale.toUpperCase();
|
|
156
|
+
}
|
|
157
|
+
var globalI18nConfig, locales, defaultLocale, localeNames, localeLabels;
|
|
158
|
+
var init_config = __esm({
|
|
159
|
+
"src/lib/i18n/config.ts"() {
|
|
160
|
+
"use strict";
|
|
161
|
+
globalI18nConfig = null;
|
|
162
|
+
locales = ["fr", "en"];
|
|
163
|
+
defaultLocale = "fr";
|
|
164
|
+
localeNames = {
|
|
165
|
+
fr: "Fran\xE7ais",
|
|
166
|
+
en: "English"
|
|
167
|
+
};
|
|
168
|
+
localeLabels = {
|
|
169
|
+
fr: "FR",
|
|
170
|
+
en: "EN"
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// src/index.ts
|
|
176
|
+
var src_exports = {};
|
|
177
|
+
__export(src_exports, {
|
|
178
|
+
FeaturesGrid: () => FeaturesGrid,
|
|
179
|
+
LOCALE_COOKIE_NAME: () => LOCALE_COOKIE_NAME,
|
|
180
|
+
TrackedLink: () => TrackedLink,
|
|
181
|
+
cn: () => cn,
|
|
182
|
+
createArticle: () => createArticle,
|
|
183
|
+
createBreadcrumbList: () => createBreadcrumbList,
|
|
184
|
+
createFAQPage: () => createFAQPage,
|
|
185
|
+
createI18nMiddleware: () => createI18nMiddleware,
|
|
186
|
+
createMultiLanguageEntries: () => createMultiLanguageEntries,
|
|
187
|
+
createOrganization: () => createOrganization,
|
|
188
|
+
createProduct: () => createProduct,
|
|
189
|
+
createSitemapEntry: () => createSitemapEntry,
|
|
190
|
+
createWebSite: () => createWebSite,
|
|
191
|
+
creditCardSchema: () => creditCardSchema,
|
|
192
|
+
dateString: () => dateString,
|
|
193
|
+
defaultLocale: () => defaultLocale,
|
|
194
|
+
defaultSlugTranslations: () => defaultSlugTranslations,
|
|
195
|
+
emailSchema: () => emailSchema,
|
|
196
|
+
fileSchema: () => fileSchema,
|
|
197
|
+
formErrorMessages: () => formErrorMessages,
|
|
198
|
+
formatCurrency: () => formatCurrency,
|
|
199
|
+
formatDate: () => formatDate,
|
|
200
|
+
formatDateRange: () => formatDateRange,
|
|
201
|
+
formatFileSize: () => formatFileSize,
|
|
202
|
+
formatList: () => formatList,
|
|
203
|
+
formatLocaleDisplay: () => formatLocaleDisplay,
|
|
204
|
+
formatNumber: () => formatNumber,
|
|
205
|
+
formatRelativeTime: () => formatRelativeTime,
|
|
206
|
+
generateArticleMetadata: () => generateArticleMetadata,
|
|
207
|
+
generateDesignTokens: () => generateDesignTokens,
|
|
208
|
+
generateMetadata: () => generateMetadata,
|
|
209
|
+
generateSitemap: () => generateSitemap,
|
|
210
|
+
generateThemeCSS: () => generateThemeCSS,
|
|
211
|
+
getAllPolicies: () => getAllPolicies,
|
|
212
|
+
getAlternateLocales: () => getAlternateLocales,
|
|
213
|
+
getDefaultLocale: () => getDefaultLocale,
|
|
214
|
+
getErrorMessage: () => getErrorMessage,
|
|
215
|
+
getFontConfig: () => getFontConfig,
|
|
216
|
+
getFontVariables: () => getFontVariables,
|
|
217
|
+
getI18nConfig: () => getI18nConfig,
|
|
218
|
+
getLocaleAutonym: () => getLocaleAutonym,
|
|
219
|
+
getLocaleCookieConfig: () => getLocaleCookieConfig,
|
|
220
|
+
getLocaleFromCookie: () => getLocaleFromCookie,
|
|
221
|
+
getLocaleLabel: () => getLocaleLabel,
|
|
222
|
+
getLocaleLabels: () => getLocaleLabels,
|
|
223
|
+
getLocaleName: () => getLocaleName,
|
|
224
|
+
getLocaleNames: () => getLocaleNames,
|
|
225
|
+
getLocalePrefix: () => getLocalePrefix,
|
|
226
|
+
getLocales: () => getLocales,
|
|
227
|
+
getLocalizedString: () => getLocalizedString,
|
|
228
|
+
getNavigationString: () => getNavigationString,
|
|
229
|
+
getPolicyLocales: () => getPolicyLocales,
|
|
230
|
+
getPolicySlugs: () => getPolicySlugs,
|
|
231
|
+
getRelativeTime: () => getRelativeTime,
|
|
232
|
+
getRtlLocales: () => getRtlLocales,
|
|
233
|
+
getTextDirection: () => getTextDirection,
|
|
234
|
+
imageSchema: () => imageSchema,
|
|
235
|
+
isI18nConfigInitialized: () => isI18nConfigInitialized,
|
|
236
|
+
isLocaleDetectionEnabled: () => isLocaleDetectionEnabled,
|
|
237
|
+
isRtlLocale: () => isRtlLocale,
|
|
238
|
+
isSupportedLocale: () => isSupportedLocale,
|
|
239
|
+
loadPolicy: () => loadPolicy,
|
|
240
|
+
locales: () => locales,
|
|
241
|
+
matchLocale: () => matchLocale,
|
|
242
|
+
mustBeTrue: () => mustBeTrue,
|
|
243
|
+
normalizeLocale: () => normalizeLocale,
|
|
244
|
+
numericString: () => numericString,
|
|
245
|
+
optionalString: () => optionalString,
|
|
246
|
+
passwordSchema: () => passwordSchema,
|
|
247
|
+
phoneSchema: () => phoneSchema,
|
|
248
|
+
postalCodeSchema: () => postalCodeSchema,
|
|
249
|
+
replaceVariables: () => replaceVariables,
|
|
250
|
+
requiredString: () => requiredString,
|
|
251
|
+
serializeStructuredData: () => serializeStructuredData,
|
|
252
|
+
setI18nConfig: () => setI18nConfig,
|
|
253
|
+
setLocaleCookie: () => setLocaleCookie,
|
|
254
|
+
trackABTestEvent: () => trackABTestEvent,
|
|
255
|
+
trackCTAClick: () => trackCTAClick,
|
|
256
|
+
trackConversion: () => trackConversion,
|
|
257
|
+
trackError: () => trackError,
|
|
258
|
+
trackEvent: () => trackEvent,
|
|
259
|
+
trackFeatureEngagement: () => trackFeatureEngagement,
|
|
260
|
+
trackFormEvent: () => trackFormEvent,
|
|
261
|
+
trackNavigation: () => trackNavigation,
|
|
262
|
+
trackOutboundLink: () => trackOutboundLink,
|
|
263
|
+
trackPageView: () => trackPageView,
|
|
264
|
+
trackPricingEvent: () => trackPricingEvent,
|
|
265
|
+
trackResourceDownload: () => trackResourceDownload,
|
|
266
|
+
trackScrollDepth: () => trackScrollDepth,
|
|
267
|
+
trackSearch: () => trackSearch,
|
|
268
|
+
trackVideoEvent: () => trackVideoEvent,
|
|
269
|
+
translateSlug: () => translateSlug,
|
|
270
|
+
urlSchema: () => urlSchema,
|
|
271
|
+
validateLocale: () => validateLocale,
|
|
272
|
+
validateSitemap: () => validateSitemap,
|
|
273
|
+
validateSitemapEntry: () => validateSitemapEntry
|
|
274
|
+
});
|
|
275
|
+
module.exports = __toCommonJS(src_exports);
|
|
276
|
+
|
|
277
|
+
// src/lib/theme/generate-css.ts
|
|
278
|
+
function generateThemeCSS(theme) {
|
|
279
|
+
return `
|
|
280
|
+
/* Brand Colors */
|
|
281
|
+
--color-primary: ${theme.brand.colors.primary};
|
|
282
|
+
--color-primary-hover: ${theme.brand.colors.primaryHover};
|
|
283
|
+
--color-primary-light: ${theme.brand.colors.primaryLight};
|
|
284
|
+
--color-primary-dark: ${theme.brand.colors.primaryDark};
|
|
285
|
+
--color-primary-gradient-start: ${theme.brand.colors.primaryGradientStart};
|
|
286
|
+
--color-primary-gradient-end: ${theme.brand.colors.primaryGradientEnd};
|
|
287
|
+
--color-hero-gradient-start: ${theme.brand.colors.heroGradientStart};
|
|
288
|
+
--color-hero-gradient-end: ${theme.brand.colors.heroGradientEnd};
|
|
289
|
+
--color-footer-gradient-start: ${theme.brand.colors.footerGradientStart};
|
|
290
|
+
--color-footer-gradient-end: ${theme.brand.colors.footerGradientEnd};
|
|
291
|
+
|
|
292
|
+
/* Fonts */
|
|
293
|
+
--font-heading: ${theme.brand.fonts.heading.family}, ${theme.brand.fonts.heading.fallback};
|
|
294
|
+
--font-body: ${theme.brand.fonts.body.family}, ${theme.brand.fonts.body.fallback};
|
|
295
|
+
--font-serif: ${theme.brand.fonts.heading.family}, ${theme.brand.fonts.heading.fallback};
|
|
296
|
+
--font-sans: ${theme.brand.fonts.body.family}, ${theme.brand.fonts.body.fallback};
|
|
297
|
+
|
|
298
|
+
/* Slate Colors */
|
|
299
|
+
--color-slate-50: ${theme.colors.slate[50]};
|
|
300
|
+
--color-slate-100: ${theme.colors.slate[100]};
|
|
301
|
+
--color-slate-200: ${theme.colors.slate[200]};
|
|
302
|
+
--color-slate-300: ${theme.colors.slate[300]};
|
|
303
|
+
--color-slate-400: ${theme.colors.slate[400]};
|
|
304
|
+
--color-slate-500: ${theme.colors.slate[500]};
|
|
305
|
+
--color-slate-600: ${theme.colors.slate[600]};
|
|
306
|
+
--color-slate-700: ${theme.colors.slate[700]};
|
|
307
|
+
--color-slate-800: ${theme.colors.slate[800]};
|
|
308
|
+
--color-slate-900: ${theme.colors.slate[900]};
|
|
309
|
+
`.trim();
|
|
310
|
+
}
|
|
311
|
+
function generateDesignTokens(theme) {
|
|
312
|
+
const radiusMap = {
|
|
313
|
+
sharp: "0",
|
|
314
|
+
rounded: "0.375rem",
|
|
315
|
+
pill: "9999px"
|
|
316
|
+
};
|
|
317
|
+
const shadowMap = {
|
|
318
|
+
flat: "none",
|
|
319
|
+
subtle: "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)",
|
|
320
|
+
prominent: "0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)"
|
|
321
|
+
};
|
|
322
|
+
const spacingMap = {
|
|
323
|
+
compact: {
|
|
324
|
+
section: "3rem",
|
|
325
|
+
element: "1rem"
|
|
326
|
+
},
|
|
327
|
+
comfortable: {
|
|
328
|
+
section: "6rem",
|
|
329
|
+
element: "1.5rem"
|
|
330
|
+
},
|
|
331
|
+
spacious: {
|
|
332
|
+
section: "8rem",
|
|
333
|
+
element: "2rem"
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
const spacing = spacingMap[theme.design.spacing];
|
|
337
|
+
return `
|
|
338
|
+
/* Design Tokens */
|
|
339
|
+
--radius-default: ${radiusMap[theme.design.borderRadius]};
|
|
340
|
+
--shadow-default: ${shadowMap[theme.design.shadows]};
|
|
341
|
+
--space-section: ${spacing.section};
|
|
342
|
+
--space-element: ${spacing.element};
|
|
343
|
+
`.trim();
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// src/lib/theme/load-fonts.ts
|
|
347
|
+
function getFontConfig(theme) {
|
|
348
|
+
return {
|
|
349
|
+
heading: {
|
|
350
|
+
family: theme.brand.fonts.heading.family,
|
|
351
|
+
weights: theme.brand.fonts.heading.weights.map(String),
|
|
352
|
+
variable: "--font-heading"
|
|
353
|
+
},
|
|
354
|
+
body: {
|
|
355
|
+
family: theme.brand.fonts.body.family,
|
|
356
|
+
weights: theme.brand.fonts.body.weights.map(String),
|
|
357
|
+
variable: "--font-body"
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
function getFontVariables(theme) {
|
|
362
|
+
const headingFamily = theme.brand.fonts.heading.family.replace(/\s+/g, "-").toLowerCase();
|
|
363
|
+
const bodyFamily = theme.brand.fonts.body.family.replace(/\s+/g, "-").toLowerCase();
|
|
364
|
+
return `var(--font-${headingFamily}) var(--font-${bodyFamily})`;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// src/lib/content/utils.ts
|
|
368
|
+
function getLocalizedString(localizedString, locale) {
|
|
369
|
+
return localizedString[locale] || localizedString["en"] || "";
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// src/lib/content/policies.ts
|
|
373
|
+
var import_fs = __toESM(require("fs"));
|
|
374
|
+
var import_path = __toESM(require("path"));
|
|
375
|
+
var import_rsc = require("next-mdx-remote/rsc");
|
|
376
|
+
var import_rehype_slug = __toESM(require("rehype-slug"));
|
|
377
|
+
async function loadPolicy(slug, locale, contentDir = "src/content/policies") {
|
|
378
|
+
const filePath = import_path.default.join(process.cwd(), contentDir, `${slug}.${locale}.md`);
|
|
379
|
+
if (!import_fs.default.existsSync(filePath)) {
|
|
380
|
+
throw new Error(
|
|
381
|
+
`Policy file not found: ${slug}.${locale}.md in ${contentDir}
|
|
382
|
+
Looking for: ${filePath}`
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
const source = import_fs.default.readFileSync(filePath, "utf8");
|
|
386
|
+
const { content, frontmatter } = await (0, import_rsc.compileMDX)({
|
|
387
|
+
source,
|
|
388
|
+
options: {
|
|
389
|
+
parseFrontmatter: true,
|
|
390
|
+
mdxOptions: {
|
|
391
|
+
rehypePlugins: [import_rehype_slug.default]
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
if (!frontmatter.title) {
|
|
396
|
+
throw new Error(`Policy ${slug}.${locale}.md is missing required frontmatter field: title`);
|
|
397
|
+
}
|
|
398
|
+
if (!frontmatter.lastUpdated) {
|
|
399
|
+
throw new Error(
|
|
400
|
+
`Policy ${slug}.${locale}.md is missing required frontmatter field: lastUpdated`
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
return {
|
|
404
|
+
content,
|
|
405
|
+
metadata: frontmatter,
|
|
406
|
+
slug,
|
|
407
|
+
locale
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
function getPolicySlugs(contentDir = "src/content/policies") {
|
|
411
|
+
const policiesDir = import_path.default.join(process.cwd(), contentDir);
|
|
412
|
+
if (!import_fs.default.existsSync(policiesDir)) {
|
|
413
|
+
console.warn(`Policies directory not found: ${policiesDir}`);
|
|
414
|
+
return [];
|
|
415
|
+
}
|
|
416
|
+
const files = import_fs.default.readdirSync(policiesDir);
|
|
417
|
+
const slugs = Array.from(
|
|
418
|
+
new Set(
|
|
419
|
+
files.filter((file) => file.endsWith(".md") || file.endsWith(".mdx")).map((file) => {
|
|
420
|
+
return file.replace(/\.[a-z]{2}(-[A-Z]{2})?\.mdx?$/, "");
|
|
421
|
+
})
|
|
422
|
+
)
|
|
423
|
+
);
|
|
424
|
+
return slugs;
|
|
425
|
+
}
|
|
426
|
+
async function getAllPolicies(locale, contentDir = "src/content/policies") {
|
|
427
|
+
const slugs = getPolicySlugs(contentDir);
|
|
428
|
+
const policies = await Promise.all(
|
|
429
|
+
slugs.map(async (slug) => {
|
|
430
|
+
try {
|
|
431
|
+
const policy = await loadPolicy(slug, locale, contentDir);
|
|
432
|
+
return {
|
|
433
|
+
slug: policy.slug,
|
|
434
|
+
locale: policy.locale,
|
|
435
|
+
metadata: policy.metadata
|
|
436
|
+
};
|
|
437
|
+
} catch (error) {
|
|
438
|
+
console.warn(`Skipping ${slug} for locale ${locale}:`, error);
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
})
|
|
442
|
+
);
|
|
443
|
+
return policies.filter((p) => p !== null);
|
|
444
|
+
}
|
|
445
|
+
function getPolicyLocales(slug, contentDir = "src/content/policies") {
|
|
446
|
+
const policiesDir = import_path.default.join(process.cwd(), contentDir);
|
|
447
|
+
if (!import_fs.default.existsSync(policiesDir)) {
|
|
448
|
+
return [];
|
|
449
|
+
}
|
|
450
|
+
const files = import_fs.default.readdirSync(policiesDir);
|
|
451
|
+
const locales2 = files.filter((file) => {
|
|
452
|
+
const pattern = new RegExp(`^${slug}\\.[a-z]{2}(-[A-Z]{2})?\\.mdx?$`);
|
|
453
|
+
return pattern.test(file);
|
|
454
|
+
}).map((file) => {
|
|
455
|
+
const match = file.match(/\.([a-z]{2}(-[A-Z]{2})?)\.mdx?$/);
|
|
456
|
+
return match ? match[1] : null;
|
|
457
|
+
}).filter((locale) => locale !== null);
|
|
458
|
+
return locales2;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// src/lib/navigation/utils.ts
|
|
462
|
+
function getNavigationString(localizedString, locale) {
|
|
463
|
+
return localizedString[locale] || localizedString["en"] || "";
|
|
464
|
+
}
|
|
465
|
+
function replaceVariables(text, variables) {
|
|
466
|
+
let result = text;
|
|
467
|
+
Object.entries(variables).forEach(([key, value]) => {
|
|
468
|
+
result = result.replace(`{${key}}`, value);
|
|
469
|
+
});
|
|
470
|
+
return result;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// src/lib/i18n/index.ts
|
|
474
|
+
init_config();
|
|
475
|
+
init_config();
|
|
476
|
+
|
|
477
|
+
// src/lib/i18n/locale-cookie.ts
|
|
478
|
+
init_config();
|
|
479
|
+
var LOCALE_COOKIE_NAME = "NEXT_LOCALE";
|
|
480
|
+
function getLocaleFromCookie() {
|
|
481
|
+
if (typeof document === "undefined") return null;
|
|
482
|
+
try {
|
|
483
|
+
const cookieConfig = getLocaleCookieConfig();
|
|
484
|
+
const cookieName = cookieConfig.name;
|
|
485
|
+
const cookie = document.cookie.split("; ").find((row) => row.startsWith(`${cookieName}=`));
|
|
486
|
+
if (!cookie) return null;
|
|
487
|
+
const value = cookie.split("=")[1];
|
|
488
|
+
if (isSupportedLocale(value)) {
|
|
489
|
+
return value;
|
|
490
|
+
}
|
|
491
|
+
return null;
|
|
492
|
+
} catch (error) {
|
|
493
|
+
if (process.env.NODE_ENV !== "production") {
|
|
494
|
+
console.warn("getLocaleFromCookie: i18n config not initialized");
|
|
495
|
+
}
|
|
496
|
+
return null;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
function setLocaleCookie(locale) {
|
|
500
|
+
if (typeof document === "undefined") return;
|
|
501
|
+
try {
|
|
502
|
+
if (!isSupportedLocale(locale)) {
|
|
503
|
+
console.warn(`Attempted to set unsupported locale: ${locale}`);
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
const cookieConfig = getLocaleCookieConfig();
|
|
507
|
+
document.cookie = `${cookieConfig.name}=${locale}; max-age=${cookieConfig.maxAge}; path=/; SameSite=${cookieConfig.sameSite}`;
|
|
508
|
+
} catch (error) {
|
|
509
|
+
if (process.env.NODE_ENV !== "production") {
|
|
510
|
+
console.warn("setLocaleCookie: i18n config not initialized");
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// src/lib/i18n/slug-translations.ts
|
|
516
|
+
var defaultSlugTranslations = {
|
|
517
|
+
fr: {
|
|
518
|
+
"/marketing-par-courriel": "/email-marketing",
|
|
519
|
+
"/courriels-transactionnels": "/transactional-emails",
|
|
520
|
+
"/forfaits": "/pricing",
|
|
521
|
+
"/nous-contacter": "/contact",
|
|
522
|
+
"/a-propos": "/about",
|
|
523
|
+
"/politique-de-confidentialite": "/privacy-policy",
|
|
524
|
+
"/conditions-dutilisation": "/terms-of-service",
|
|
525
|
+
"/politique-anti-pourriel": "/anti-spam-policy"
|
|
526
|
+
},
|
|
527
|
+
en: {
|
|
528
|
+
"/email-marketing": "/marketing-par-courriel",
|
|
529
|
+
"/transactional-emails": "/courriels-transactionnels",
|
|
530
|
+
"/pricing": "/forfaits",
|
|
531
|
+
"/contact": "/nous-contacter",
|
|
532
|
+
"/about": "/a-propos",
|
|
533
|
+
"/privacy-policy": "/politique-de-confidentialite",
|
|
534
|
+
"/terms-of-service": "/conditions-dutilisation",
|
|
535
|
+
"/anti-spam-policy": "/politique-anti-pourriel"
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
function translateSlug(path2, fromLocale, toLocale, customTranslations) {
|
|
539
|
+
let configTranslations = {};
|
|
540
|
+
try {
|
|
541
|
+
const { getI18nConfig: getI18nConfig2 } = (init_config(), __toCommonJS(config_exports));
|
|
542
|
+
const config = getI18nConfig2();
|
|
543
|
+
configTranslations = config.slugTranslations || {};
|
|
544
|
+
} catch {
|
|
545
|
+
}
|
|
546
|
+
const translations = mergeTranslations(
|
|
547
|
+
defaultSlugTranslations,
|
|
548
|
+
configTranslations,
|
|
549
|
+
customTranslations || {}
|
|
550
|
+
);
|
|
551
|
+
const translationMap = translations[fromLocale];
|
|
552
|
+
if (!translationMap) {
|
|
553
|
+
return path2;
|
|
554
|
+
}
|
|
555
|
+
if (translationMap[path2]) {
|
|
556
|
+
return translationMap[path2];
|
|
557
|
+
}
|
|
558
|
+
for (const [fromSlug, toSlug] of Object.entries(translationMap)) {
|
|
559
|
+
if (path2.startsWith(fromSlug + "/")) {
|
|
560
|
+
return path2.replace(fromSlug, toSlug);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
return path2;
|
|
564
|
+
}
|
|
565
|
+
function mergeTranslations(...translations) {
|
|
566
|
+
const merged = {};
|
|
567
|
+
for (const trans of translations) {
|
|
568
|
+
for (const [locale, slugs] of Object.entries(trans)) {
|
|
569
|
+
if (!merged[locale]) {
|
|
570
|
+
merged[locale] = {};
|
|
571
|
+
}
|
|
572
|
+
Object.assign(merged[locale], slugs);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
return merged;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// src/lib/i18n/formatters.ts
|
|
579
|
+
function formatDate(date, locale, options) {
|
|
580
|
+
return new Intl.DateTimeFormat(locale, options).format(date);
|
|
581
|
+
}
|
|
582
|
+
function formatNumber(value, locale, options) {
|
|
583
|
+
return new Intl.NumberFormat(locale, options).format(value);
|
|
584
|
+
}
|
|
585
|
+
function formatCurrency(amount, locale, currency, options) {
|
|
586
|
+
return new Intl.NumberFormat(locale, {
|
|
587
|
+
style: "currency",
|
|
588
|
+
currency,
|
|
589
|
+
...options
|
|
590
|
+
}).format(amount);
|
|
591
|
+
}
|
|
592
|
+
function formatRelativeTime(value, unit, locale, options) {
|
|
593
|
+
return new Intl.RelativeTimeFormat(locale, options).format(value, unit);
|
|
594
|
+
}
|
|
595
|
+
function formatDateRange(startDate, endDate, locale, options) {
|
|
596
|
+
const formatter = new Intl.DateTimeFormat(locale, options);
|
|
597
|
+
if ("formatRange" in formatter) {
|
|
598
|
+
return formatter.formatRange(startDate, endDate);
|
|
599
|
+
}
|
|
600
|
+
return `${formatter.format(startDate)} \u2013 ${formatter.format(endDate)}`;
|
|
601
|
+
}
|
|
602
|
+
function formatList(items, locale) {
|
|
603
|
+
if (typeof Intl !== "undefined" && "ListFormat" in Intl) {
|
|
604
|
+
return new Intl.ListFormat(locale).format(items);
|
|
605
|
+
}
|
|
606
|
+
if (items.length === 0) return "";
|
|
607
|
+
if (items.length === 1) return items[0];
|
|
608
|
+
if (items.length === 2) {
|
|
609
|
+
const connector2 = locale === "fr" ? " et " : " and ";
|
|
610
|
+
return items.join(connector2);
|
|
611
|
+
}
|
|
612
|
+
const last = items[items.length - 1];
|
|
613
|
+
const rest = items.slice(0, -1);
|
|
614
|
+
const connector = locale === "fr" ? " et " : ", and ";
|
|
615
|
+
return rest.join(", ") + connector + last;
|
|
616
|
+
}
|
|
617
|
+
function formatFileSize(bytes, locale, decimals = 2) {
|
|
618
|
+
if (bytes === 0) return "0 B";
|
|
619
|
+
const k = 1024;
|
|
620
|
+
const sizes = ["B", "KB", "MB", "GB", "TB", "PB"];
|
|
621
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
622
|
+
const value = bytes / Math.pow(k, i);
|
|
623
|
+
return `${formatNumber(value, locale, {
|
|
624
|
+
minimumFractionDigits: decimals,
|
|
625
|
+
maximumFractionDigits: decimals
|
|
626
|
+
})} ${sizes[i]}`;
|
|
627
|
+
}
|
|
628
|
+
function getRelativeTime(date, locale, options) {
|
|
629
|
+
const now = /* @__PURE__ */ new Date();
|
|
630
|
+
const diffMs = date.getTime() - now.getTime();
|
|
631
|
+
const diffSecs = Math.round(diffMs / 1e3);
|
|
632
|
+
const diffMins = Math.round(diffSecs / 60);
|
|
633
|
+
const diffHours = Math.round(diffMins / 60);
|
|
634
|
+
const diffDays = Math.round(diffHours / 24);
|
|
635
|
+
const diffWeeks = Math.round(diffDays / 7);
|
|
636
|
+
const diffMonths = Math.round(diffDays / 30);
|
|
637
|
+
const diffYears = Math.round(diffDays / 365);
|
|
638
|
+
if (Math.abs(diffSecs) < 60) {
|
|
639
|
+
return formatRelativeTime(diffSecs, "second", locale, options);
|
|
640
|
+
} else if (Math.abs(diffMins) < 60) {
|
|
641
|
+
return formatRelativeTime(diffMins, "minute", locale, options);
|
|
642
|
+
} else if (Math.abs(diffHours) < 24) {
|
|
643
|
+
return formatRelativeTime(diffHours, "hour", locale, options);
|
|
644
|
+
} else if (Math.abs(diffDays) < 7) {
|
|
645
|
+
return formatRelativeTime(diffDays, "day", locale, options);
|
|
646
|
+
} else if (Math.abs(diffWeeks) < 4) {
|
|
647
|
+
return formatRelativeTime(diffWeeks, "week", locale, options);
|
|
648
|
+
} else if (Math.abs(diffMonths) < 12) {
|
|
649
|
+
return formatRelativeTime(diffMonths, "month", locale, options);
|
|
650
|
+
} else {
|
|
651
|
+
return formatRelativeTime(diffYears, "year", locale, options);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// src/lib/i18n/middleware.ts
|
|
656
|
+
var import_server = require("next/server");
|
|
657
|
+
function createI18nMiddleware(config) {
|
|
658
|
+
return function middleware(request) {
|
|
659
|
+
const { pathname } = request.nextUrl;
|
|
660
|
+
const pathnameLocale = getLocaleFromPathname(pathname, config);
|
|
661
|
+
if (pathnameLocale) {
|
|
662
|
+
if (!config.locales.includes(pathnameLocale)) {
|
|
663
|
+
return redirectToLocale(request, config.defaultLocale, pathname, config);
|
|
664
|
+
}
|
|
665
|
+
const response = import_server.NextResponse.next();
|
|
666
|
+
setLocaleCookie2(response, pathnameLocale, config);
|
|
667
|
+
return response;
|
|
668
|
+
}
|
|
669
|
+
const detectedLocale = detectLocale(request, config);
|
|
670
|
+
const prefixMode = config.localePrefix || "as-needed";
|
|
671
|
+
if (prefixMode === "always") {
|
|
672
|
+
return redirectToLocale(request, detectedLocale, pathname, config);
|
|
673
|
+
} else if (prefixMode === "as-needed") {
|
|
674
|
+
if (detectedLocale !== config.defaultLocale) {
|
|
675
|
+
return redirectToLocale(request, detectedLocale, pathname, config);
|
|
676
|
+
}
|
|
677
|
+
const response = import_server.NextResponse.next();
|
|
678
|
+
setLocaleCookie2(response, config.defaultLocale, config);
|
|
679
|
+
return response;
|
|
680
|
+
} else {
|
|
681
|
+
const response = import_server.NextResponse.next();
|
|
682
|
+
setLocaleCookie2(response, detectedLocale, config);
|
|
683
|
+
return response;
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
function getLocaleFromPathname(pathname, config) {
|
|
688
|
+
const segments = pathname.split("/").filter(Boolean);
|
|
689
|
+
if (segments.length === 0) {
|
|
690
|
+
return null;
|
|
691
|
+
}
|
|
692
|
+
const firstSegment = segments[0];
|
|
693
|
+
if (config.locales.includes(firstSegment)) {
|
|
694
|
+
return firstSegment;
|
|
695
|
+
}
|
|
696
|
+
return null;
|
|
697
|
+
}
|
|
698
|
+
function detectLocale(request, config) {
|
|
699
|
+
const cookieName = config.localeCookie?.name || "NEXT_LOCALE";
|
|
700
|
+
const cookieLocale = request.cookies.get(cookieName)?.value;
|
|
701
|
+
if (cookieLocale && config.locales.includes(cookieLocale)) {
|
|
702
|
+
return cookieLocale;
|
|
703
|
+
}
|
|
704
|
+
if (config.localeDetection !== false) {
|
|
705
|
+
const acceptLanguage = request.headers.get("accept-language");
|
|
706
|
+
const browserLocale = negotiateLanguage(acceptLanguage, config.locales);
|
|
707
|
+
if (browserLocale) {
|
|
708
|
+
return browserLocale;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
return config.defaultLocale;
|
|
712
|
+
}
|
|
713
|
+
function negotiateLanguage(acceptLanguage, supportedLocales) {
|
|
714
|
+
if (!acceptLanguage) {
|
|
715
|
+
return null;
|
|
716
|
+
}
|
|
717
|
+
const preferences = parseAcceptLanguage(acceptLanguage);
|
|
718
|
+
for (const pref of preferences) {
|
|
719
|
+
if (supportedLocales.includes(pref.locale)) {
|
|
720
|
+
return pref.locale;
|
|
721
|
+
}
|
|
722
|
+
const languageCode = pref.locale.split("-")[0];
|
|
723
|
+
if (supportedLocales.includes(languageCode)) {
|
|
724
|
+
return languageCode;
|
|
725
|
+
}
|
|
726
|
+
const match = supportedLocales.find(
|
|
727
|
+
(locale) => locale.startsWith(languageCode + "-") || locale === languageCode
|
|
728
|
+
);
|
|
729
|
+
if (match) {
|
|
730
|
+
return match;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
return null;
|
|
734
|
+
}
|
|
735
|
+
function parseAcceptLanguage(header) {
|
|
736
|
+
const preferences = [];
|
|
737
|
+
const parts = header.split(",").map((p) => p.trim());
|
|
738
|
+
for (const part of parts) {
|
|
739
|
+
const [locale, ...rest] = part.split(";").map((p) => p.trim());
|
|
740
|
+
let quality = 1;
|
|
741
|
+
const qParam = rest.find((p) => p.startsWith("q="));
|
|
742
|
+
if (qParam) {
|
|
743
|
+
const qValue = parseFloat(qParam.substring(2));
|
|
744
|
+
if (!isNaN(qValue)) {
|
|
745
|
+
quality = qValue;
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
preferences.push({
|
|
749
|
+
locale: locale.toLowerCase(),
|
|
750
|
+
quality
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
preferences.sort((a, b) => b.quality - a.quality);
|
|
754
|
+
return preferences;
|
|
755
|
+
}
|
|
756
|
+
function redirectToLocale(request, locale, pathname, config) {
|
|
757
|
+
const url = request.nextUrl.clone();
|
|
758
|
+
const pathWithoutLocale = removeLocalePrefix(pathname, config);
|
|
759
|
+
url.pathname = `/${locale}${pathWithoutLocale}`;
|
|
760
|
+
const response = import_server.NextResponse.redirect(url);
|
|
761
|
+
setLocaleCookie2(response, locale, config);
|
|
762
|
+
return response;
|
|
763
|
+
}
|
|
764
|
+
function removeLocalePrefix(pathname, config) {
|
|
765
|
+
const segments = pathname.split("/").filter(Boolean);
|
|
766
|
+
if (segments.length === 0) {
|
|
767
|
+
return "/";
|
|
768
|
+
}
|
|
769
|
+
const firstSegment = segments[0];
|
|
770
|
+
if (config.locales.includes(firstSegment)) {
|
|
771
|
+
const remaining = segments.slice(1);
|
|
772
|
+
return remaining.length > 0 ? `/${remaining.join("/")}` : "/";
|
|
773
|
+
}
|
|
774
|
+
return pathname;
|
|
775
|
+
}
|
|
776
|
+
function setLocaleCookie2(response, locale, config) {
|
|
777
|
+
const cookieConfig = config.localeCookie || {};
|
|
778
|
+
response.cookies.set({
|
|
779
|
+
name: cookieConfig.name || "NEXT_LOCALE",
|
|
780
|
+
value: locale,
|
|
781
|
+
maxAge: cookieConfig.maxAge || 365 * 24 * 60 * 60,
|
|
782
|
+
// 1 year
|
|
783
|
+
path: "/",
|
|
784
|
+
sameSite: cookieConfig.sameSite || "lax"
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// src/lib/i18n/utils.ts
|
|
789
|
+
init_config();
|
|
790
|
+
function isRtlLocale(locale) {
|
|
791
|
+
const rtlLocales = getRtlLocales();
|
|
792
|
+
return rtlLocales.includes(locale);
|
|
793
|
+
}
|
|
794
|
+
function getTextDirection(locale) {
|
|
795
|
+
return isRtlLocale(locale) ? "rtl" : "ltr";
|
|
796
|
+
}
|
|
797
|
+
function validateLocale(locale) {
|
|
798
|
+
return isSupportedLocale(locale);
|
|
799
|
+
}
|
|
800
|
+
function getAlternateLocales(currentLocale) {
|
|
801
|
+
try {
|
|
802
|
+
const config = getI18nConfig();
|
|
803
|
+
return config.locales.filter((l) => l !== currentLocale);
|
|
804
|
+
} catch (error) {
|
|
805
|
+
if (process.env.NODE_ENV !== "production") {
|
|
806
|
+
console.warn("getAlternateLocales: i18n config not initialized, returning []");
|
|
807
|
+
}
|
|
808
|
+
return [];
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
function normalizeLocale(locale) {
|
|
812
|
+
return locale.toLowerCase().split("-")[0];
|
|
813
|
+
}
|
|
814
|
+
function matchLocale(locale) {
|
|
815
|
+
try {
|
|
816
|
+
const config = getI18nConfig();
|
|
817
|
+
const normalized = normalizeLocale(locale);
|
|
818
|
+
if (config.locales.includes(locale)) {
|
|
819
|
+
return locale;
|
|
820
|
+
}
|
|
821
|
+
if (config.locales.includes(normalized)) {
|
|
822
|
+
return normalized;
|
|
823
|
+
}
|
|
824
|
+
const match = config.locales.find((l) => l.startsWith(normalized));
|
|
825
|
+
if (match) {
|
|
826
|
+
return match;
|
|
827
|
+
}
|
|
828
|
+
return null;
|
|
829
|
+
} catch (error) {
|
|
830
|
+
if (process.env.NODE_ENV !== "production") {
|
|
831
|
+
console.warn("matchLocale: i18n config not initialized, returning null");
|
|
832
|
+
}
|
|
833
|
+
return null;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
function getLocaleAutonym(locale) {
|
|
837
|
+
const autonyms = {
|
|
838
|
+
en: "English",
|
|
839
|
+
fr: "Fran\xE7ais",
|
|
840
|
+
es: "Espa\xF1ol",
|
|
841
|
+
de: "Deutsch",
|
|
842
|
+
it: "Italiano",
|
|
843
|
+
pt: "Portugu\xEAs",
|
|
844
|
+
ru: "\u0420\u0443\u0441\u0441\u043A\u0438\u0439",
|
|
845
|
+
ja: "\u65E5\u672C\u8A9E",
|
|
846
|
+
ko: "\uD55C\uAD6D\uC5B4",
|
|
847
|
+
zh: "\u4E2D\u6587",
|
|
848
|
+
ar: "\u0627\u0644\u0639\u0631\u0628\u064A\u0629",
|
|
849
|
+
he: "\u05E2\u05D1\u05E8\u05D9\u05EA",
|
|
850
|
+
hi: "\u0939\u093F\u0928\u094D\u0926\u0940",
|
|
851
|
+
tr: "T\xFCrk\xE7e",
|
|
852
|
+
pl: "Polski",
|
|
853
|
+
nl: "Nederlands",
|
|
854
|
+
sv: "Svenska",
|
|
855
|
+
da: "Dansk",
|
|
856
|
+
no: "Norsk",
|
|
857
|
+
fi: "Suomi",
|
|
858
|
+
cs: "\u010Ce\u0161tina",
|
|
859
|
+
el: "\u0395\u03BB\u03BB\u03B7\u03BD\u03B9\u03BA\u03AC",
|
|
860
|
+
th: "\u0E44\u0E17\u0E22",
|
|
861
|
+
vi: "Ti\u1EBFng Vi\u1EC7t",
|
|
862
|
+
id: "Bahasa Indonesia",
|
|
863
|
+
ms: "Bahasa Melayu",
|
|
864
|
+
uk: "\u0423\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0430"
|
|
865
|
+
};
|
|
866
|
+
try {
|
|
867
|
+
const { getLocaleNames: getLocaleNames2 } = (init_config(), __toCommonJS(config_exports));
|
|
868
|
+
const names = getLocaleNames2();
|
|
869
|
+
if (names[locale]) {
|
|
870
|
+
return names[locale];
|
|
871
|
+
}
|
|
872
|
+
} catch {
|
|
873
|
+
}
|
|
874
|
+
return autonyms[locale] || locale.toUpperCase();
|
|
875
|
+
}
|
|
876
|
+
function formatLocaleDisplay(locale, format = "name") {
|
|
877
|
+
if (format === "code") {
|
|
878
|
+
return locale;
|
|
879
|
+
}
|
|
880
|
+
if (format === "label") {
|
|
881
|
+
try {
|
|
882
|
+
const { getLocaleLabel: getLocaleLabel2 } = (init_config(), __toCommonJS(config_exports));
|
|
883
|
+
return getLocaleLabel2(locale);
|
|
884
|
+
} catch {
|
|
885
|
+
return locale.toUpperCase();
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
return getLocaleAutonym(locale);
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
// src/lib/utils/cn.ts
|
|
892
|
+
var import_clsx = require("clsx");
|
|
893
|
+
var import_tailwind_merge = require("tailwind-merge");
|
|
894
|
+
function cn(...inputs) {
|
|
895
|
+
return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// src/lib/analytics.ts
|
|
899
|
+
function pushToDataLayer(data) {
|
|
900
|
+
if (typeof window === "undefined") return;
|
|
901
|
+
window.dataLayer = window.dataLayer || [];
|
|
902
|
+
window.dataLayer.push(data);
|
|
903
|
+
if (process.env.NODE_ENV === "development") {
|
|
904
|
+
console.log("[Analytics]", data);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
function trackEvent(eventName, properties) {
|
|
908
|
+
pushToDataLayer({
|
|
909
|
+
event: eventName,
|
|
910
|
+
...properties
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
function trackCTAClick(ctaLocation, ctaText, ctaType = "other") {
|
|
914
|
+
pushToDataLayer({
|
|
915
|
+
event: "cta_click",
|
|
916
|
+
event_category: "cta",
|
|
917
|
+
event_label: `${ctaLocation}_${ctaType}`,
|
|
918
|
+
cta_location: ctaLocation,
|
|
919
|
+
cta_text: ctaText,
|
|
920
|
+
cta_type: ctaType
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
function trackFormEvent(formName, action, metadata) {
|
|
924
|
+
pushToDataLayer({
|
|
925
|
+
event: `form_${action}`,
|
|
926
|
+
event_category: "form",
|
|
927
|
+
event_label: formName,
|
|
928
|
+
form_name: formName,
|
|
929
|
+
form_action: action,
|
|
930
|
+
...metadata
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
function trackPageView(pagePath, pageTitle) {
|
|
934
|
+
pushToDataLayer({
|
|
935
|
+
event: "page_view",
|
|
936
|
+
page_path: pagePath,
|
|
937
|
+
page_title: pageTitle
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
function trackPricingEvent(action, metadata) {
|
|
941
|
+
pushToDataLayer({
|
|
942
|
+
event: "pricing_interaction",
|
|
943
|
+
event_category: "engagement",
|
|
944
|
+
event_label: action,
|
|
945
|
+
pricing_action: action,
|
|
946
|
+
...metadata
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
function trackFeatureEngagement(featureName, action, metadata) {
|
|
950
|
+
pushToDataLayer({
|
|
951
|
+
event: "feature_engagement",
|
|
952
|
+
event_category: "engagement",
|
|
953
|
+
event_label: `${featureName}_${action}`,
|
|
954
|
+
feature_name: featureName,
|
|
955
|
+
feature_action: action,
|
|
956
|
+
...metadata
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
function trackResourceDownload(resourceName, resourceType) {
|
|
960
|
+
pushToDataLayer({
|
|
961
|
+
event: "resource_download",
|
|
962
|
+
event_category: "engagement",
|
|
963
|
+
event_label: resourceName,
|
|
964
|
+
resource_name: resourceName,
|
|
965
|
+
resource_type: resourceType
|
|
966
|
+
});
|
|
967
|
+
}
|
|
968
|
+
function trackVideoEvent(videoTitle, action, metadata) {
|
|
969
|
+
pushToDataLayer({
|
|
970
|
+
event: "video_interaction",
|
|
971
|
+
event_category: "engagement",
|
|
972
|
+
event_label: `${videoTitle}_${action}`,
|
|
973
|
+
video_title: videoTitle,
|
|
974
|
+
video_action: action,
|
|
975
|
+
...metadata
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
function trackNavigation(linkText, linkUrl, linkLocation) {
|
|
979
|
+
pushToDataLayer({
|
|
980
|
+
event: "navigation_click",
|
|
981
|
+
event_category: "navigation",
|
|
982
|
+
event_label: linkText,
|
|
983
|
+
link_text: linkText,
|
|
984
|
+
link_url: linkUrl,
|
|
985
|
+
link_location: linkLocation
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
function trackABTestEvent(testId, variant, eventName, metadata) {
|
|
989
|
+
pushToDataLayer({
|
|
990
|
+
event: "ab_test_event",
|
|
991
|
+
event_category: "ab_test",
|
|
992
|
+
event_label: `${testId}_${variant}_${eventName}`,
|
|
993
|
+
test_id: testId,
|
|
994
|
+
variant,
|
|
995
|
+
test_event: eventName,
|
|
996
|
+
...metadata
|
|
997
|
+
});
|
|
998
|
+
}
|
|
999
|
+
function trackConversion(conversionType, value, metadata) {
|
|
1000
|
+
pushToDataLayer({
|
|
1001
|
+
event: "conversion",
|
|
1002
|
+
event_category: "conversion",
|
|
1003
|
+
event_label: conversionType,
|
|
1004
|
+
conversion_type: conversionType,
|
|
1005
|
+
value,
|
|
1006
|
+
...metadata
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
function trackScrollDepth(percentage, pagePath) {
|
|
1010
|
+
pushToDataLayer({
|
|
1011
|
+
event: "scroll_depth",
|
|
1012
|
+
event_category: "engagement",
|
|
1013
|
+
event_label: `${percentage}%`,
|
|
1014
|
+
scroll_percentage: percentage,
|
|
1015
|
+
page_path: pagePath
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
function trackSearch(searchTerm, resultCount) {
|
|
1019
|
+
pushToDataLayer({
|
|
1020
|
+
event: "search",
|
|
1021
|
+
event_category: "engagement",
|
|
1022
|
+
event_label: searchTerm,
|
|
1023
|
+
search_term: searchTerm,
|
|
1024
|
+
result_count: resultCount
|
|
1025
|
+
});
|
|
1026
|
+
}
|
|
1027
|
+
function trackOutboundLink(url, linkText) {
|
|
1028
|
+
pushToDataLayer({
|
|
1029
|
+
event: "outbound_link",
|
|
1030
|
+
event_category: "navigation",
|
|
1031
|
+
event_label: url,
|
|
1032
|
+
outbound_url: url,
|
|
1033
|
+
link_text: linkText
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
function trackError(errorType, errorMessage) {
|
|
1037
|
+
pushToDataLayer({
|
|
1038
|
+
event: "error",
|
|
1039
|
+
event_category: "engagement",
|
|
1040
|
+
event_label: errorType,
|
|
1041
|
+
error_type: errorType,
|
|
1042
|
+
error_message: errorMessage
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
// src/lib/utils/forms.tsx
|
|
1047
|
+
var React = __toESM(require("react"));
|
|
1048
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
1049
|
+
function hasZod() {
|
|
1050
|
+
try {
|
|
1051
|
+
require.resolve("zod");
|
|
1052
|
+
return true;
|
|
1053
|
+
} catch {
|
|
1054
|
+
return false;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
function getZod() {
|
|
1058
|
+
if (hasZod()) {
|
|
1059
|
+
return require("zod");
|
|
1060
|
+
}
|
|
1061
|
+
return null;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
// src/lib/forms/schemas.ts
|
|
1065
|
+
var z = getZod();
|
|
1066
|
+
var emailSchema = z?.string().email();
|
|
1067
|
+
var phoneSchema = z?.string().regex(
|
|
1068
|
+
/^\+?[1-9]\d{1,14}$/,
|
|
1069
|
+
"Invalid phone number format"
|
|
1070
|
+
);
|
|
1071
|
+
var postalCodeSchema = z?.string().regex(
|
|
1072
|
+
/^(\d{5}(-\d{4})?|[A-Z]\d[A-Z] ?\d[A-Z]\d)$/i,
|
|
1073
|
+
"Invalid postal code"
|
|
1074
|
+
);
|
|
1075
|
+
var passwordSchema = z?.string().min(8, "Password must be at least 8 characters").regex(/[A-Z]/, "Password must contain at least one uppercase letter").regex(/[a-z]/, "Password must contain at least one lowercase letter").regex(/[0-9]/, "Password must contain at least one number");
|
|
1076
|
+
var urlSchema = z?.string().url();
|
|
1077
|
+
var creditCardSchema = z?.string().refine((value) => {
|
|
1078
|
+
const digits = value.replace(/\D/g, "");
|
|
1079
|
+
if (digits.length < 13 || digits.length > 19) return false;
|
|
1080
|
+
let sum = 0;
|
|
1081
|
+
let isEven = false;
|
|
1082
|
+
for (let i = digits.length - 1; i >= 0; i--) {
|
|
1083
|
+
let digit = parseInt(digits[i]);
|
|
1084
|
+
if (isEven) {
|
|
1085
|
+
digit *= 2;
|
|
1086
|
+
if (digit > 9) digit -= 9;
|
|
1087
|
+
}
|
|
1088
|
+
sum += digit;
|
|
1089
|
+
isEven = !isEven;
|
|
1090
|
+
}
|
|
1091
|
+
return sum % 10 === 0;
|
|
1092
|
+
}, "Invalid credit card number");
|
|
1093
|
+
function fileSchema(acceptedTypes, maxSizeMB) {
|
|
1094
|
+
return z?.instanceof(File).refine(
|
|
1095
|
+
(file) => acceptedTypes.includes(file.type),
|
|
1096
|
+
`File must be one of: ${acceptedTypes.join(", ")}`
|
|
1097
|
+
).refine(
|
|
1098
|
+
(file) => file.size <= maxSizeMB * 1024 * 1024,
|
|
1099
|
+
`File size must be less than ${maxSizeMB}MB`
|
|
1100
|
+
);
|
|
1101
|
+
}
|
|
1102
|
+
function imageSchema(maxSizeMB = 5) {
|
|
1103
|
+
return fileSchema(["image/jpeg", "image/png", "image/webp"], maxSizeMB);
|
|
1104
|
+
}
|
|
1105
|
+
var requiredString = z?.string().min(1, "This field is required");
|
|
1106
|
+
var optionalString = z?.string().optional().or(z?.literal(""));
|
|
1107
|
+
var numericString = z?.string().regex(/^\d+$/, "Must contain only numbers");
|
|
1108
|
+
var dateString = z?.string().datetime();
|
|
1109
|
+
var mustBeTrue = z?.literal(true, {
|
|
1110
|
+
errorMap: () => ({ message: "You must accept to continue" })
|
|
1111
|
+
});
|
|
1112
|
+
|
|
1113
|
+
// src/lib/forms/errors.ts
|
|
1114
|
+
var formErrorMessages = {
|
|
1115
|
+
required: {
|
|
1116
|
+
en: "This field is required",
|
|
1117
|
+
fr: "Ce champ est obligatoire"
|
|
1118
|
+
},
|
|
1119
|
+
email: {
|
|
1120
|
+
en: "Please enter a valid email address",
|
|
1121
|
+
fr: "Veuillez entrer une adresse e-mail valide"
|
|
1122
|
+
},
|
|
1123
|
+
phone: {
|
|
1124
|
+
en: "Please enter a valid phone number",
|
|
1125
|
+
fr: "Veuillez entrer un num\xE9ro de t\xE9l\xE9phone valide"
|
|
1126
|
+
},
|
|
1127
|
+
url: {
|
|
1128
|
+
en: "Please enter a valid URL",
|
|
1129
|
+
fr: "Veuillez entrer une URL valide"
|
|
1130
|
+
},
|
|
1131
|
+
password: {
|
|
1132
|
+
tooShort: {
|
|
1133
|
+
en: "Password must be at least 8 characters",
|
|
1134
|
+
fr: "Le mot de passe doit contenir au moins 8 caract\xE8res"
|
|
1135
|
+
},
|
|
1136
|
+
noUppercase: {
|
|
1137
|
+
en: "Password must contain at least one uppercase letter",
|
|
1138
|
+
fr: "Le mot de passe doit contenir au moins une lettre majuscule"
|
|
1139
|
+
},
|
|
1140
|
+
noLowercase: {
|
|
1141
|
+
en: "Password must contain at least one lowercase letter",
|
|
1142
|
+
fr: "Le mot de passe doit contenir au moins une lettre minuscule"
|
|
1143
|
+
},
|
|
1144
|
+
noNumber: {
|
|
1145
|
+
en: "Password must contain at least one number",
|
|
1146
|
+
fr: "Le mot de passe doit contenir au moins un chiffre"
|
|
1147
|
+
}
|
|
1148
|
+
},
|
|
1149
|
+
postalCode: {
|
|
1150
|
+
en: "Invalid postal code",
|
|
1151
|
+
fr: "Code postal invalide"
|
|
1152
|
+
},
|
|
1153
|
+
creditCard: {
|
|
1154
|
+
en: "Invalid credit card number",
|
|
1155
|
+
fr: "Num\xE9ro de carte de cr\xE9dit invalide"
|
|
1156
|
+
},
|
|
1157
|
+
file: {
|
|
1158
|
+
tooLarge: {
|
|
1159
|
+
en: (maxSize) => `File size must be less than ${maxSize}MB`,
|
|
1160
|
+
fr: (maxSize) => `La taille du fichier doit \xEAtre inf\xE9rieure \xE0 ${maxSize}Mo`
|
|
1161
|
+
},
|
|
1162
|
+
invalidType: {
|
|
1163
|
+
en: (types) => `File must be one of: ${types.join(", ")}`,
|
|
1164
|
+
fr: (types) => `Le fichier doit \xEAtre de type : ${types.join(", ")}`
|
|
1165
|
+
}
|
|
1166
|
+
},
|
|
1167
|
+
mustAccept: {
|
|
1168
|
+
en: "You must accept to continue",
|
|
1169
|
+
fr: "Vous devez accepter pour continuer"
|
|
1170
|
+
},
|
|
1171
|
+
minLength: {
|
|
1172
|
+
en: (min) => `Must be at least ${min} characters`,
|
|
1173
|
+
fr: (min) => `Doit contenir au moins ${min} caract\xE8res`
|
|
1174
|
+
},
|
|
1175
|
+
maxLength: {
|
|
1176
|
+
en: (max) => `Must be no more than ${max} characters`,
|
|
1177
|
+
fr: (max) => `Ne doit pas d\xE9passer ${max} caract\xE8res`
|
|
1178
|
+
},
|
|
1179
|
+
min: {
|
|
1180
|
+
en: (min) => `Must be at least ${min}`,
|
|
1181
|
+
fr: (min) => `Doit \xEAtre au moins ${min}`
|
|
1182
|
+
},
|
|
1183
|
+
max: {
|
|
1184
|
+
en: (max) => `Must be no more than ${max}`,
|
|
1185
|
+
fr: (max) => `Ne doit pas d\xE9passer ${max}`
|
|
1186
|
+
},
|
|
1187
|
+
invalidFormat: {
|
|
1188
|
+
en: "Invalid format",
|
|
1189
|
+
fr: "Format invalide"
|
|
1190
|
+
}
|
|
1191
|
+
};
|
|
1192
|
+
function getErrorMessage(key, locale = "en", params) {
|
|
1193
|
+
const message = formErrorMessages[key];
|
|
1194
|
+
if (!message) return "";
|
|
1195
|
+
const localized = message[locale];
|
|
1196
|
+
if (typeof localized === "function") {
|
|
1197
|
+
return localized(params);
|
|
1198
|
+
}
|
|
1199
|
+
return localized || "";
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
// src/lib/seo/metadata.ts
|
|
1203
|
+
function generateMetadata({
|
|
1204
|
+
title,
|
|
1205
|
+
description,
|
|
1206
|
+
url,
|
|
1207
|
+
image,
|
|
1208
|
+
imageAlt,
|
|
1209
|
+
type = "website",
|
|
1210
|
+
article,
|
|
1211
|
+
twitterCard = "summary_large_image",
|
|
1212
|
+
twitterSite,
|
|
1213
|
+
twitterCreator,
|
|
1214
|
+
locale = "en",
|
|
1215
|
+
alternateLocales,
|
|
1216
|
+
siteName,
|
|
1217
|
+
robots,
|
|
1218
|
+
keywords,
|
|
1219
|
+
author,
|
|
1220
|
+
other
|
|
1221
|
+
}) {
|
|
1222
|
+
const metadata = {
|
|
1223
|
+
title,
|
|
1224
|
+
description,
|
|
1225
|
+
...keywords && { keywords: keywords.join(", ") },
|
|
1226
|
+
...author && { authors: [{ name: author }] },
|
|
1227
|
+
// Open Graph
|
|
1228
|
+
openGraph: {
|
|
1229
|
+
title,
|
|
1230
|
+
description,
|
|
1231
|
+
type,
|
|
1232
|
+
...url && { url },
|
|
1233
|
+
...siteName && { siteName },
|
|
1234
|
+
...locale && { locale },
|
|
1235
|
+
...image && {
|
|
1236
|
+
images: [
|
|
1237
|
+
{
|
|
1238
|
+
url: image,
|
|
1239
|
+
...imageAlt && { alt: imageAlt }
|
|
1240
|
+
}
|
|
1241
|
+
]
|
|
1242
|
+
},
|
|
1243
|
+
...type === "article" && article && {
|
|
1244
|
+
publishedTime: article.publishedTime,
|
|
1245
|
+
modifiedTime: article.modifiedTime,
|
|
1246
|
+
authors: article.author ? [article.author] : void 0,
|
|
1247
|
+
tags: article.tags
|
|
1248
|
+
}
|
|
1249
|
+
},
|
|
1250
|
+
// Twitter Card
|
|
1251
|
+
twitter: {
|
|
1252
|
+
card: twitterCard,
|
|
1253
|
+
title,
|
|
1254
|
+
description,
|
|
1255
|
+
...twitterSite && { site: `@${twitterSite}` },
|
|
1256
|
+
...twitterCreator && { creator: `@${twitterCreator}` },
|
|
1257
|
+
...image && {
|
|
1258
|
+
images: [image]
|
|
1259
|
+
}
|
|
1260
|
+
},
|
|
1261
|
+
// Robots
|
|
1262
|
+
...robots && { robots },
|
|
1263
|
+
// Alternates for i18n
|
|
1264
|
+
...url && alternateLocales && {
|
|
1265
|
+
alternates: {
|
|
1266
|
+
canonical: url,
|
|
1267
|
+
languages: Object.fromEntries(
|
|
1268
|
+
alternateLocales.map((loc) => [
|
|
1269
|
+
loc,
|
|
1270
|
+
url.replace(`/${locale}`, `/${loc}`)
|
|
1271
|
+
])
|
|
1272
|
+
)
|
|
1273
|
+
}
|
|
1274
|
+
},
|
|
1275
|
+
// Additional metadata
|
|
1276
|
+
...other && { other }
|
|
1277
|
+
};
|
|
1278
|
+
return metadata;
|
|
1279
|
+
}
|
|
1280
|
+
function generateArticleMetadata({
|
|
1281
|
+
title,
|
|
1282
|
+
description,
|
|
1283
|
+
image,
|
|
1284
|
+
publishedTime,
|
|
1285
|
+
modifiedTime,
|
|
1286
|
+
author,
|
|
1287
|
+
tags,
|
|
1288
|
+
url,
|
|
1289
|
+
siteName,
|
|
1290
|
+
twitterSite
|
|
1291
|
+
}) {
|
|
1292
|
+
return generateMetadata({
|
|
1293
|
+
title,
|
|
1294
|
+
description,
|
|
1295
|
+
image,
|
|
1296
|
+
url,
|
|
1297
|
+
siteName,
|
|
1298
|
+
twitterSite,
|
|
1299
|
+
type: "article",
|
|
1300
|
+
article: {
|
|
1301
|
+
publishedTime,
|
|
1302
|
+
modifiedTime,
|
|
1303
|
+
author,
|
|
1304
|
+
tags
|
|
1305
|
+
}
|
|
1306
|
+
});
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
// src/lib/seo/structured-data.ts
|
|
1310
|
+
function createOrganization(data) {
|
|
1311
|
+
return {
|
|
1312
|
+
"@type": "Organization",
|
|
1313
|
+
...data
|
|
1314
|
+
};
|
|
1315
|
+
}
|
|
1316
|
+
function createWebSite(data) {
|
|
1317
|
+
const website = {
|
|
1318
|
+
"@type": "WebSite",
|
|
1319
|
+
name: data.name,
|
|
1320
|
+
url: data.url,
|
|
1321
|
+
description: data.description,
|
|
1322
|
+
publisher: data.publisher
|
|
1323
|
+
};
|
|
1324
|
+
if (data.searchUrlTemplate) {
|
|
1325
|
+
website.potentialAction = {
|
|
1326
|
+
"@type": "SearchAction",
|
|
1327
|
+
target: {
|
|
1328
|
+
"@type": "EntryPoint",
|
|
1329
|
+
urlTemplate: data.searchUrlTemplate
|
|
1330
|
+
},
|
|
1331
|
+
"query-input": "required name=search_term_string"
|
|
1332
|
+
};
|
|
1333
|
+
}
|
|
1334
|
+
return website;
|
|
1335
|
+
}
|
|
1336
|
+
function createProduct(data) {
|
|
1337
|
+
return {
|
|
1338
|
+
"@type": "Product",
|
|
1339
|
+
...data
|
|
1340
|
+
};
|
|
1341
|
+
}
|
|
1342
|
+
function createFAQPage(faqs) {
|
|
1343
|
+
return {
|
|
1344
|
+
"@type": "FAQPage",
|
|
1345
|
+
mainEntity: faqs.map(({ question, answer }) => ({
|
|
1346
|
+
"@type": "Question",
|
|
1347
|
+
name: question,
|
|
1348
|
+
acceptedAnswer: {
|
|
1349
|
+
"@type": "Answer",
|
|
1350
|
+
text: answer
|
|
1351
|
+
}
|
|
1352
|
+
}))
|
|
1353
|
+
};
|
|
1354
|
+
}
|
|
1355
|
+
function createArticle(data) {
|
|
1356
|
+
return {
|
|
1357
|
+
"@type": data.type || "Article",
|
|
1358
|
+
headline: data.headline,
|
|
1359
|
+
description: data.description,
|
|
1360
|
+
image: data.image,
|
|
1361
|
+
author: typeof data.author === "string" ? data.author : data.author,
|
|
1362
|
+
publisher: data.publisher,
|
|
1363
|
+
datePublished: data.datePublished,
|
|
1364
|
+
dateModified: data.dateModified,
|
|
1365
|
+
mainEntityOfPage: data.mainEntityOfPage
|
|
1366
|
+
};
|
|
1367
|
+
}
|
|
1368
|
+
function createBreadcrumbList(items) {
|
|
1369
|
+
return {
|
|
1370
|
+
"@type": "BreadcrumbList",
|
|
1371
|
+
itemListElement: items.map((item, index) => ({
|
|
1372
|
+
"@type": "ListItem",
|
|
1373
|
+
position: index + 1,
|
|
1374
|
+
name: item.name,
|
|
1375
|
+
item: item.url
|
|
1376
|
+
}))
|
|
1377
|
+
};
|
|
1378
|
+
}
|
|
1379
|
+
function serializeStructuredData(data) {
|
|
1380
|
+
const jsonLd = Array.isArray(data) ? { "@context": "https://schema.org", "@graph": data } : { "@context": "https://schema.org", ...data };
|
|
1381
|
+
return JSON.stringify(jsonLd);
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
// src/lib/seo/sitemap.ts
|
|
1385
|
+
function generateSitemap(config) {
|
|
1386
|
+
const { entries, prettyPrint = false } = config;
|
|
1387
|
+
const indent = prettyPrint ? " " : "";
|
|
1388
|
+
const newline = prettyPrint ? "\n" : "";
|
|
1389
|
+
const urlEntries = entries.map((entry) => {
|
|
1390
|
+
const parts = [];
|
|
1391
|
+
parts.push(`${indent}<url>${newline}`);
|
|
1392
|
+
parts.push(`${indent}${indent}<loc>${escapeXml(entry.url)}</loc>${newline}`);
|
|
1393
|
+
if (entry.lastModified) {
|
|
1394
|
+
const date = entry.lastModified instanceof Date ? entry.lastModified.toISOString() : entry.lastModified;
|
|
1395
|
+
parts.push(`${indent}${indent}<lastmod>${date}</lastmod>${newline}`);
|
|
1396
|
+
}
|
|
1397
|
+
if (entry.changeFrequency) {
|
|
1398
|
+
parts.push(`${indent}${indent}<changefreq>${entry.changeFrequency}</changefreq>${newline}`);
|
|
1399
|
+
}
|
|
1400
|
+
if (entry.priority !== void 0) {
|
|
1401
|
+
const priority = Math.max(0, Math.min(1, entry.priority)).toFixed(1);
|
|
1402
|
+
parts.push(`${indent}${indent}<priority>${priority}</priority>${newline}`);
|
|
1403
|
+
}
|
|
1404
|
+
if (entry.alternates && entry.alternates.length > 0) {
|
|
1405
|
+
entry.alternates.forEach((alternate) => {
|
|
1406
|
+
parts.push(
|
|
1407
|
+
`${indent}${indent}<xhtml:link rel="alternate" hreflang="${escapeXml(
|
|
1408
|
+
alternate.hreflang
|
|
1409
|
+
)}" href="${escapeXml(alternate.href)}" />${newline}`
|
|
1410
|
+
);
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1413
|
+
parts.push(`${indent}</url>${newline}`);
|
|
1414
|
+
return parts.join("");
|
|
1415
|
+
}).join("");
|
|
1416
|
+
const xml = [
|
|
1417
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
1418
|
+
newline,
|
|
1419
|
+
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">',
|
|
1420
|
+
newline,
|
|
1421
|
+
urlEntries,
|
|
1422
|
+
"</urlset>"
|
|
1423
|
+
].join("");
|
|
1424
|
+
return xml;
|
|
1425
|
+
}
|
|
1426
|
+
function createMultiLanguageEntries(baseUrl, path2, locales2, defaultLocale2, options) {
|
|
1427
|
+
const cleanPath = path2.startsWith("/") ? path2 : `/${path2}`;
|
|
1428
|
+
return locales2.map((locale) => {
|
|
1429
|
+
const alternates = locales2.map((altLocale) => ({
|
|
1430
|
+
hreflang: altLocale,
|
|
1431
|
+
href: `${baseUrl}/${altLocale}${cleanPath}`
|
|
1432
|
+
}));
|
|
1433
|
+
if (defaultLocale2) {
|
|
1434
|
+
alternates.push({
|
|
1435
|
+
hreflang: "x-default",
|
|
1436
|
+
href: `${baseUrl}/${defaultLocale2}${cleanPath}`
|
|
1437
|
+
});
|
|
1438
|
+
}
|
|
1439
|
+
return {
|
|
1440
|
+
url: `${baseUrl}/${locale}${cleanPath}`,
|
|
1441
|
+
alternates,
|
|
1442
|
+
...options
|
|
1443
|
+
};
|
|
1444
|
+
});
|
|
1445
|
+
}
|
|
1446
|
+
function createSitemapEntry(baseUrl, path2, options) {
|
|
1447
|
+
const cleanPath = path2.startsWith("/") ? path2 : `/${path2}`;
|
|
1448
|
+
const url = path2 === "/" ? baseUrl : `${baseUrl}${cleanPath}`;
|
|
1449
|
+
return {
|
|
1450
|
+
url,
|
|
1451
|
+
...options
|
|
1452
|
+
};
|
|
1453
|
+
}
|
|
1454
|
+
function escapeXml(unsafe) {
|
|
1455
|
+
return unsafe.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1456
|
+
}
|
|
1457
|
+
function validateSitemapEntry(entry) {
|
|
1458
|
+
const errors = [];
|
|
1459
|
+
if (!entry.url.startsWith("http://") && !entry.url.startsWith("https://")) {
|
|
1460
|
+
errors.push(`URL must be absolute: ${entry.url}`);
|
|
1461
|
+
}
|
|
1462
|
+
if (entry.priority !== void 0 && (entry.priority < 0 || entry.priority > 1)) {
|
|
1463
|
+
errors.push(`Priority must be between 0 and 1: ${entry.priority}`);
|
|
1464
|
+
}
|
|
1465
|
+
if (entry.alternates) {
|
|
1466
|
+
entry.alternates.forEach((alt, index) => {
|
|
1467
|
+
if (!alt.href.startsWith("http://") && !alt.href.startsWith("https://")) {
|
|
1468
|
+
errors.push(`Alternate ${index} href must be absolute: ${alt.href}`);
|
|
1469
|
+
}
|
|
1470
|
+
});
|
|
1471
|
+
}
|
|
1472
|
+
return errors;
|
|
1473
|
+
}
|
|
1474
|
+
function validateSitemap(config) {
|
|
1475
|
+
const errors = [];
|
|
1476
|
+
if (!config.baseUrl.startsWith("http://") && !config.baseUrl.startsWith("https://")) {
|
|
1477
|
+
errors.push({ messages: ["Base URL must be absolute"] });
|
|
1478
|
+
}
|
|
1479
|
+
config.entries.forEach((entry) => {
|
|
1480
|
+
const entryErrors = validateSitemapEntry(entry);
|
|
1481
|
+
if (entryErrors.length > 0) {
|
|
1482
|
+
errors.push({ entry, messages: entryErrors });
|
|
1483
|
+
}
|
|
1484
|
+
});
|
|
1485
|
+
return {
|
|
1486
|
+
isValid: errors.length === 0,
|
|
1487
|
+
errors
|
|
1488
|
+
};
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
// src/components/TrackedLink.tsx
|
|
1492
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1493
|
+
function TrackedLink({
|
|
1494
|
+
href,
|
|
1495
|
+
children,
|
|
1496
|
+
className,
|
|
1497
|
+
trackingType = "auto",
|
|
1498
|
+
ctaLocation,
|
|
1499
|
+
ctaType = "other",
|
|
1500
|
+
navLocation = "content",
|
|
1501
|
+
onClick,
|
|
1502
|
+
...props
|
|
1503
|
+
}) {
|
|
1504
|
+
const handleClick = () => {
|
|
1505
|
+
const linkText = typeof children === "string" ? children : href;
|
|
1506
|
+
let type = trackingType;
|
|
1507
|
+
if (type === "auto") {
|
|
1508
|
+
if (href.startsWith("http") && !href.includes("courrielleur.com")) {
|
|
1509
|
+
type = "outbound";
|
|
1510
|
+
} else if (href.includes("signup") || href.includes("trial")) {
|
|
1511
|
+
type = "cta";
|
|
1512
|
+
} else {
|
|
1513
|
+
type = "navigation";
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
switch (type) {
|
|
1517
|
+
case "cta":
|
|
1518
|
+
trackCTAClick(ctaLocation || "unknown", linkText, ctaType);
|
|
1519
|
+
break;
|
|
1520
|
+
case "outbound":
|
|
1521
|
+
trackOutboundLink(href, linkText);
|
|
1522
|
+
break;
|
|
1523
|
+
case "navigation":
|
|
1524
|
+
trackNavigation(linkText, href, navLocation);
|
|
1525
|
+
break;
|
|
1526
|
+
}
|
|
1527
|
+
onClick?.();
|
|
1528
|
+
};
|
|
1529
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("a", { href, className, onClick: handleClick, ...props, children });
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
// src/components/FeaturesGrid.tsx
|
|
1533
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1534
|
+
var labels = {
|
|
1535
|
+
keyBenefits: {
|
|
1536
|
+
fr: "Avantages cl\xE9s :",
|
|
1537
|
+
en: "Key Benefits:"
|
|
1538
|
+
},
|
|
1539
|
+
useCases: {
|
|
1540
|
+
fr: "Cas d'usage :",
|
|
1541
|
+
en: "Use Cases:"
|
|
1542
|
+
},
|
|
1543
|
+
learnMore: {
|
|
1544
|
+
fr: "En savoir plus",
|
|
1545
|
+
en: "Learn more"
|
|
1546
|
+
}
|
|
1547
|
+
};
|
|
1548
|
+
function FeaturesGrid({ categories, className = "", locale = "en" }) {
|
|
1549
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: `py-20 ${className}`, children: categories.map((category, categoryIndex) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1550
|
+
"section",
|
|
1551
|
+
{
|
|
1552
|
+
className: `${categoryIndex % 2 === 0 ? "bg-white" : "bg-warm-gray"} py-16`,
|
|
1553
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "container mx-auto px-6", children: [
|
|
1554
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "max-w-3xl mb-12", children: [
|
|
1555
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { className: "text-3xl md:text-4xl font-bold text-charcoal mb-4", children: category.name }),
|
|
1556
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-lg text-charcoal/80", children: category.description })
|
|
1557
|
+
] }),
|
|
1558
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8", children: category.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1559
|
+
"div",
|
|
1560
|
+
{
|
|
1561
|
+
className: "bg-white rounded-lg p-6 shadow-sm hover:shadow-md transition-shadow duration-200",
|
|
1562
|
+
children: [
|
|
1563
|
+
feature.icon && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "w-12 h-12 mb-4 text-primary", children: feature.icon }),
|
|
1564
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { className: "text-xl font-semibold text-charcoal mb-3", children: feature.name }),
|
|
1565
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-charcoal/80 mb-4", children: feature.description }),
|
|
1566
|
+
feature.benefits && feature.benefits.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mb-4", children: [
|
|
1567
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-sm font-semibold text-charcoal/60 mb-2", children: labels.keyBenefits[locale] }),
|
|
1568
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { className: "space-y-1", children: feature.benefits.map((benefit, idx) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1569
|
+
"li",
|
|
1570
|
+
{
|
|
1571
|
+
className: "text-sm text-charcoal/70 flex items-start gap-2",
|
|
1572
|
+
children: [
|
|
1573
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-primary mt-1", children: "\u2713" }),
|
|
1574
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: benefit })
|
|
1575
|
+
]
|
|
1576
|
+
},
|
|
1577
|
+
idx
|
|
1578
|
+
)) })
|
|
1579
|
+
] }),
|
|
1580
|
+
feature.useCases && feature.useCases.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mb-4", children: [
|
|
1581
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-sm font-semibold text-charcoal/60 mb-2", children: labels.useCases[locale] }),
|
|
1582
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { className: "space-y-1", children: feature.useCases.map((useCase, idx) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1583
|
+
"li",
|
|
1584
|
+
{
|
|
1585
|
+
className: "text-sm text-charcoal/70 flex items-start gap-2",
|
|
1586
|
+
children: [
|
|
1587
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-charcoal/40", children: "\u2022" }),
|
|
1588
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: useCase })
|
|
1589
|
+
]
|
|
1590
|
+
},
|
|
1591
|
+
idx
|
|
1592
|
+
)) })
|
|
1593
|
+
] }),
|
|
1594
|
+
feature.learnMoreHref && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1595
|
+
"a",
|
|
1596
|
+
{
|
|
1597
|
+
href: feature.learnMoreHref,
|
|
1598
|
+
className: "text-primary hover:text-primary-hover text-sm font-medium inline-flex items-center gap-1 group",
|
|
1599
|
+
children: [
|
|
1600
|
+
labels.learnMore[locale],
|
|
1601
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "group-hover:translate-x-1 transition-transform duration-200", children: "\u2192" })
|
|
1602
|
+
]
|
|
1603
|
+
}
|
|
1604
|
+
)
|
|
1605
|
+
]
|
|
1606
|
+
},
|
|
1607
|
+
feature.id
|
|
1608
|
+
)) })
|
|
1609
|
+
] })
|
|
1610
|
+
},
|
|
1611
|
+
category.id
|
|
1612
|
+
)) });
|
|
1613
|
+
}
|
|
1614
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1615
|
+
0 && (module.exports = {
|
|
1616
|
+
FeaturesGrid,
|
|
1617
|
+
LOCALE_COOKIE_NAME,
|
|
1618
|
+
TrackedLink,
|
|
1619
|
+
cn,
|
|
1620
|
+
createArticle,
|
|
1621
|
+
createBreadcrumbList,
|
|
1622
|
+
createFAQPage,
|
|
1623
|
+
createI18nMiddleware,
|
|
1624
|
+
createMultiLanguageEntries,
|
|
1625
|
+
createOrganization,
|
|
1626
|
+
createProduct,
|
|
1627
|
+
createSitemapEntry,
|
|
1628
|
+
createWebSite,
|
|
1629
|
+
creditCardSchema,
|
|
1630
|
+
dateString,
|
|
1631
|
+
defaultLocale,
|
|
1632
|
+
defaultSlugTranslations,
|
|
1633
|
+
emailSchema,
|
|
1634
|
+
fileSchema,
|
|
1635
|
+
formErrorMessages,
|
|
1636
|
+
formatCurrency,
|
|
1637
|
+
formatDate,
|
|
1638
|
+
formatDateRange,
|
|
1639
|
+
formatFileSize,
|
|
1640
|
+
formatList,
|
|
1641
|
+
formatLocaleDisplay,
|
|
1642
|
+
formatNumber,
|
|
1643
|
+
formatRelativeTime,
|
|
1644
|
+
generateArticleMetadata,
|
|
1645
|
+
generateDesignTokens,
|
|
1646
|
+
generateMetadata,
|
|
1647
|
+
generateSitemap,
|
|
1648
|
+
generateThemeCSS,
|
|
1649
|
+
getAllPolicies,
|
|
1650
|
+
getAlternateLocales,
|
|
1651
|
+
getDefaultLocale,
|
|
1652
|
+
getErrorMessage,
|
|
1653
|
+
getFontConfig,
|
|
1654
|
+
getFontVariables,
|
|
1655
|
+
getI18nConfig,
|
|
1656
|
+
getLocaleAutonym,
|
|
1657
|
+
getLocaleCookieConfig,
|
|
1658
|
+
getLocaleFromCookie,
|
|
1659
|
+
getLocaleLabel,
|
|
1660
|
+
getLocaleLabels,
|
|
1661
|
+
getLocaleName,
|
|
1662
|
+
getLocaleNames,
|
|
1663
|
+
getLocalePrefix,
|
|
1664
|
+
getLocales,
|
|
1665
|
+
getLocalizedString,
|
|
1666
|
+
getNavigationString,
|
|
1667
|
+
getPolicyLocales,
|
|
1668
|
+
getPolicySlugs,
|
|
1669
|
+
getRelativeTime,
|
|
1670
|
+
getRtlLocales,
|
|
1671
|
+
getTextDirection,
|
|
1672
|
+
imageSchema,
|
|
1673
|
+
isI18nConfigInitialized,
|
|
1674
|
+
isLocaleDetectionEnabled,
|
|
1675
|
+
isRtlLocale,
|
|
1676
|
+
isSupportedLocale,
|
|
1677
|
+
loadPolicy,
|
|
1678
|
+
locales,
|
|
1679
|
+
matchLocale,
|
|
1680
|
+
mustBeTrue,
|
|
1681
|
+
normalizeLocale,
|
|
1682
|
+
numericString,
|
|
1683
|
+
optionalString,
|
|
1684
|
+
passwordSchema,
|
|
1685
|
+
phoneSchema,
|
|
1686
|
+
postalCodeSchema,
|
|
1687
|
+
replaceVariables,
|
|
1688
|
+
requiredString,
|
|
1689
|
+
serializeStructuredData,
|
|
1690
|
+
setI18nConfig,
|
|
1691
|
+
setLocaleCookie,
|
|
1692
|
+
trackABTestEvent,
|
|
1693
|
+
trackCTAClick,
|
|
1694
|
+
trackConversion,
|
|
1695
|
+
trackError,
|
|
1696
|
+
trackEvent,
|
|
1697
|
+
trackFeatureEngagement,
|
|
1698
|
+
trackFormEvent,
|
|
1699
|
+
trackNavigation,
|
|
1700
|
+
trackOutboundLink,
|
|
1701
|
+
trackPageView,
|
|
1702
|
+
trackPricingEvent,
|
|
1703
|
+
trackResourceDownload,
|
|
1704
|
+
trackScrollDepth,
|
|
1705
|
+
trackSearch,
|
|
1706
|
+
trackVideoEvent,
|
|
1707
|
+
translateSlug,
|
|
1708
|
+
urlSchema,
|
|
1709
|
+
validateLocale,
|
|
1710
|
+
validateSitemap,
|
|
1711
|
+
validateSitemapEntry
|
|
1712
|
+
});
|
|
1713
|
+
//# sourceMappingURL=index.js.map
|