@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.
Files changed (166) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +572 -0
  3. package/bin/create-simple-site.js +390 -0
  4. package/bin/simple-site.js +664 -0
  5. package/dist/client.js +135 -0
  6. package/dist/client.js.map +1 -0
  7. package/dist/client.mjs +107 -0
  8. package/dist/client.mjs.map +1 -0
  9. package/dist/components/index.d.mts +3936 -0
  10. package/dist/components/index.d.ts +3936 -0
  11. package/dist/components/index.js +38265 -0
  12. package/dist/components/index.js.map +1 -0
  13. package/dist/components/index.mjs +38173 -0
  14. package/dist/components/index.mjs.map +1 -0
  15. package/dist/config/index.d.mts +298 -0
  16. package/dist/config/index.d.ts +298 -0
  17. package/dist/config/index.js +19 -0
  18. package/dist/config/index.js.map +1 -0
  19. package/dist/config/index.mjs +1 -0
  20. package/dist/config/index.mjs.map +1 -0
  21. package/dist/index.d.mts +2184 -0
  22. package/dist/index.d.ts +2184 -0
  23. package/dist/index.js +1713 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/index.mjs +1605 -0
  26. package/dist/index.mjs.map +1 -0
  27. package/dist/lib/i18n/index.js +665 -0
  28. package/dist/lib/i18n/index.js.map +1 -0
  29. package/dist/lib/i18n/index.mjs +621 -0
  30. package/dist/lib/i18n/index.mjs.map +1 -0
  31. package/docs/DOCUMENTATION-STRUCTURE.md +1156 -0
  32. package/docs/EXPORTS.md +125 -0
  33. package/docs/PERFORMANCE.md +757 -0
  34. package/docs/POLICY-PAGES.md +867 -0
  35. package/docs/ROADMAP.md +334 -0
  36. package/docs/SEO.md +455 -0
  37. package/docs/SITEMAP.md +708 -0
  38. package/docs/STRUCTURED-DATA.md +671 -0
  39. package/docs/accessibility/common-patterns.md +529 -0
  40. package/docs/accessibility/keyboard-navigation.md +263 -0
  41. package/docs/accessibility/overview.md +122 -0
  42. package/docs/accessibility/screen-readers.md +311 -0
  43. package/docs/accessibility/wcag-compliance.md +159 -0
  44. package/docs/api/README.md +164 -0
  45. package/docs/api/components/Accessibility.md +356 -0
  46. package/docs/api/components/Button.md +240 -0
  47. package/docs/api/components/HeroSection.md +306 -0
  48. package/docs/architecture/decisions.md +449 -0
  49. package/docs/components/AnalyticsTracker.md +58 -0
  50. package/docs/components/AnimatedCounter.md +48 -0
  51. package/docs/components/AnimatedSection.md +56 -0
  52. package/docs/components/BlogCard.md +42 -0
  53. package/docs/components/Checkbox.md +56 -0
  54. package/docs/components/CodeBlock.md +52 -0
  55. package/docs/components/ComparisonTable.md +40 -0
  56. package/docs/components/ComponentDemo.md +38 -0
  57. package/docs/components/CountdownTimer.md +51 -0
  58. package/docs/components/ExitIntentModal.md +56 -0
  59. package/docs/components/FAQAccordion.md +66 -0
  60. package/docs/components/FeaturesGrid.md +55 -0
  61. package/docs/components/FileUpload.md +54 -0
  62. package/docs/components/I18nMetaTags.md +55 -0
  63. package/docs/components/Icon.md +53 -0
  64. package/docs/components/LazySection.md +46 -0
  65. package/docs/components/LiveProof.md +53 -0
  66. package/docs/components/LoadingSpinner.md +46 -0
  67. package/docs/components/MultiStepForm.md +48 -0
  68. package/docs/components/PolicyLayout.md +55 -0
  69. package/docs/components/PricingTable.md +49 -0
  70. package/docs/components/Radio.md +59 -0
  71. package/docs/components/SEOMetaTags.md +58 -0
  72. package/docs/components/ScriptInjector.md +50 -0
  73. package/docs/components/Select.md +72 -0
  74. package/docs/components/Skeleton.md +47 -0
  75. package/docs/components/StatsSection.md +48 -0
  76. package/docs/components/StickyBar.md +62 -0
  77. package/docs/components/StructuredData.md +99 -0
  78. package/docs/components/StyleGuide.md +46 -0
  79. package/docs/components/TableOfContents.md +47 -0
  80. package/docs/components/TestimonialCarousel.md +42 -0
  81. package/docs/components/Timeline.md +51 -0
  82. package/docs/components/Toast.md +59 -0
  83. package/docs/components/TrackedLink.md +62 -0
  84. package/docs/components/TrustBadges.md +44 -0
  85. package/docs/components/conversion/MobileCTA.md +363 -0
  86. package/docs/components/forms/ContactForm.md +75 -0
  87. package/docs/components/forms/FormField.md +74 -0
  88. package/docs/components/layout/Footer.md +601 -0
  89. package/docs/components/layout/Header.md +549 -0
  90. package/docs/components/layout/LanguageSelector.md +54 -0
  91. package/docs/components/layout/LanguageSwitcher.md +24 -0
  92. package/docs/components/overview.md +447 -0
  93. package/docs/components/sections/AboutSection.md +48 -0
  94. package/docs/components/sections/CTASection.md +596 -0
  95. package/docs/components/sections/CaseStudySection.md +47 -0
  96. package/docs/components/sections/ContactSection.md +599 -0
  97. package/docs/components/sections/FeatureSection.md +44 -0
  98. package/docs/components/sections/HeroSection.md +404 -0
  99. package/docs/components/sections/LogosSection.md +47 -0
  100. package/docs/components/sections/PersonalTaxesSection.md +23 -0
  101. package/docs/components/sections/RecruitingSection.md +23 -0
  102. package/docs/components/sections/SecurePortalSection.md +23 -0
  103. package/docs/components/sections/ServicePageLayout.md +52 -0
  104. package/docs/components/sections/ServicesSection.md +49 -0
  105. package/docs/components/sections/TestimonialSection.md +44 -0
  106. package/docs/components/sections/WhyChooseUsSection.md +54 -0
  107. package/docs/components/ui/Breadcrumb.md +70 -0
  108. package/docs/components/ui/Button.md +514 -0
  109. package/docs/components/ui/Card.md +501 -0
  110. package/docs/components/ui/Input.md +54 -0
  111. package/docs/components/ui/MobileLinks.md +43 -0
  112. package/docs/components/ui/Modal.md +60 -0
  113. package/docs/components/ui/Tabs.md +62 -0
  114. package/docs/components/ui/Textarea.md +52 -0
  115. package/docs/core-concepts/configuration-driven.md +552 -0
  116. package/docs/core-concepts/overview.md +351 -0
  117. package/docs/features/accessibility/README.md +73 -0
  118. package/docs/features/accessibility/aria-support.md +177 -0
  119. package/docs/features/accessibility/color-contrast.md +155 -0
  120. package/docs/features/accessibility/focus-management.md +187 -0
  121. package/docs/features/accessibility/testing.md +196 -0
  122. package/docs/features/analytics/README.md +51 -0
  123. package/docs/features/analytics/ab-testing.md +171 -0
  124. package/docs/features/analytics/conversion-tracking.md +207 -0
  125. package/docs/features/analytics/custom-events.md +219 -0
  126. package/docs/features/analytics/privacy.md +198 -0
  127. package/docs/features/analytics/setup.md +114 -0
  128. package/docs/features/analytics/tracking-events.md +224 -0
  129. package/docs/features/i18n/README.md +51 -0
  130. package/docs/features/i18n/best-practices.md +273 -0
  131. package/docs/features/i18n/configuration.md +84 -0
  132. package/docs/features/i18n/formatting.md +133 -0
  133. package/docs/features/i18n/locale-detection.md +122 -0
  134. package/docs/features/i18n/routing.md +99 -0
  135. package/docs/features/i18n/rtl-support.md +191 -0
  136. package/docs/features/i18n/translations.md +129 -0
  137. package/docs/features/internationalization.md +595 -0
  138. package/docs/features/performance/README.md +77 -0
  139. package/docs/features/performance/bundle-size.md +134 -0
  140. package/docs/features/performance/caching.md +131 -0
  141. package/docs/features/performance/code-splitting.md +121 -0
  142. package/docs/features/performance/image-optimization.md +110 -0
  143. package/docs/features/performance/lazy-loading.md +92 -0
  144. package/docs/features/performance/monitoring.md +148 -0
  145. package/docs/features/seo/README.md +51 -0
  146. package/docs/features/seo/best-practices.md +184 -0
  147. package/docs/features/seo/canonical-urls.md +182 -0
  148. package/docs/features/seo/meta-tags.md +126 -0
  149. package/docs/features/seo/open-graph.md +166 -0
  150. package/docs/features/seo/robots-txt.md +146 -0
  151. package/docs/features/seo/sitemaps.md +162 -0
  152. package/docs/features/seo/structured-data.md +166 -0
  153. package/docs/getting-started/installation.md +292 -0
  154. package/docs/getting-started/introduction.md +195 -0
  155. package/docs/getting-started/quick-start.md +460 -0
  156. package/docs/guides/analytics-setup.md +616 -0
  157. package/docs/i18n/CONFIGURATION.md +353 -0
  158. package/docs/i18n/EXAMPLES.md +402 -0
  159. package/docs/i18n/MIGRATION.md +260 -0
  160. package/docs/i18n/SEO.md +392 -0
  161. package/docs/i18n/STATIC-GENERATION-FIX.md +71 -0
  162. package/docs/migration/changelog.md +136 -0
  163. package/docs/migration/overview.md +233 -0
  164. package/docs/recipes/adding-animations.md +475 -0
  165. package/docs/recipes/forms-with-validation.md +393 -0
  166. package/package.json +152 -0
@@ -0,0 +1,273 @@
1
+ # i18n Best Practices
2
+
3
+ Guidelines and patterns for effective internationalization.
4
+
5
+ ## Planning
6
+
7
+ ### Choose Locales Carefully
8
+
9
+ - Start with languages your target audience actually speaks
10
+ - Consider regional variants (en-US vs en-GB, fr-FR vs fr-CA)
11
+ - Don't add languages you can't properly maintain
12
+
13
+ ### URL Structure
14
+
15
+ Choose the right `localePrefix` mode:
16
+
17
+ - **'as-needed'** (Recommended) - Clean default locale URLs, prefixed alternatives
18
+ - **'always'** - Explicit language in all URLs, equal treatment
19
+ - **'never'** - Simplest URLs, harder to share specific language links
20
+
21
+ ## Content Management
22
+
23
+ ### Keep Translations Complete
24
+
25
+ Ensure all locales have translations:
26
+
27
+ ```typescript
28
+ // ✅ Good - All locales covered
29
+ const heading = {
30
+ en: 'Welcome',
31
+ fr: 'Bienvenue',
32
+ es: 'Bienvenido',
33
+ };
34
+
35
+ // ❌ Bad - Missing Spanish
36
+ const heading = {
37
+ en: 'Welcome',
38
+ fr: 'Bienvenue',
39
+ };
40
+ ```
41
+
42
+ ### Use TypeScript for Safety
43
+
44
+ Define translation keys with types:
45
+
46
+ ```typescript
47
+ type TranslationKeys = {
48
+ heading: LocalizedString;
49
+ description: LocalizedString;
50
+ cta: LocalizedString;
51
+ };
52
+
53
+ const translations: TranslationKeys = {
54
+ heading: { en: '...', fr: '...' },
55
+ description: { en: '...', fr: '...' },
56
+ cta: { en: '...', fr: '...' },
57
+ };
58
+ ```
59
+
60
+ ### Organize Translations
61
+
62
+ Group by feature or page:
63
+
64
+ ```
65
+ config/translations/
66
+ ├── common.ts # Shared across site
67
+ ├── navigation.ts # Nav, footer links
68
+ ├── home.ts # Homepage
69
+ ├── about.ts # About page
70
+ └── services.ts # Services page
71
+ ```
72
+
73
+ ## SEO Optimization
74
+
75
+ ### Use hreflang Tags
76
+
77
+ Framework automatically generates hreflang tags via I18nMetaTags:
78
+
79
+ ```typescript
80
+ import { I18nMetaTags } from '@zoyth/simple-site-framework/components';
81
+
82
+ <I18nMetaTags
83
+ currentLocale={locale}
84
+ pathname={pathname}
85
+ baseUrl="https://example.com"
86
+ />
87
+ ```
88
+
89
+ ### Separate Sitemaps
90
+
91
+ Generate sitemap per locale or include all in one with locale information.
92
+
93
+ ### Canonical URLs
94
+
95
+ Set canonical URLs correctly for each locale variant.
96
+
97
+ ## Performance
98
+
99
+ ### Static Generation
100
+
101
+ Pre-generate all locale variants:
102
+
103
+ ```typescript
104
+ export async function generateStaticParams() {
105
+ const locales = ['en', 'fr', 'es'];
106
+
107
+ return locales.map(locale => ({ locale }));
108
+ }
109
+ ```
110
+
111
+ ### Code Splitting
112
+
113
+ Split translations by page to reduce bundle size:
114
+
115
+ ```typescript
116
+ // Load translations on-demand
117
+ const translations = await import(`./translations/${locale}.ts`);
118
+ ```
119
+
120
+ ### Avoid Over-Translation
121
+
122
+ Don't translate:
123
+ - Brand names
124
+ - Product names (unless officially localized)
125
+ - Technical terms without clear equivalents
126
+ - Code examples
127
+
128
+ ## User Experience
129
+
130
+ ### Language Detection
131
+
132
+ - Enable browser detection for first visit
133
+ - Store preference in cookie
134
+ - Allow manual override via LanguageSelector
135
+ - Don't force redirects on every visit
136
+
137
+ ### Language Switcher Placement
138
+
139
+ Place LanguageSelector where users expect it:
140
+ - Header (top-right is common)
141
+ - Footer
142
+ - Mobile menu
143
+
144
+ ### Preserve Context
145
+
146
+ When switching languages:
147
+ ```typescript
148
+ // ✅ Good - Stay on same page
149
+ /en/about → /fr/about
150
+
151
+ // ❌ Bad - Go to homepage
152
+ /en/about → /fr/
153
+ ```
154
+
155
+ ## Content Guidelines
156
+
157
+ ### Avoid Concatenation
158
+
159
+ ```typescript
160
+ // ❌ Bad - Word order varies by language
161
+ const message = `${userName} ${action} ${item}`;
162
+
163
+ // ✅ Good - Full sentence per locale
164
+ const message = {
165
+ en: `${userName} purchased ${item}`,
166
+ fr: `${userName} a acheté ${item}`,
167
+ };
168
+ ```
169
+
170
+ ### Handle Pluralization
171
+
172
+ Different languages have different plural rules:
173
+
174
+ ```typescript
175
+ const itemCount = {
176
+ en: count === 1 ? '1 item' : `${count} items`,
177
+ fr: count <= 1 ? '1 article' : `${count} articles`,
178
+ ar: /* Arabic has 6 plural forms! */
179
+ };
180
+ ```
181
+
182
+ Consider using a library like `react-intl` for complex pluralization.
183
+
184
+ ### Date and Time Clarity
185
+
186
+ Always use locale-aware formatting:
187
+
188
+ ```typescript
189
+ import { formatDate } from '@zoyth/simple-site-framework/lib/i18n';
190
+
191
+ formatDate(date, locale, { dateStyle: 'long' });
192
+ ```
193
+
194
+ ## Testing
195
+
196
+ ### Test All Locales
197
+
198
+ - Navigate through site in each locale
199
+ - Test forms and validation messages
200
+ - Verify date/number formatting
201
+ - Check layout with longer translations (German often longer than English)
202
+ - Test RTL languages if supported
203
+
204
+ ### Automated Testing
205
+
206
+ ```typescript
207
+ describe('i18n', () => {
208
+ const locales = ['en', 'fr', 'es'];
209
+
210
+ locales.forEach(locale => {
211
+ it(`renders ${locale} homepage`, () => {
212
+ // Test each locale
213
+ });
214
+ });
215
+ });
216
+ ```
217
+
218
+ ## Maintenance
219
+
220
+ ### Version Control
221
+
222
+ Track translations in git alongside code:
223
+ - Easy to review changes
224
+ - See translation history
225
+ - Merge conflicts are manageable
226
+
227
+ ### Translation Workflow
228
+
229
+ 1. Develop feature in default locale
230
+ 2. Extract translatable strings
231
+ 3. Send to translators
232
+ 4. Review and integrate translations
233
+ 5. Test all locales
234
+ 6. Deploy
235
+
236
+ ### Professional Translation
237
+
238
+ For production sites:
239
+ - Use professional translators
240
+ - Avoid machine translation for customer-facing content
241
+ - Consider translation management platforms
242
+ - Review translations in context
243
+
244
+ ## Common Pitfalls
245
+
246
+ ### Don't Assume English
247
+
248
+ Framework doesn't assume English as default - you choose the default locale.
249
+
250
+ ### Don't Hardcode Strings
251
+
252
+ ```typescript
253
+ // ❌ Bad
254
+ <button>Click here</button>
255
+
256
+ // ✅ Good
257
+ <button>{buttonText}</button>
258
+ ```
259
+
260
+ ### Don't Skip Metadata
261
+
262
+ Translate:
263
+ - Page titles
264
+ - Meta descriptions
265
+ - Alt text for images
266
+ - Form labels and errors
267
+ - Button text
268
+
269
+ ## See Also
270
+
271
+ - [Configuration](./configuration.md)
272
+ - [Translations](./translations.md)
273
+ - [SEO Guide](../seo/best-practices.md)
@@ -0,0 +1,84 @@
1
+ # i18n Configuration
2
+
3
+ Configure internationalization settings for your multi-language site.
4
+
5
+ ## Configuration File
6
+
7
+ Create your i18n configuration:
8
+
9
+ ```typescript
10
+ // src/config/i18n.ts
11
+ import type { I18nConfig } from '@zoyth/simple-site-framework/lib/i18n';
12
+
13
+ export const i18nConfig: I18nConfig = {
14
+ locales: ['en', 'fr', 'es', 'de'],
15
+ defaultLocale: 'en',
16
+ localePrefix: 'as-needed',
17
+ localeDetection: true,
18
+ localeNames: {
19
+ en: 'English',
20
+ fr: 'Français',
21
+ es: 'Español',
22
+ de: 'Deutsch',
23
+ },
24
+ localeLabels: {
25
+ en: 'EN',
26
+ fr: 'FR',
27
+ es: 'ES',
28
+ de: 'DE',
29
+ },
30
+ rtlLocales: ['ar', 'he'],
31
+ localeCookie: {
32
+ name: 'NEXT_LOCALE',
33
+ maxAge: 365 * 24 * 60 * 60, // 1 year
34
+ sameSite: 'lax',
35
+ },
36
+ };
37
+ ```
38
+
39
+ ## Configuration Options
40
+
41
+ | Option | Type | Required | Description |
42
+ |--------|------|----------|-------------|
43
+ | `locales` | `string[]` | Yes | Supported locale codes |
44
+ | `defaultLocale` | `string` | Yes | Default/fallback locale |
45
+ | `localePrefix` | `'always' \| 'as-needed' \| 'never'` | No | URL prefix mode (default: 'as-needed') |
46
+ | `localeDetection` | `boolean` | No | Auto-detect from browser (default: true) |
47
+ | `localeNames` | `Record<string, string>` | No | Full language names for UI |
48
+ | `localeLabels` | `Record<string, string>` | No | Short labels for UI |
49
+ | `rtlLocales` | `string[]` | No | Right-to-left locales |
50
+ | `localeCookie` | `object` | No | Cookie configuration |
51
+
52
+ ## Locale Prefix Modes
53
+
54
+ ### 'always'
55
+ All URLs include locale prefix:
56
+ - `/en/about`
57
+ - `/fr/about`
58
+
59
+ ### 'as-needed' (Default)
60
+ Only non-default locales have prefix:
61
+ - `/about` (default locale)
62
+ - `/fr/about` (other locales)
63
+
64
+ ### 'never'
65
+ No locale prefixes, detection via cookie/header:
66
+ - `/about` (all languages)
67
+
68
+ ## Initialization
69
+
70
+ Initialize config in your root layout:
71
+
72
+ ```typescript
73
+ // app/[locale]/layout.tsx
74
+ import { setI18nConfig } from '@zoyth/simple-site-framework/lib/i18n';
75
+ import { i18nConfig } from '../../config/i18n';
76
+
77
+ setI18nConfig(i18nConfig);
78
+ ```
79
+
80
+ ## See Also
81
+
82
+ - [Routing](./routing.md)
83
+ - [Locale Detection](./locale-detection.md)
84
+ - [Configuration Guide](../../i18n/CONFIGURATION.md)
@@ -0,0 +1,133 @@
1
+ # Locale-Aware Formatting
2
+
3
+ Format dates, numbers, and currency based on user's locale.
4
+
5
+ ## Date Formatting
6
+
7
+ Format dates according to locale conventions:
8
+
9
+ ```typescript
10
+ import { formatDate } from '@zoyth/simple-site-framework/lib/i18n';
11
+
12
+ const date = new Date('2024-12-25');
13
+
14
+ formatDate(date, 'en'); // "12/25/2024"
15
+ formatDate(date, 'fr'); // "25/12/2024"
16
+ formatDate(date, 'de'); // "25.12.2024"
17
+
18
+ // With options
19
+ formatDate(date, 'en', {
20
+ dateStyle: 'long',
21
+ }); // "December 25, 2024"
22
+
23
+ formatDate(date, 'fr', {
24
+ dateStyle: 'long',
25
+ }); // "25 décembre 2024"
26
+ ```
27
+
28
+ ## Number Formatting
29
+
30
+ Format numbers with locale-specific separators:
31
+
32
+ ```typescript
33
+ import { formatNumber } from '@zoyth/simple-site-framework/lib/i18n';
34
+
35
+ const number = 1234567.89;
36
+
37
+ formatNumber(number, 'en'); // "1,234,567.89"
38
+ formatNumber(number, 'fr'); // "1 234 567,89"
39
+ formatNumber(number, 'de'); // "1.234.567,89"
40
+
41
+ // With options
42
+ formatNumber(number, 'en', {
43
+ minimumFractionDigits: 2,
44
+ maximumFractionDigits: 2,
45
+ }); // "1,234,567.89"
46
+ ```
47
+
48
+ ## Currency Formatting
49
+
50
+ Format currency amounts:
51
+
52
+ ```typescript
53
+ import { formatCurrency } from '@zoyth/simple-site-framework/lib/i18n';
54
+
55
+ const amount = 1234.56;
56
+
57
+ formatCurrency(amount, 'en', 'USD'); // "$1,234.56"
58
+ formatCurrency(amount, 'fr', 'EUR'); // "1 234,56 €"
59
+ formatCurrency(amount, 'de', 'EUR'); // "1.234,56 €"
60
+ formatCurrency(amount, 'ja', 'JPY'); // "¥1,235"
61
+
62
+ // With options
63
+ formatCurrency(amount, 'en', 'USD', {
64
+ currencyDisplay: 'name',
65
+ }); // "1,234.56 US dollars"
66
+ ```
67
+
68
+ ## Relative Time Formatting
69
+
70
+ Format relative time periods:
71
+
72
+ ```typescript
73
+ import { formatRelativeTime } from '@zoyth/simple-site-framework/lib/i18n';
74
+
75
+ formatRelativeTime(-1, 'day', 'en'); // "1 day ago"
76
+ formatRelativeTime(-1, 'day', 'fr'); // "il y a 1 jour"
77
+ formatRelativeTime(2, 'week', 'en'); // "in 2 weeks"
78
+ formatRelativeTime(2, 'week', 'es'); // "dentro de 2 semanas"
79
+ ```
80
+
81
+ ## List Formatting
82
+
83
+ Format lists according to locale:
84
+
85
+ ```typescript
86
+ const items = ['Apple', 'Banana', 'Orange'];
87
+
88
+ new Intl.ListFormat('en').format(items);
89
+ // "Apple, Banana, and Orange"
90
+
91
+ new Intl.ListFormat('fr').format(items);
92
+ // "Apple, Banana et Orange"
93
+
94
+ new Intl.ListFormat('es', { type: 'disjunction' }).format(items);
95
+ // "Apple, Banana o Orange"
96
+ ```
97
+
98
+ ## Intl API Reference
99
+
100
+ All formatters use the native JavaScript Intl API:
101
+
102
+ - `Intl.DateTimeFormat` - Date/time formatting
103
+ - `Intl.NumberFormat` - Number/currency formatting
104
+ - `Intl.RelativeTimeFormat` - Relative time formatting
105
+ - `Intl.ListFormat` - List formatting
106
+
107
+ ## Usage in Components
108
+
109
+ Example with formatted dates:
110
+
111
+ ```typescript
112
+ import { formatDate } from '@zoyth/simple-site-framework/lib/i18n';
113
+
114
+ export function BlogPost({ date, locale }: Props) {
115
+ const formattedDate = formatDate(date, locale, {
116
+ dateStyle: 'long',
117
+ });
118
+
119
+ return (
120
+ <article>
121
+ <time dateTime={date.toISOString()}>
122
+ {formattedDate}
123
+ </time>
124
+ </article>
125
+ );
126
+ }
127
+ ```
128
+
129
+ ## See Also
130
+
131
+ - [Translations](./translations.md)
132
+ - [Configuration](./configuration.md)
133
+ - [MDN Intl Documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl)
@@ -0,0 +1,122 @@
1
+ # Locale Detection
2
+
3
+ Automatic language detection from browser, cookies, and URL.
4
+
5
+ ## Detection Strategy
6
+
7
+ The middleware detects locale in this order:
8
+
9
+ 1. **URL Parameter** - Explicit locale in path (`/fr/about`)
10
+ 2. **Cookie** - Previously selected locale (`NEXT_LOCALE` cookie)
11
+ 3. **Accept-Language Header** - Browser language preference
12
+ 4. **Default Locale** - Configured fallback
13
+
14
+ ## Browser Detection
15
+
16
+ Enable in configuration:
17
+
18
+ ```typescript
19
+ export const i18nConfig = {
20
+ locales: ['en', 'fr', 'es', 'de'],
21
+ defaultLocale: 'en',
22
+ localeDetection: true, // Enable browser detection
23
+ };
24
+ ```
25
+
26
+ ### Accept-Language Header
27
+
28
+ The framework parses the `Accept-Language` header:
29
+
30
+ ```
31
+ Accept-Language: fr-CA,fr;q=0.9,en;q=0.8,de;q=0.5
32
+ ```
33
+
34
+ Matches:
35
+ 1. Exact match: `fr-CA` → `fr`
36
+ 2. Language prefix: `fr` → `fr`
37
+ 3. Quality values (q) determine priority
38
+ 4. Fallback to default if no match
39
+
40
+ ## Cookie Persistence
41
+
42
+ When user selects a language, it's stored in a cookie:
43
+
44
+ ```typescript
45
+ // Cookie configuration
46
+ localeCookie: {
47
+ name: 'NEXT_LOCALE',
48
+ maxAge: 365 * 24 * 60 * 60, // 1 year
49
+ sameSite: 'lax',
50
+ }
51
+ ```
52
+
53
+ Cookie persists across:
54
+ - Browser sessions
55
+ - Different pages
56
+ - Returning visits
57
+
58
+ ## Manual Cookie Setting
59
+
60
+ Set locale cookie programmatically:
61
+
62
+ ```typescript
63
+ import { setLocaleCookie } from '@zoyth/simple-site-framework/lib/i18n';
64
+
65
+ function handleLanguageChange(locale: string) {
66
+ setLocaleCookie(locale);
67
+ // Navigate to new locale...
68
+ }
69
+ ```
70
+
71
+ ## Detection Flow
72
+
73
+ ```
74
+ User visits site
75
+
76
+ Check URL for locale
77
+ ↓ (not found)
78
+ Check NEXT_LOCALE cookie
79
+ ↓ (not found)
80
+ Parse Accept-Language header
81
+ ↓ (no match)
82
+ Use default locale
83
+
84
+ Redirect if needed (based on localePrefix mode)
85
+
86
+ Set cookie with detected locale
87
+ ```
88
+
89
+ ## Disabling Detection
90
+
91
+ Disable automatic browser detection:
92
+
93
+ ```typescript
94
+ export const i18nConfig = {
95
+ locales: ['en', 'fr'],
96
+ defaultLocale: 'en',
97
+ localeDetection: false, // Disable
98
+ };
99
+ ```
100
+
101
+ Users must explicitly select language via LanguageSelector.
102
+
103
+ ## Testing Detection
104
+
105
+ Test different scenarios:
106
+
107
+ ```bash
108
+ # Test with specific Accept-Language
109
+ curl -H "Accept-Language: fr-FR,fr;q=0.9" http://localhost:3000/
110
+
111
+ # Test with cookie
112
+ curl -H "Cookie: NEXT_LOCALE=es" http://localhost:3000/
113
+
114
+ # Test URL override
115
+ curl http://localhost:3000/de/
116
+ ```
117
+
118
+ ## See Also
119
+
120
+ - [Routing](./routing.md)
121
+ - [Configuration](./configuration.md)
122
+ - [LanguageSelector Component](../../components/layout/LanguageSelector.md)
@@ -0,0 +1,99 @@
1
+ # Locale Routing
2
+
3
+ URL routing and navigation for multi-language sites.
4
+
5
+ ## Route Structure
6
+
7
+ Based on your `localePrefix` configuration:
8
+
9
+ ### 'as-needed' Mode (Default)
10
+ ```
11
+ / → Default locale homepage
12
+ /about → Default locale about page
13
+ /fr/ → French homepage
14
+ /fr/about → French about page
15
+ ```
16
+
17
+ ### 'always' Mode
18
+ ```
19
+ /en/ → English homepage
20
+ /en/about → English about page
21
+ /fr/ → French homepage
22
+ /fr/about → French about page
23
+ ```
24
+
25
+ ### 'never' Mode
26
+ ```
27
+ / → Homepage (locale from cookie/header)
28
+ /about → About page (locale from cookie/header)
29
+ ```
30
+
31
+ ## Middleware Setup
32
+
33
+ Create middleware for automatic routing:
34
+
35
+ ```typescript
36
+ // src/middleware.ts
37
+ import { createI18nMiddleware } from '@zoyth/simple-site-framework/lib/i18n';
38
+ import { i18nConfig } from './config/i18n';
39
+
40
+ export default createI18nMiddleware(i18nConfig);
41
+
42
+ export const config = {
43
+ matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
44
+ };
45
+ ```
46
+
47
+ ## Navigation
48
+
49
+ ### Using Next.js Link
50
+
51
+ ```typescript
52
+ import Link from 'next/link';
53
+
54
+ // Same-locale navigation
55
+ <Link href="/about">About</Link>
56
+
57
+ // Cross-locale navigation
58
+ <Link href="/fr/about">À propos</Link>
59
+ ```
60
+
61
+ ### Language Switching
62
+
63
+ Use the LanguageSelector component:
64
+
65
+ ```typescript
66
+ import { LanguageSelector } from '@zoyth/simple-site-framework/components';
67
+
68
+ <LanguageSelector currentLocale={locale} />
69
+ ```
70
+
71
+ ## Dynamic Routes
72
+
73
+ Handle dynamic routes with locale parameter:
74
+
75
+ ```typescript
76
+ // app/[locale]/blog/[slug]/page.tsx
77
+ export async function generateStaticParams() {
78
+ const locales = ['en', 'fr'];
79
+ const slugs = ['post-1', 'post-2'];
80
+
81
+ return locales.flatMap(locale =>
82
+ slugs.map(slug => ({ locale, slug }))
83
+ );
84
+ }
85
+ ```
86
+
87
+ ## Redirects
88
+
89
+ The middleware automatically:
90
+ 1. Detects missing locale in URL
91
+ 2. Determines user's preferred locale
92
+ 3. Redirects to appropriate localized URL
93
+ 4. Sets locale cookie for persistence
94
+
95
+ ## See Also
96
+
97
+ - [Configuration](./configuration.md)
98
+ - [Locale Detection](./locale-detection.md)
99
+ - [Routing Examples](../../i18n/EXAMPLES.md)