@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,621 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __esm = (fn, res) => function __init() {
|
|
6
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
7
|
+
};
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
|
+
|
|
22
|
+
// src/lib/i18n/config.ts
|
|
23
|
+
var config_exports = {};
|
|
24
|
+
__export(config_exports, {
|
|
25
|
+
__resetI18nConfig__: () => __resetI18nConfig__,
|
|
26
|
+
defaultLocale: () => defaultLocale,
|
|
27
|
+
getDefaultLocale: () => getDefaultLocale,
|
|
28
|
+
getI18nConfig: () => getI18nConfig,
|
|
29
|
+
getLocaleCookieConfig: () => getLocaleCookieConfig,
|
|
30
|
+
getLocaleLabel: () => getLocaleLabel,
|
|
31
|
+
getLocaleLabels: () => getLocaleLabels,
|
|
32
|
+
getLocaleName: () => getLocaleName,
|
|
33
|
+
getLocaleNames: () => getLocaleNames,
|
|
34
|
+
getLocalePrefix: () => getLocalePrefix,
|
|
35
|
+
getLocales: () => getLocales,
|
|
36
|
+
getRtlLocales: () => getRtlLocales,
|
|
37
|
+
isI18nConfigInitialized: () => isI18nConfigInitialized,
|
|
38
|
+
isLocaleDetectionEnabled: () => isLocaleDetectionEnabled,
|
|
39
|
+
isSupportedLocale: () => isSupportedLocale,
|
|
40
|
+
localeLabels: () => localeLabels,
|
|
41
|
+
localeNames: () => localeNames,
|
|
42
|
+
locales: () => locales,
|
|
43
|
+
setI18nConfig: () => setI18nConfig
|
|
44
|
+
});
|
|
45
|
+
function setI18nConfig(config) {
|
|
46
|
+
if (!config.locales || config.locales.length === 0) {
|
|
47
|
+
throw new Error("i18n config must define at least one locale");
|
|
48
|
+
}
|
|
49
|
+
if (!config.defaultLocale) {
|
|
50
|
+
throw new Error("i18n config must define a default locale");
|
|
51
|
+
}
|
|
52
|
+
if (!config.locales.includes(config.defaultLocale)) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
`Default locale "${config.defaultLocale}" must be included in locales array: [${config.locales.join(", ")}]`
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
const configWithDefaults = {
|
|
58
|
+
...config,
|
|
59
|
+
localePrefix: config.localePrefix || "as-needed",
|
|
60
|
+
localeDetection: config.localeDetection !== false,
|
|
61
|
+
// Default to true
|
|
62
|
+
localeNames: config.localeNames || {},
|
|
63
|
+
localeLabels: config.localeLabels || {},
|
|
64
|
+
rtlLocales: config.rtlLocales || [],
|
|
65
|
+
localeCookie: {
|
|
66
|
+
name: config.localeCookie?.name || "NEXT_LOCALE",
|
|
67
|
+
maxAge: config.localeCookie?.maxAge || 365 * 24 * 60 * 60,
|
|
68
|
+
// 1 year
|
|
69
|
+
sameSite: config.localeCookie?.sameSite || "lax"
|
|
70
|
+
},
|
|
71
|
+
slugTranslations: config.slugTranslations || {}
|
|
72
|
+
};
|
|
73
|
+
globalI18nConfig = configWithDefaults;
|
|
74
|
+
}
|
|
75
|
+
function getI18nConfig() {
|
|
76
|
+
if (!globalI18nConfig) {
|
|
77
|
+
if (process.env.NODE_ENV !== "production") {
|
|
78
|
+
console.warn(
|
|
79
|
+
"\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."
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
locales: ["fr", "en"],
|
|
84
|
+
defaultLocale: "fr",
|
|
85
|
+
localePrefix: "as-needed",
|
|
86
|
+
localeDetection: true,
|
|
87
|
+
localeNames: { fr: "Fran\xE7ais", en: "English" },
|
|
88
|
+
localeLabels: { fr: "FR", en: "EN" },
|
|
89
|
+
rtlLocales: [],
|
|
90
|
+
localeCookie: {
|
|
91
|
+
name: "NEXT_LOCALE",
|
|
92
|
+
maxAge: 365 * 24 * 60 * 60,
|
|
93
|
+
sameSite: "lax"
|
|
94
|
+
},
|
|
95
|
+
slugTranslations: {}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
return globalI18nConfig;
|
|
99
|
+
}
|
|
100
|
+
function isI18nConfigInitialized() {
|
|
101
|
+
return globalI18nConfig !== null;
|
|
102
|
+
}
|
|
103
|
+
function __resetI18nConfig__() {
|
|
104
|
+
globalI18nConfig = null;
|
|
105
|
+
}
|
|
106
|
+
function getLocales() {
|
|
107
|
+
return getI18nConfig().locales;
|
|
108
|
+
}
|
|
109
|
+
function getDefaultLocale() {
|
|
110
|
+
return getI18nConfig().defaultLocale;
|
|
111
|
+
}
|
|
112
|
+
function getLocalePrefix() {
|
|
113
|
+
return getI18nConfig().localePrefix || "as-needed";
|
|
114
|
+
}
|
|
115
|
+
function getLocaleNames() {
|
|
116
|
+
return getI18nConfig().localeNames || {};
|
|
117
|
+
}
|
|
118
|
+
function getLocaleLabels() {
|
|
119
|
+
return getI18nConfig().localeLabels || {};
|
|
120
|
+
}
|
|
121
|
+
function getRtlLocales() {
|
|
122
|
+
return getI18nConfig().rtlLocales || [];
|
|
123
|
+
}
|
|
124
|
+
function isLocaleDetectionEnabled() {
|
|
125
|
+
return getI18nConfig().localeDetection !== false;
|
|
126
|
+
}
|
|
127
|
+
function getLocaleCookieConfig() {
|
|
128
|
+
const config = getI18nConfig();
|
|
129
|
+
return {
|
|
130
|
+
name: config.localeCookie?.name || "NEXT_LOCALE",
|
|
131
|
+
maxAge: config.localeCookie?.maxAge || 365 * 24 * 60 * 60,
|
|
132
|
+
sameSite: config.localeCookie?.sameSite || "lax"
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function isSupportedLocale(locale) {
|
|
136
|
+
return getLocales().includes(locale);
|
|
137
|
+
}
|
|
138
|
+
function getLocaleName(locale) {
|
|
139
|
+
const names = getLocaleNames();
|
|
140
|
+
return names[locale] || locale;
|
|
141
|
+
}
|
|
142
|
+
function getLocaleLabel(locale) {
|
|
143
|
+
const labels = getLocaleLabels();
|
|
144
|
+
return labels[locale] || locale.toUpperCase();
|
|
145
|
+
}
|
|
146
|
+
var globalI18nConfig, locales, defaultLocale, localeNames, localeLabels;
|
|
147
|
+
var init_config = __esm({
|
|
148
|
+
"src/lib/i18n/config.ts"() {
|
|
149
|
+
"use strict";
|
|
150
|
+
globalI18nConfig = null;
|
|
151
|
+
locales = ["fr", "en"];
|
|
152
|
+
defaultLocale = "fr";
|
|
153
|
+
localeNames = {
|
|
154
|
+
fr: "Fran\xE7ais",
|
|
155
|
+
en: "English"
|
|
156
|
+
};
|
|
157
|
+
localeLabels = {
|
|
158
|
+
fr: "FR",
|
|
159
|
+
en: "EN"
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// src/lib/i18n/index.ts
|
|
165
|
+
init_config();
|
|
166
|
+
init_config();
|
|
167
|
+
|
|
168
|
+
// src/lib/i18n/locale-cookie.ts
|
|
169
|
+
init_config();
|
|
170
|
+
var LOCALE_COOKIE_NAME = "NEXT_LOCALE";
|
|
171
|
+
function getLocaleFromCookie() {
|
|
172
|
+
if (typeof document === "undefined") return null;
|
|
173
|
+
try {
|
|
174
|
+
const cookieConfig = getLocaleCookieConfig();
|
|
175
|
+
const cookieName = cookieConfig.name;
|
|
176
|
+
const cookie = document.cookie.split("; ").find((row) => row.startsWith(`${cookieName}=`));
|
|
177
|
+
if (!cookie) return null;
|
|
178
|
+
const value = cookie.split("=")[1];
|
|
179
|
+
if (isSupportedLocale(value)) {
|
|
180
|
+
return value;
|
|
181
|
+
}
|
|
182
|
+
return null;
|
|
183
|
+
} catch (error) {
|
|
184
|
+
if (process.env.NODE_ENV !== "production") {
|
|
185
|
+
console.warn("getLocaleFromCookie: i18n config not initialized");
|
|
186
|
+
}
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function setLocaleCookie(locale) {
|
|
191
|
+
if (typeof document === "undefined") return;
|
|
192
|
+
try {
|
|
193
|
+
if (!isSupportedLocale(locale)) {
|
|
194
|
+
console.warn(`Attempted to set unsupported locale: ${locale}`);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const cookieConfig = getLocaleCookieConfig();
|
|
198
|
+
document.cookie = `${cookieConfig.name}=${locale}; max-age=${cookieConfig.maxAge}; path=/; SameSite=${cookieConfig.sameSite}`;
|
|
199
|
+
} catch (error) {
|
|
200
|
+
if (process.env.NODE_ENV !== "production") {
|
|
201
|
+
console.warn("setLocaleCookie: i18n config not initialized");
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// src/lib/i18n/slug-translations.ts
|
|
207
|
+
var defaultSlugTranslations = {
|
|
208
|
+
fr: {
|
|
209
|
+
"/marketing-par-courriel": "/email-marketing",
|
|
210
|
+
"/courriels-transactionnels": "/transactional-emails",
|
|
211
|
+
"/forfaits": "/pricing",
|
|
212
|
+
"/nous-contacter": "/contact",
|
|
213
|
+
"/a-propos": "/about",
|
|
214
|
+
"/politique-de-confidentialite": "/privacy-policy",
|
|
215
|
+
"/conditions-dutilisation": "/terms-of-service",
|
|
216
|
+
"/politique-anti-pourriel": "/anti-spam-policy"
|
|
217
|
+
},
|
|
218
|
+
en: {
|
|
219
|
+
"/email-marketing": "/marketing-par-courriel",
|
|
220
|
+
"/transactional-emails": "/courriels-transactionnels",
|
|
221
|
+
"/pricing": "/forfaits",
|
|
222
|
+
"/contact": "/nous-contacter",
|
|
223
|
+
"/about": "/a-propos",
|
|
224
|
+
"/privacy-policy": "/politique-de-confidentialite",
|
|
225
|
+
"/terms-of-service": "/conditions-dutilisation",
|
|
226
|
+
"/anti-spam-policy": "/politique-anti-pourriel"
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
function translateSlug(path, fromLocale, toLocale, customTranslations) {
|
|
230
|
+
let configTranslations = {};
|
|
231
|
+
try {
|
|
232
|
+
const { getI18nConfig: getI18nConfig2 } = (init_config(), __toCommonJS(config_exports));
|
|
233
|
+
const config = getI18nConfig2();
|
|
234
|
+
configTranslations = config.slugTranslations || {};
|
|
235
|
+
} catch {
|
|
236
|
+
}
|
|
237
|
+
const translations = mergeTranslations(
|
|
238
|
+
defaultSlugTranslations,
|
|
239
|
+
configTranslations,
|
|
240
|
+
customTranslations || {}
|
|
241
|
+
);
|
|
242
|
+
const translationMap = translations[fromLocale];
|
|
243
|
+
if (!translationMap) {
|
|
244
|
+
return path;
|
|
245
|
+
}
|
|
246
|
+
if (translationMap[path]) {
|
|
247
|
+
return translationMap[path];
|
|
248
|
+
}
|
|
249
|
+
for (const [fromSlug, toSlug] of Object.entries(translationMap)) {
|
|
250
|
+
if (path.startsWith(fromSlug + "/")) {
|
|
251
|
+
return path.replace(fromSlug, toSlug);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return path;
|
|
255
|
+
}
|
|
256
|
+
function mergeTranslations(...translations) {
|
|
257
|
+
const merged = {};
|
|
258
|
+
for (const trans of translations) {
|
|
259
|
+
for (const [locale, slugs] of Object.entries(trans)) {
|
|
260
|
+
if (!merged[locale]) {
|
|
261
|
+
merged[locale] = {};
|
|
262
|
+
}
|
|
263
|
+
Object.assign(merged[locale], slugs);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return merged;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// src/lib/i18n/formatters.ts
|
|
270
|
+
function formatDate(date, locale, options) {
|
|
271
|
+
return new Intl.DateTimeFormat(locale, options).format(date);
|
|
272
|
+
}
|
|
273
|
+
function formatNumber(value, locale, options) {
|
|
274
|
+
return new Intl.NumberFormat(locale, options).format(value);
|
|
275
|
+
}
|
|
276
|
+
function formatCurrency(amount, locale, currency, options) {
|
|
277
|
+
return new Intl.NumberFormat(locale, {
|
|
278
|
+
style: "currency",
|
|
279
|
+
currency,
|
|
280
|
+
...options
|
|
281
|
+
}).format(amount);
|
|
282
|
+
}
|
|
283
|
+
function formatRelativeTime(value, unit, locale, options) {
|
|
284
|
+
return new Intl.RelativeTimeFormat(locale, options).format(value, unit);
|
|
285
|
+
}
|
|
286
|
+
function formatDateRange(startDate, endDate, locale, options) {
|
|
287
|
+
const formatter = new Intl.DateTimeFormat(locale, options);
|
|
288
|
+
if ("formatRange" in formatter) {
|
|
289
|
+
return formatter.formatRange(startDate, endDate);
|
|
290
|
+
}
|
|
291
|
+
return `${formatter.format(startDate)} \u2013 ${formatter.format(endDate)}`;
|
|
292
|
+
}
|
|
293
|
+
function formatList(items, locale) {
|
|
294
|
+
if (typeof Intl !== "undefined" && "ListFormat" in Intl) {
|
|
295
|
+
return new Intl.ListFormat(locale).format(items);
|
|
296
|
+
}
|
|
297
|
+
if (items.length === 0) return "";
|
|
298
|
+
if (items.length === 1) return items[0];
|
|
299
|
+
if (items.length === 2) {
|
|
300
|
+
const connector2 = locale === "fr" ? " et " : " and ";
|
|
301
|
+
return items.join(connector2);
|
|
302
|
+
}
|
|
303
|
+
const last = items[items.length - 1];
|
|
304
|
+
const rest = items.slice(0, -1);
|
|
305
|
+
const connector = locale === "fr" ? " et " : ", and ";
|
|
306
|
+
return rest.join(", ") + connector + last;
|
|
307
|
+
}
|
|
308
|
+
function formatFileSize(bytes, locale, decimals = 2) {
|
|
309
|
+
if (bytes === 0) return "0 B";
|
|
310
|
+
const k = 1024;
|
|
311
|
+
const sizes = ["B", "KB", "MB", "GB", "TB", "PB"];
|
|
312
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
313
|
+
const value = bytes / Math.pow(k, i);
|
|
314
|
+
return `${formatNumber(value, locale, {
|
|
315
|
+
minimumFractionDigits: decimals,
|
|
316
|
+
maximumFractionDigits: decimals
|
|
317
|
+
})} ${sizes[i]}`;
|
|
318
|
+
}
|
|
319
|
+
function getRelativeTime(date, locale, options) {
|
|
320
|
+
const now = /* @__PURE__ */ new Date();
|
|
321
|
+
const diffMs = date.getTime() - now.getTime();
|
|
322
|
+
const diffSecs = Math.round(diffMs / 1e3);
|
|
323
|
+
const diffMins = Math.round(diffSecs / 60);
|
|
324
|
+
const diffHours = Math.round(diffMins / 60);
|
|
325
|
+
const diffDays = Math.round(diffHours / 24);
|
|
326
|
+
const diffWeeks = Math.round(diffDays / 7);
|
|
327
|
+
const diffMonths = Math.round(diffDays / 30);
|
|
328
|
+
const diffYears = Math.round(diffDays / 365);
|
|
329
|
+
if (Math.abs(diffSecs) < 60) {
|
|
330
|
+
return formatRelativeTime(diffSecs, "second", locale, options);
|
|
331
|
+
} else if (Math.abs(diffMins) < 60) {
|
|
332
|
+
return formatRelativeTime(diffMins, "minute", locale, options);
|
|
333
|
+
} else if (Math.abs(diffHours) < 24) {
|
|
334
|
+
return formatRelativeTime(diffHours, "hour", locale, options);
|
|
335
|
+
} else if (Math.abs(diffDays) < 7) {
|
|
336
|
+
return formatRelativeTime(diffDays, "day", locale, options);
|
|
337
|
+
} else if (Math.abs(diffWeeks) < 4) {
|
|
338
|
+
return formatRelativeTime(diffWeeks, "week", locale, options);
|
|
339
|
+
} else if (Math.abs(diffMonths) < 12) {
|
|
340
|
+
return formatRelativeTime(diffMonths, "month", locale, options);
|
|
341
|
+
} else {
|
|
342
|
+
return formatRelativeTime(diffYears, "year", locale, options);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// src/lib/i18n/middleware.ts
|
|
347
|
+
import { NextResponse } from "next/server";
|
|
348
|
+
function createI18nMiddleware(config) {
|
|
349
|
+
return function middleware(request) {
|
|
350
|
+
const { pathname } = request.nextUrl;
|
|
351
|
+
const pathnameLocale = getLocaleFromPathname(pathname, config);
|
|
352
|
+
if (pathnameLocale) {
|
|
353
|
+
if (!config.locales.includes(pathnameLocale)) {
|
|
354
|
+
return redirectToLocale(request, config.defaultLocale, pathname, config);
|
|
355
|
+
}
|
|
356
|
+
const response = NextResponse.next();
|
|
357
|
+
setLocaleCookie2(response, pathnameLocale, config);
|
|
358
|
+
return response;
|
|
359
|
+
}
|
|
360
|
+
const detectedLocale = detectLocale(request, config);
|
|
361
|
+
const prefixMode = config.localePrefix || "as-needed";
|
|
362
|
+
if (prefixMode === "always") {
|
|
363
|
+
return redirectToLocale(request, detectedLocale, pathname, config);
|
|
364
|
+
} else if (prefixMode === "as-needed") {
|
|
365
|
+
if (detectedLocale !== config.defaultLocale) {
|
|
366
|
+
return redirectToLocale(request, detectedLocale, pathname, config);
|
|
367
|
+
}
|
|
368
|
+
const response = NextResponse.next();
|
|
369
|
+
setLocaleCookie2(response, config.defaultLocale, config);
|
|
370
|
+
return response;
|
|
371
|
+
} else {
|
|
372
|
+
const response = NextResponse.next();
|
|
373
|
+
setLocaleCookie2(response, detectedLocale, config);
|
|
374
|
+
return response;
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
function getLocaleFromPathname(pathname, config) {
|
|
379
|
+
const segments = pathname.split("/").filter(Boolean);
|
|
380
|
+
if (segments.length === 0) {
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
const firstSegment = segments[0];
|
|
384
|
+
if (config.locales.includes(firstSegment)) {
|
|
385
|
+
return firstSegment;
|
|
386
|
+
}
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
function detectLocale(request, config) {
|
|
390
|
+
const cookieName = config.localeCookie?.name || "NEXT_LOCALE";
|
|
391
|
+
const cookieLocale = request.cookies.get(cookieName)?.value;
|
|
392
|
+
if (cookieLocale && config.locales.includes(cookieLocale)) {
|
|
393
|
+
return cookieLocale;
|
|
394
|
+
}
|
|
395
|
+
if (config.localeDetection !== false) {
|
|
396
|
+
const acceptLanguage = request.headers.get("accept-language");
|
|
397
|
+
const browserLocale = negotiateLanguage(acceptLanguage, config.locales);
|
|
398
|
+
if (browserLocale) {
|
|
399
|
+
return browserLocale;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return config.defaultLocale;
|
|
403
|
+
}
|
|
404
|
+
function negotiateLanguage(acceptLanguage, supportedLocales) {
|
|
405
|
+
if (!acceptLanguage) {
|
|
406
|
+
return null;
|
|
407
|
+
}
|
|
408
|
+
const preferences = parseAcceptLanguage(acceptLanguage);
|
|
409
|
+
for (const pref of preferences) {
|
|
410
|
+
if (supportedLocales.includes(pref.locale)) {
|
|
411
|
+
return pref.locale;
|
|
412
|
+
}
|
|
413
|
+
const languageCode = pref.locale.split("-")[0];
|
|
414
|
+
if (supportedLocales.includes(languageCode)) {
|
|
415
|
+
return languageCode;
|
|
416
|
+
}
|
|
417
|
+
const match = supportedLocales.find(
|
|
418
|
+
(locale) => locale.startsWith(languageCode + "-") || locale === languageCode
|
|
419
|
+
);
|
|
420
|
+
if (match) {
|
|
421
|
+
return match;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
426
|
+
function parseAcceptLanguage(header) {
|
|
427
|
+
const preferences = [];
|
|
428
|
+
const parts = header.split(",").map((p) => p.trim());
|
|
429
|
+
for (const part of parts) {
|
|
430
|
+
const [locale, ...rest] = part.split(";").map((p) => p.trim());
|
|
431
|
+
let quality = 1;
|
|
432
|
+
const qParam = rest.find((p) => p.startsWith("q="));
|
|
433
|
+
if (qParam) {
|
|
434
|
+
const qValue = parseFloat(qParam.substring(2));
|
|
435
|
+
if (!isNaN(qValue)) {
|
|
436
|
+
quality = qValue;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
preferences.push({
|
|
440
|
+
locale: locale.toLowerCase(),
|
|
441
|
+
quality
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
preferences.sort((a, b) => b.quality - a.quality);
|
|
445
|
+
return preferences;
|
|
446
|
+
}
|
|
447
|
+
function redirectToLocale(request, locale, pathname, config) {
|
|
448
|
+
const url = request.nextUrl.clone();
|
|
449
|
+
const pathWithoutLocale = removeLocalePrefix(pathname, config);
|
|
450
|
+
url.pathname = `/${locale}${pathWithoutLocale}`;
|
|
451
|
+
const response = NextResponse.redirect(url);
|
|
452
|
+
setLocaleCookie2(response, locale, config);
|
|
453
|
+
return response;
|
|
454
|
+
}
|
|
455
|
+
function removeLocalePrefix(pathname, config) {
|
|
456
|
+
const segments = pathname.split("/").filter(Boolean);
|
|
457
|
+
if (segments.length === 0) {
|
|
458
|
+
return "/";
|
|
459
|
+
}
|
|
460
|
+
const firstSegment = segments[0];
|
|
461
|
+
if (config.locales.includes(firstSegment)) {
|
|
462
|
+
const remaining = segments.slice(1);
|
|
463
|
+
return remaining.length > 0 ? `/${remaining.join("/")}` : "/";
|
|
464
|
+
}
|
|
465
|
+
return pathname;
|
|
466
|
+
}
|
|
467
|
+
function setLocaleCookie2(response, locale, config) {
|
|
468
|
+
const cookieConfig = config.localeCookie || {};
|
|
469
|
+
response.cookies.set({
|
|
470
|
+
name: cookieConfig.name || "NEXT_LOCALE",
|
|
471
|
+
value: locale,
|
|
472
|
+
maxAge: cookieConfig.maxAge || 365 * 24 * 60 * 60,
|
|
473
|
+
// 1 year
|
|
474
|
+
path: "/",
|
|
475
|
+
sameSite: cookieConfig.sameSite || "lax"
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// src/lib/i18n/utils.ts
|
|
480
|
+
init_config();
|
|
481
|
+
function isRtlLocale(locale) {
|
|
482
|
+
const rtlLocales = getRtlLocales();
|
|
483
|
+
return rtlLocales.includes(locale);
|
|
484
|
+
}
|
|
485
|
+
function getTextDirection(locale) {
|
|
486
|
+
return isRtlLocale(locale) ? "rtl" : "ltr";
|
|
487
|
+
}
|
|
488
|
+
function validateLocale(locale) {
|
|
489
|
+
return isSupportedLocale(locale);
|
|
490
|
+
}
|
|
491
|
+
function getAlternateLocales(currentLocale) {
|
|
492
|
+
try {
|
|
493
|
+
const config = getI18nConfig();
|
|
494
|
+
return config.locales.filter((l) => l !== currentLocale);
|
|
495
|
+
} catch (error) {
|
|
496
|
+
if (process.env.NODE_ENV !== "production") {
|
|
497
|
+
console.warn("getAlternateLocales: i18n config not initialized, returning []");
|
|
498
|
+
}
|
|
499
|
+
return [];
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
function normalizeLocale(locale) {
|
|
503
|
+
return locale.toLowerCase().split("-")[0];
|
|
504
|
+
}
|
|
505
|
+
function matchLocale(locale) {
|
|
506
|
+
try {
|
|
507
|
+
const config = getI18nConfig();
|
|
508
|
+
const normalized = normalizeLocale(locale);
|
|
509
|
+
if (config.locales.includes(locale)) {
|
|
510
|
+
return locale;
|
|
511
|
+
}
|
|
512
|
+
if (config.locales.includes(normalized)) {
|
|
513
|
+
return normalized;
|
|
514
|
+
}
|
|
515
|
+
const match = config.locales.find((l) => l.startsWith(normalized));
|
|
516
|
+
if (match) {
|
|
517
|
+
return match;
|
|
518
|
+
}
|
|
519
|
+
return null;
|
|
520
|
+
} catch (error) {
|
|
521
|
+
if (process.env.NODE_ENV !== "production") {
|
|
522
|
+
console.warn("matchLocale: i18n config not initialized, returning null");
|
|
523
|
+
}
|
|
524
|
+
return null;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
function getLocaleAutonym(locale) {
|
|
528
|
+
const autonyms = {
|
|
529
|
+
en: "English",
|
|
530
|
+
fr: "Fran\xE7ais",
|
|
531
|
+
es: "Espa\xF1ol",
|
|
532
|
+
de: "Deutsch",
|
|
533
|
+
it: "Italiano",
|
|
534
|
+
pt: "Portugu\xEAs",
|
|
535
|
+
ru: "\u0420\u0443\u0441\u0441\u043A\u0438\u0439",
|
|
536
|
+
ja: "\u65E5\u672C\u8A9E",
|
|
537
|
+
ko: "\uD55C\uAD6D\uC5B4",
|
|
538
|
+
zh: "\u4E2D\u6587",
|
|
539
|
+
ar: "\u0627\u0644\u0639\u0631\u0628\u064A\u0629",
|
|
540
|
+
he: "\u05E2\u05D1\u05E8\u05D9\u05EA",
|
|
541
|
+
hi: "\u0939\u093F\u0928\u094D\u0926\u0940",
|
|
542
|
+
tr: "T\xFCrk\xE7e",
|
|
543
|
+
pl: "Polski",
|
|
544
|
+
nl: "Nederlands",
|
|
545
|
+
sv: "Svenska",
|
|
546
|
+
da: "Dansk",
|
|
547
|
+
no: "Norsk",
|
|
548
|
+
fi: "Suomi",
|
|
549
|
+
cs: "\u010Ce\u0161tina",
|
|
550
|
+
el: "\u0395\u03BB\u03BB\u03B7\u03BD\u03B9\u03BA\u03AC",
|
|
551
|
+
th: "\u0E44\u0E17\u0E22",
|
|
552
|
+
vi: "Ti\u1EBFng Vi\u1EC7t",
|
|
553
|
+
id: "Bahasa Indonesia",
|
|
554
|
+
ms: "Bahasa Melayu",
|
|
555
|
+
uk: "\u0423\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0430"
|
|
556
|
+
};
|
|
557
|
+
try {
|
|
558
|
+
const { getLocaleNames: getLocaleNames2 } = (init_config(), __toCommonJS(config_exports));
|
|
559
|
+
const names = getLocaleNames2();
|
|
560
|
+
if (names[locale]) {
|
|
561
|
+
return names[locale];
|
|
562
|
+
}
|
|
563
|
+
} catch {
|
|
564
|
+
}
|
|
565
|
+
return autonyms[locale] || locale.toUpperCase();
|
|
566
|
+
}
|
|
567
|
+
function formatLocaleDisplay(locale, format = "name") {
|
|
568
|
+
if (format === "code") {
|
|
569
|
+
return locale;
|
|
570
|
+
}
|
|
571
|
+
if (format === "label") {
|
|
572
|
+
try {
|
|
573
|
+
const { getLocaleLabel: getLocaleLabel2 } = (init_config(), __toCommonJS(config_exports));
|
|
574
|
+
return getLocaleLabel2(locale);
|
|
575
|
+
} catch {
|
|
576
|
+
return locale.toUpperCase();
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
return getLocaleAutonym(locale);
|
|
580
|
+
}
|
|
581
|
+
export {
|
|
582
|
+
LOCALE_COOKIE_NAME,
|
|
583
|
+
createI18nMiddleware,
|
|
584
|
+
defaultLocale,
|
|
585
|
+
defaultSlugTranslations,
|
|
586
|
+
formatCurrency,
|
|
587
|
+
formatDate,
|
|
588
|
+
formatDateRange,
|
|
589
|
+
formatFileSize,
|
|
590
|
+
formatList,
|
|
591
|
+
formatLocaleDisplay,
|
|
592
|
+
formatNumber,
|
|
593
|
+
formatRelativeTime,
|
|
594
|
+
getAlternateLocales,
|
|
595
|
+
getDefaultLocale,
|
|
596
|
+
getI18nConfig,
|
|
597
|
+
getLocaleAutonym,
|
|
598
|
+
getLocaleCookieConfig,
|
|
599
|
+
getLocaleFromCookie,
|
|
600
|
+
getLocaleLabel,
|
|
601
|
+
getLocaleLabels,
|
|
602
|
+
getLocaleName,
|
|
603
|
+
getLocaleNames,
|
|
604
|
+
getLocalePrefix,
|
|
605
|
+
getLocales,
|
|
606
|
+
getRelativeTime,
|
|
607
|
+
getRtlLocales,
|
|
608
|
+
getTextDirection,
|
|
609
|
+
isI18nConfigInitialized,
|
|
610
|
+
isLocaleDetectionEnabled,
|
|
611
|
+
isRtlLocale,
|
|
612
|
+
isSupportedLocale,
|
|
613
|
+
locales,
|
|
614
|
+
matchLocale,
|
|
615
|
+
normalizeLocale,
|
|
616
|
+
setI18nConfig,
|
|
617
|
+
setLocaleCookie,
|
|
618
|
+
translateSlug,
|
|
619
|
+
validateLocale
|
|
620
|
+
};
|
|
621
|
+
//# sourceMappingURL=index.mjs.map
|