create-nextblock 0.12.13 → 0.12.15

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 (42) hide show
  1. package/package.json +1 -1
  2. package/templates/nextblock-template/app/(auth-pages)/sign-in/page.tsx +0 -13
  3. package/templates/nextblock-template/app/(auth-pages)/sign-up/SignUpForm.tsx +0 -14
  4. package/templates/nextblock-template/app/[slug]/page.tsx +12 -4
  5. package/templates/nextblock-template/app/api/cron/reset-sandbox/sandboxResetSql.ts +209 -101
  6. package/templates/nextblock-template/app/cms/CmsClientLayout.tsx +2 -3
  7. package/templates/nextblock-template/app/cms/components/FeatureImageField.tsx +1 -0
  8. package/templates/nextblock-template/app/cms/interactions/InteractionsModerationClient.tsx +1 -1
  9. package/templates/nextblock-template/app/cms/interactions/page.tsx +1 -1
  10. package/templates/nextblock-template/app/cms/media/components/MediaPickerDialog.tsx +38 -35
  11. package/templates/nextblock-template/app/cms/media/components/MediaUploadForm.tsx +14 -11
  12. package/templates/nextblock-template/app/cms/products/ProductFormClientShell.tsx +62 -14
  13. package/templates/nextblock-template/app/cms/products/[id]/edit/page.tsx +2 -2
  14. package/templates/nextblock-template/app/cms/revisions/RevisionHistoryButton.tsx +2 -2
  15. package/templates/nextblock-template/app/cms/revisions/actions.ts +5 -5
  16. package/templates/nextblock-template/app/cms/settings/languages/actions.ts +53 -1
  17. package/templates/nextblock-template/app/cms/settings/languages/components/LanguageDetectionPanel.tsx +188 -0
  18. package/templates/nextblock-template/app/cms/settings/languages/page.tsx +12 -1
  19. package/templates/nextblock-template/app/cms/users/actions.ts +0 -3
  20. package/templates/nextblock-template/app/cms/users/components/UserForm.tsx +0 -2
  21. package/templates/nextblock-template/app/cms/users/page.tsx +0 -2
  22. package/templates/nextblock-template/app/layout.tsx +42 -1
  23. package/templates/nextblock-template/app/product/[slug]/page.tsx +12 -4
  24. package/templates/nextblock-template/app/profile/ProfileAccountSidebar.tsx +1 -1
  25. package/templates/nextblock-template/app/profile/account-data.ts +1 -1
  26. package/templates/nextblock-template/app/profile/account-types.ts +0 -1
  27. package/templates/nextblock-template/app/profile/page.tsx +0 -1
  28. package/templates/nextblock-template/app/providers.tsx +2 -0
  29. package/templates/nextblock-template/components/PostCommentsSection.tsx +2 -2
  30. package/templates/nextblock-template/components/ProductReviewsSection.tsx +2 -2
  31. package/templates/nextblock-template/components/header-auth.tsx +1 -1
  32. package/templates/nextblock-template/context/LanguageContext.tsx +22 -7
  33. package/templates/nextblock-template/docs/TECHNICAL_SPECIFICATION.md +46 -43
  34. package/templates/nextblock-template/lib/custom-block-relation-registry.ts +3 -3
  35. package/templates/nextblock-template/lib/i18n/country-languages.ts +247 -0
  36. package/templates/nextblock-template/lib/i18n/detection.test.ts +197 -0
  37. package/templates/nextblock-template/lib/i18n/detection.ts +192 -0
  38. package/templates/nextblock-template/lib/setup/migrations-bundle.ts +72 -37
  39. package/templates/nextblock-template/package.json +1 -1
  40. package/templates/nextblock-template/proxy.ts +141 -8
  41. package/templates/nextblock-template/tsconfig.tsbuildinfo +1 -1
  42. package/templates/nextblock-template/components/GitHubLoginButton.tsx +0 -36
@@ -95,7 +95,7 @@ NextBlock CMS integrates with a specific, opinionated set of external services w
95
95
  The system delivers six top-level capability families, each surfaced through dedicated route groups in the Next.js App Router:
96
96
 
97
97
  1. **Public Content Delivery** — Routes `app/[slug]`, `app/article/[slug]`, shared `app/layout.tsx`, `robots.txt`, and `sitemap.xml` serve marketing pages, articles, and product pages.
98
- 2. **Authentication & Account Management** — The `app/(auth-pages)/*` group and `app/auth/callback/route.ts` implement Supabase Auth session exchange, GitHub OAuth (via `components/GitHubLoginButton.tsx`), and profile completion flows.
98
+ 2. **Authentication & Account Management** — The `app/(auth-pages)/*` group and `app/auth/callback/route.ts` implement Supabase Auth email/password session exchange and profile completion flows.
99
99
  3. **CMS Administration** — The `app/cms/*` route tree provides block editing, media management, product catalog management, order administration, shipping configuration, tax settings, and user administration, gated by role.
100
100
  4. **Commerce Surface** — Routes `app/product/*`, `app/cart`, and `app/checkout` implement the customer-facing storefront; `app/api/checkout/route.ts` handles server-side checkout orchestration.
101
101
  5. **Operational Endpoints** — Routes `app/api/webhooks/*` (Stripe/Freemius) and `app/api/cron/*` (sandbox reset, currency sync) provide the machine-to-machine surface; `app/api/upload` and `app/api/process-image` handle media ingestion and optimization.
@@ -269,7 +269,7 @@ The following architectural invariants are enforced at workspace level and must
269
269
 
270
270
  **Editor Capabilities** — The `@nextblock-cms/editor` library (version `0.2.24`) exports `Editor`, `NotionEditor`, `EditorToolbar`, `EditorBubbleMenu`, `EditorFloatingMenu`, `EnhancedFloatingMenu`, `SlashCommandList`, `DragHandle`, `HtmlContent`, and `editorExtensions`. Feature set includes Tiptap StarterKit rich text, syntax-highlighted code blocks, tables, task lists, slash commands, drag handles, image handling, character counting, typography, mathematics, emoji, mentions, inline alert and call-to-action widgets, and custom HTML-preserving extensions for `div`, `style`, `script`, `svg`, `span`, and catch-all attribute preservation. A media-picker bridge is exposed via `setOpenImagePicker()`.
271
271
 
272
- **Translation & Localization** — The system supports two locales at present (`en`, `fr` defined as `SUPPORTED_LOCALES` in `apps/nextblock/proxy.ts`), backed by `languages` and `translations` tables from migration `00000000000001_setup_cms_core.sql`. Content revision history is stored as snapshot + JSON Patch diff (enum `revision_type: snapshot, diff`) per migration `00000000000002_setup_content_tables.sql`.
272
+ **Translation & Localization** — The set of served locales is the active rows of the `languages` table (managed at `/cms/settings/languages`; the proxy reads them with a 60-second in-memory cache and only falls back to the hardcoded `FALLBACK_LOCALES` `en`/`fr` when the DB is unreadable), backed by `languages` and `translations` tables from migration `00000000000001_setup_cms_core.sql`. First-visit language detection is admin-configurable (see F-007): browser `Accept-Language`, IP-country via host geo headers, combined, or always-default — implemented in `apps/nextblock/lib/i18n/detection.ts` and stored in `site_settings.language_detection_settings`. Content revision history is stored as snapshot + JSON Patch diff (enum `revision_type: snapshot, diff`) per migration `00000000000002_setup_content_tables.sql`.
273
273
 
274
274
  **Page Lifecycle** — Pages move through `draft`, `published`, and `archived` statuses (enum `page_status`).
275
275
 
@@ -390,7 +390,7 @@ Based on claims and deferred items in the repository documentation, the followin
390
390
 
391
391
  #### 1.3.3.5 Integration Points Not Covered
392
392
 
393
- The system does not natively integrate with: alternative payment processors beyond Stripe and Freemius, alternative authentication providers beyond Supabase Auth and GitHub OAuth, alternative object-storage providers beyond S3-compatible endpoints (specifically Cloudflare R2 in reference deployments), alternative monorepo orchestrators beyond Nx, or alternative hosting platforms requiring cron-configuration formats other than Vercel's `vercel.json` schema.
393
+ The system does not natively integrate with: alternative payment processors beyond Stripe and Freemius, alternative authentication providers beyond Supabase Auth, alternative object-storage providers beyond S3-compatible endpoints (specifically Cloudflare R2 in reference deployments), alternative monorepo orchestrators beyond Nx, or alternative hosting platforms requiring cron-configuration formats other than Vercel's `vercel.json` schema.
394
394
 
395
395
  ---
396
396
 
@@ -619,7 +619,11 @@ Media management combines a Cloudflare R2-backed object store with an image-proc
619
619
 
620
620
  **Description**
621
621
 
622
- Two locales are currently supported `en` and `fr` defined as `SUPPORTED_LOCALES` in `apps/nextblock/proxy.ts` and seeded in migration `00000000000008_seed_platform_defaults.sql` (with `en` marked as the single default). The `languages` table enforces a single `is_default` row; the `translations` table stores translation keys with JSONB-per-locale values. Localized content entities (pages, posts, products) are clustered via `translation_group_id` UUIDs. Locale propagation uses the `NEXT_USER_LOCALE` cookie (`maxAge: 31_536_000` seconds, one year) and the `X-User-Locale` request header, both set by the request proxy. The client-side provider chain `LanguageProvider → TranslationsProvider` in `apps/nextblock/app/providers.tsx` bridges server-resolved locale into React context.
622
+ Served locales are the active rows of the `languages` table, managed at `/cms/settings/languages` (the seed provides `en` and `fr`, with `en` as the single default; `apps/nextblock/proxy.ts` keeps `FALLBACK_LOCALES = ['en','fr']` only as a safety net when the DB is unreadable). The `languages` table enforces a single `is_default` row; the `translations` table stores translation keys with JSONB-per-locale values. Localized content entities (pages, posts, products) are clustered via `translation_group_id` UUIDs.
623
+
624
+ **First-visit language detection** is admin-configurable on `/cms/settings/languages` and stored as the non-sensitive `site_settings` key `language_detection_settings` (`{ mode, rememberVisitorChoice }`). Modes: `browser` (default — full `Accept-Language` q-value parsing), `country` (visitor's country from host geo headers `x-vercel-ip-country` / `cf-ipcountry` / `cloudfront-viewer-country` / `x-country-code`, mapped through `apps/nextblock/lib/i18n/country-languages.ts`), `browser_then_country`, and `default` (no detection). The pure resolution helpers live in `apps/nextblock/lib/i18n/detection.ts`; the proxy applies them for any request without a valid locale cookie, resolving against the active languages + default language read from the DB with a 60-second in-memory cache.
625
+
626
+ Locale propagation uses the `NEXT_USER_LOCALE` cookie and the `X-User-Locale` request header, both set by the request proxy. When `rememberVisitorChoice` is `true` (default) the cookie persists for one year (`maxAge: 31_536_000` seconds); when `false` it is a session cookie, so detection re-runs each new browser session (the client `LanguageProvider` mirrors the same expiry for manual switcher choices). A valid cookie always beats detection. The client-side provider chain `LanguageProvider → TranslationsProvider` in `apps/nextblock/app/providers.tsx` bridges server-resolved locale into React context.
623
627
 
624
628
  **Business Value:** Unlocks multi-market deployments without requiring adopters to integrate a separate i18n library. **User Benefits:** Language switching persists across sessions; same content IDs preserve relationships across translations. **Technical Context:** Implemented at schema level (migration `00000000000001_setup_cms_core.sql`), proxy level, and provider level.
625
629
 
@@ -700,9 +704,9 @@ Three menu locations are supported: `HEADER`, `FOOTER`, and `SIDEBAR`, encoded b
700
704
 
701
705
  **Description**
702
706
 
703
- Authentication is layered over Supabase Auth using `@supabase/ssr` 0.7.0 and `@supabase/supabase-js` 2.77.0. Server actions in `apps/nextblock/app/actions.ts` implement sign-in, sign-up, and forgot-password flows, complemented by GitHub OAuth via `components/GitHubLoginButton.tsx`. The `app/auth/callback/route.ts` route exchanges authorization codes for sessions (`supabase.auth.exchangeCodeForSession()`), loads the profile role, and redirects using `resolvePostAuthRedirect()`. Email templates for confirmation, email change, invitation, magic-link, reauthentication, and password recovery reside in `libs/db/src/supabase/templates/`. A `handle_new_user()` trigger function paired with the `on_auth_user_created` trigger on `auth.users` automatically provisions a `profiles` row on registration.
707
+ Authentication is layered over Supabase Auth using `@supabase/ssr` 0.7.0 and `@supabase/supabase-js` 2.77.0. Server actions in `apps/nextblock/app/actions.ts` implement sign-in, sign-up, and forgot-password flows. The `app/auth/callback/route.ts` route exchanges authorization codes for sessions (used by the email-confirmation and recovery flows) (`supabase.auth.exchangeCodeForSession()`), loads the profile role, and redirects using `resolvePostAuthRedirect()`. Email templates for confirmation, email change, invitation, magic-link, reauthentication, and password recovery reside in `libs/db/src/supabase/templates/`. A `handle_new_user()` trigger function paired with the `on_auth_user_created` trigger on `auth.users` automatically provisions a `profiles` row on registration.
704
708
 
705
- **Business Value:** Eliminates the need for adopters to integrate a separate identity provider. **User Benefits:** Multiple sign-in methods (email/password, GitHub OAuth, magic link) with localized email templates. **Technical Context:** Supabase Auth session cookies are synchronized by the request proxy to Server Components.
709
+ **Business Value:** Eliminates the need for adopters to integrate a separate identity provider. **User Benefits:** Multiple sign-in methods (email/password, magic link) with localized email templates. **Technical Context:** Supabase Auth session cookies are synchronized by the request proxy to Server Components.
706
710
 
707
711
  **Dependencies**
708
712
 
@@ -1290,7 +1294,8 @@ This subsection provides the detailed, testable requirements that operationalize
1290
1294
  | F-005-RQ-002 | Editor MUST preserve custom HTML constructs (`div`, `style`, `script`, `svg`, `span`) and unrecognized attributes | Must-Have | High |
1291
1295
  | F-006-RQ-001 | Media uploads MUST record metadata (`object_key`, `file_type`, `size_bytes`, `width`, `height`, `blur_data_url`, `variants`) in the `media` table | Must-Have | Medium |
1292
1296
  | F-006-RQ-002 | `recordMediaUpload` MUST reject non-`ADMIN`/non-`WRITER` callers | Must-Have | Low |
1293
- | F-007-RQ-001 | Locale MUST propagate via `NEXT_USER_LOCALE` cookie (1-year max age) and `X-User-Locale` header | Must-Have | Low |
1297
+ | F-007-RQ-001 | Locale MUST propagate via `NEXT_USER_LOCALE` cookie (1-year max age when `rememberVisitorChoice` is on; session cookie when off) and `X-User-Locale` header | Must-Have | Low |
1298
+ | F-007-RQ-002 | First-visit locale MUST honor the admin-configured detection mode (`browser`, `country`, `browser_then_country`, `default`) from `site_settings.language_detection_settings` | Must-Have | Medium |
1294
1299
  | F-008-RQ-001 | Content revisions MUST use the hybrid snapshot/diff model keyed by `revision_type` enum | Must-Have | Medium |
1295
1300
  | F-009-RQ-001 | Navigation items MUST support `HEADER`, `FOOTER`, `SIDEBAR` locations with hierarchy and ordering | Must-Have | Low |
1296
1301
 
@@ -1303,7 +1308,7 @@ This subsection provides the detailed, testable requirements that operationalize
1303
1308
  | F-004-RQ-001 | Block type string, content object | Validated block content or ZodError | Schema validation synchronous |
1304
1309
  | F-005-RQ-002 | HTML or Tiptap JSON | Rendered editor content preserving DOM | No HTML loss across round-trip |
1305
1310
  | F-006-RQ-001 | Multipart file or presigned PUT | `media` row with metadata | Upload completes within browser timeout |
1306
- | F-007-RQ-001 | `lang` URL parameter or header | Locale cookie + `X-User-Locale` header | Proxy overhead < 5 ms |
1311
+ | F-007-RQ-001 | Locale cookie, `Accept-Language`, host geo-country header | Locale cookie + `X-User-Locale` header | Proxy overhead < 5 ms on cache hit; one Supabase round-trip per isolate per 60 s on miss (loaded concurrently with the auth lookup, shared across concurrent misses) |
1307
1312
 
1308
1313
  #### 2.2.1.3 Validation Rules — Content Delivery
1309
1314
 
@@ -1312,7 +1317,7 @@ This subsection provides the detailed, testable requirements that operationalize
1312
1317
  | F-001-RQ-002 | Image MIME must be in supported list | Only R2-served URLs accepted by next/image loader |
1313
1318
  | F-004-RQ-001 | Block `type` MUST exist in registry; content MUST pass Zod schema | Schema prevents XSS via typed fields |
1314
1319
  | F-006-RQ-001 | `size_bytes` must be non-negative; `object_key` unique | RLS: ADMIN/WRITER write; public read |
1315
- | F-007-RQ-001 | Locale MUST be in `SUPPORTED_LOCALES` (`en`, `fr`) | Cookie uses default security attributes |
1320
+ | F-007-RQ-001 | Locale MUST be an active `languages` row (hardcoded `FALLBACK_LOCALES` `en`/`fr` only when the DB is unreadable) | Cookie uses default security attributes |
1316
1321
  | F-008-RQ-001 | Snapshot version MUST be unique per `page_id` | Writes RLS-restricted to ADMIN/WRITER |
1317
1322
  | F-009-RQ-001 | `menu_location` MUST be one of the three enum values | Writes RLS-restricted to ADMIN/WRITER |
1318
1323
 
@@ -1323,7 +1328,7 @@ This subsection provides the detailed, testable requirements that operationalize
1323
1328
  | Requirement ID | Description | Priority | Complexity |
1324
1329
  |:--|:--|:--|:--|
1325
1330
  | F-002-RQ-001 | Supabase Auth session exchange MUST succeed via `app/auth/callback/route.ts` | Must-Have | Medium |
1326
- | F-002-RQ-002 | GitHub OAuth MUST be available as an alternative sign-in path | Should-Have | Low |
1331
+ | F-002-RQ-002 | *(Removed)* Third-party OAuth sign-in is intentionally not provided — it cannot be configured as part of a one-click deployment | Won't-Have | |
1327
1332
  | F-002-RQ-003 | `handle_new_user()` trigger MUST provision a `profiles` row on every `auth.users` insert | Must-Have | Medium |
1328
1333
  | F-003-RQ-001 | First registered user MUST become `ADMIN`; subsequent users MUST become `USER` | Must-Have | Medium |
1329
1334
  | F-003-RQ-002 | `/cms` MUST require `WRITER` or `ADMIN`; `/cms/admin`, `/cms/users`, `/cms/settings` MUST require `ADMIN` only | Must-Have | Medium |
@@ -1612,7 +1617,7 @@ The following matrix links features to the sections of the technical specificati
1612
1617
  | Build Status | `ecommerce:build` Nx standalone target currently not green (per §1.3.3.1) | F-013–F-022 |
1613
1618
  | Freemius Reconciliation | Webhook events acknowledged only; DB reconciliation pending (per §1.3.3.1) | F-017, F-021 |
1614
1619
  | Postal Code Shipping | Schema present; runtime resolver does not consume (per §1.3.3.1) | F-019 |
1615
- | Locale Count | Only `en` and `fr` in current release (per §1.3.3.4) | F-007, F-009, F-013 |
1620
+ | Locale Count | Seed provides `en` and `fr`; additional locales are added as `languages` rows at `/cms/settings/languages` | F-007, F-009, F-013 |
1616
1621
  | Module Boundaries | `libs/ui` MUST NOT depend on `apps/nextblock` | F-028 |
1617
1622
 
1618
1623
  ### 2.4.2 Performance Requirements
@@ -1674,7 +1679,7 @@ The following matrix links features to the sections of the technical specificati
1674
1679
  | FX override | `FX_API_BASE_URL` toggle allows switching provider without code change | F-018 |
1675
1680
  | Sandbox dataset | `SANDBOX_RESET_SQL` in `/api/cron/reset-sandbox/route.ts` must be regenerated as schema evolves | F-025, F-026 |
1676
1681
  | Package alignment | `@nextblock-cms/ecom` package name vs. `@nextblock-cms/ecommerce` alias (per §1.3.3.1) requires coordination when republished | F-013–F-022 |
1677
- | Locale expansion | Adding locales beyond `en`/`fr` requires updates to `SUPPORTED_LOCALES` and seed data | F-007 |
1682
+ | Locale expansion | Locales are DB-driven: add an active `languages` row (plus translations/content); `FALLBACK_LOCALES` in `proxy.ts` is only a DB-unreachable safety net | F-007 |
1678
1683
  | Block registry updates | New block types must satisfy F-024 contract and register in `blockRegistry.ts` | F-004, F-024 |
1679
1684
  | RLS policy review | Migration `00000000000006_setup_rls_and_grants.sql` should be audited on new table introduction | All DB-backed features |
1680
1685
 
@@ -2135,7 +2140,7 @@ graph TB
2135
2140
 
2136
2141
  #### 3.4.1.1 Responsibilities
2137
2142
 
2138
- Supabase provides PostgreSQL data storage, Row-Level Security, authentication (F-002), and session cookie management. GitHub OAuth is layered on top via the `components/GitHubLoginButton.tsx` component. Six email templates for the authentication flow (confirmation, email change, invitation, magic-link, reauthentication, password recovery) reside in `libs/db/src/supabase/templates/` and are synchronized with Supabase through `npm run configure:supabase-auth`.
2143
+ Supabase provides PostgreSQL data storage, Row-Level Security, authentication (F-002), and session cookie management. Six email templates for the authentication flow (confirmation, email change, invitation, magic-link, reauthentication, password recovery) reside in `libs/db/src/supabase/templates/` and are synchronized with Supabase through `npm run configure:supabase-auth`.
2139
2144
 
2140
2145
  #### 3.4.1.2 Environment Variables
2141
2146
 
@@ -2254,7 +2259,7 @@ The workspace implements a layered caching strategy aligned with the performance
2254
2259
  | `unstable_cache` (package activation) | 60 s | Avoids DB hit on every F-022 license check | `libs/db/src/lib/package-validation.ts` |
2255
2260
  | Next.js ISR (public layout) | 60 s (`PUBLIC_LAYOUT_REVALIDATE_SECONDS`) | Public content revalidation | `apps/nextblock/app/layout.tsx` |
2256
2261
  | Image cache TTL | 31,536,000 s (1 year) | Immutable optimized-image responses | `next.config.js` |
2257
- | Locale cookie | 31,536,000 s (1 year) | Persistent locale preference | `proxy.ts` |
2262
+ | Locale cookie | 31,536,000 s (1 year), or session cookie when "remember visitor's language" is off | Persistent locale preference | `proxy.ts` |
2258
2263
  | On-demand revalidation | N/A | Per-path invalidation via `REVALIDATE_SECRET_TOKEN` | `/api/revalidate` (F-027) |
2259
2264
  | bfcache-compatible HTML | `max-age=0, must-revalidate` | Preserves back-forward cache | `proxy.ts` |
2260
2265
 
@@ -2428,7 +2433,7 @@ The Default Technology Stack specified for this project included assumptions tha
2428
2433
  | **Terraform** IaC | Declarative configs (`vercel.json`, Supabase migrations, `project.json`) | Application-layer declarative configuration suffices given the managed-service stack |
2429
2434
  | **GitHub Actions** CI/CD | Vercel Git deploy + Node release scripts | Release cadence and library publishing managed via `tools/scripts/*` |
2430
2435
  | **Python / Flask** backend | TypeScript + Next.js 16 route handlers | Monolithic Next.js model enables RSC co-located with API routes |
2431
- | **Auth0** authentication | Supabase Auth + GitHub OAuth | Supabase Auth unifies database and identity for RLS-driven authorization (F-003) |
2436
+ | **Auth0** authentication | Supabase Auth (GoTrue) | Supabase Auth unifies database and identity for RLS-driven authorization (F-003) |
2432
2437
  | **MongoDB** database | Supabase PostgreSQL | Relational schema with RLS is better suited to the commerce and content data model |
2433
2438
  | **Langchain** AI framework | Not present (structural AI-readiness only) | Per §1.3.3.1, AI runtime features are "coming soon" |
2434
2439
  | **React-Native** mobile | Not used — web-only Next.js | Out-of-scope per §1.3.3.5 |
@@ -2615,9 +2620,9 @@ flowchart TB
2615
2620
  Supabase --> SyncSession[supabase.auth.getSession<br/>Sync Cookies]
2616
2621
  SyncSession --> Locale{Read<br/>NEXT_USER_LOCALE<br/>Cookie}
2617
2622
 
2618
- Locale -->|Valid: en or fr| SetLocaleHeader[Set X-User-Locale]
2619
- Locale -->|Invalid or Missing| DefaultEn[Default to 'en']
2620
- DefaultEn --> SetLocaleHeader
2623
+ Locale -->|Valid: active language| SetLocaleHeader[Set X-User-Locale]
2624
+ Locale -->|Invalid or Missing| Detect[Detect per settings:<br/>browser / country /<br/>browser_then_country / default]
2625
+ Detect --> SetLocaleHeader
2621
2626
 
2622
2627
  SetLocaleHeader --> GetUser[supabase.auth.getUser]
2623
2628
  GetUser --> CmsGuard{Path starts<br/>with /cms?}
@@ -4156,7 +4161,7 @@ The Zustand-backed cart (with `persist` middleware) captures items in the browse
4156
4161
 
4157
4162
  #### 5.1.3.3 Primary Authoring Flow
4158
4163
 
4159
- Authors sign in via Supabase Auth or GitHub OAuth, whose callback is handled at `app/auth/callback/route.ts`. After the role-based redirect from `lib/auth-redirects.ts`, authors enter `/cms/*` surfaces, which use Tiptap-based editors and the in-app `blockRegistry.ts` to compose pages. Published content triggers a Supabase webhook to `/api/revalidate`, which calls `revalidatePath` for `/slug` (pages) or `/article/slug` (posts).
4164
+ Authors sign in via Supabase Auth (email/password), whose email-confirmation callback is handled at `app/auth/callback/route.ts`. After the role-based redirect from `lib/auth-redirects.ts`, authors enter `/cms/*` surfaces, which use Tiptap-based editors and the in-app `blockRegistry.ts` to compose pages. Published content triggers a Supabase webhook to `/api/revalidate`, which calls `revalidatePath` for `/slug` (pages) or `/article/slug` (posts).
4160
4165
 
4161
4166
  #### 5.1.3.4 Integration, Transformation, and Storage
4162
4167
 
@@ -4703,7 +4708,7 @@ The primary error notification path is the Feedback System (F-029), which allows
4703
4708
 
4704
4709
  #### 5.4.4.1 Authentication
4705
4710
 
4706
- Supabase Auth provides email/password, magic-link, and GitHub OAuth. Sign-in server actions live in `app/actions.ts`; the OAuth callback resolves to `app/auth/callback/route.ts`, which exchanges the code for a session and then delegates to `lib/auth-redirects.ts` for the post-auth destination. The redirect resolver validates that any `next` parameter is a safe internal path, preserves `/reset-password`, routes `ADMIN`/`WRITER` to `safePath` or `/cms/dashboard`, sends `USER` without `full_name` to `/profile`, and otherwise falls back to `/`.
4711
+ Supabase Auth provides email/password and magic-link sign-in. Sign-in server actions live in `app/actions.ts`; the email-confirmation callback resolves to `app/auth/callback/route.ts`, which exchanges the code for a session and then delegates to `lib/auth-redirects.ts` for the post-auth destination. The redirect resolver validates that any `next` parameter is a safe internal path, preserves `/reset-password`, routes `ADMIN`/`WRITER` to `safePath` or `/cms/dashboard`, sends `USER` without `full_name` to `/profile`, and otherwise falls back to `/`.
4707
4712
 
4708
4713
  #### 5.4.4.2 Authorization Model
4709
4714
 
@@ -5656,7 +5661,7 @@ Declared in migration `00000000000001_setup_cms_core.sql`, these tables bootstra
5656
5661
  | Table | Primary Key | Key Columns / Constraints |
5657
5662
  |-------|-------------|----------------------------|
5658
5663
  | `site_settings` | `key` (text) | `value jsonb` — flat key/value store for global configuration |
5659
- | `profiles` | `id uuid` | FK → `auth.users(id) ON DELETE CASCADE`; `role user_role NOT NULL DEFAULT 'USER'`; `full_name`, `avatar_url`, `website`, `github_username`, `phone` |
5664
+ | `profiles` | `id uuid` | FK → `auth.users(id) ON DELETE CASCADE`; `role user_role NOT NULL DEFAULT 'USER'`; `full_name`, `avatar_url`, `website`, `phone` |
5660
5665
  | `user_addresses` | `id` (identity) | FK → `profiles(id)`; `address_type CHECK IN ('billing','shipping')`; `is_default`, `recipient_name`, `line1/2`, `city`, `state`, `postal_code`, `country_code` |
5661
5666
  | `languages` | `id bigint` | `code UNIQUE`, `name`, `is_default`, `is_active DEFAULT true` |
5662
5667
  | `media` | `id uuid` | `uploader_id → profiles`; `object_key UNIQUE`; `variants jsonb`; `blur_data_url`; `width`, `height`, `size_bytes`, `file_path`, `folder` |
@@ -7561,16 +7566,17 @@ flowchart TB
7561
7566
 
7562
7567
  #### 6.4.2.1 Identity Management
7563
7568
 
7564
- The identity provider is **Supabase Auth (GoTrue)**, consumed through the `@supabase/ssr` adapter layer. Four distinct authentication surfaces are exposed to end users:
7569
+ The identity provider is **Supabase Auth (GoTrue)**, consumed through the `@supabase/ssr` adapter layer. Three distinct authentication surfaces are exposed to end users:
7565
7570
 
7566
7571
  | Authentication Method | Entry Point | Implementation |
7567
7572
  |-----------------------|-------------|----------------|
7568
7573
  | Email/Password | `signInAction` in `app/actions.ts` | `supabase.auth.signInWithPassword` |
7569
7574
  | Account Registration | `signUpAction` in `app/actions.ts` | `supabase.auth.signUp` with email confirmation |
7570
7575
  | Password Recovery | `forgotPasswordAction` in `app/actions.ts` | `supabase.auth.resetPasswordForEmail` (OTP) |
7571
- | GitHub OAuth | `components/GitHubLoginButton.tsx` | `supabase.auth.signInWithOAuth({ provider: 'github' })` |
7572
7576
 
7573
- The OAuth callback route handler at `apps/nextblock/app/auth/callback/route.ts` receives a short-lived authorization code on the query string, invokes `supabase.auth.exchangeCodeForSession(code)` to convert the code to a session, fetches the user profile via `getProfileWithRoleServerSide(user.id)`, and delegates redirection logic to `resolvePostAuthRedirect` in `apps/nextblock/lib/auth-redirects.ts`. On any exchange failure, the handler redirects to `initialRedirectTo || '/'`.
7577
+ > **No third-party OAuth providers.** GitHub OAuth sign-in and account-linking were removed: they cannot be provisioned as part of a one-click deployment (each install needs its own GitHub OAuth app, client secret, and Supabase's manual-linking toggle, none of which can ship in a migration). The unrelated self-update "Connect GitHub" flow (`lib/updates/*`) is not an end-user auth surface and remains.
7578
+
7579
+ The auth callback route handler at `apps/nextblock/app/auth/callback/route.ts` receives a short-lived authorization code on the query string (used by the email-confirmation and recovery flows), invokes `supabase.auth.exchangeCodeForSession(code)` to convert the code to a session, fetches the user profile via `getProfileWithRoleServerSide(user.id)`, and delegates redirection logic to `resolvePostAuthRedirect` in `apps/nextblock/lib/auth-redirects.ts`. On any exchange failure, the handler redirects to `initialRedirectTo || '/'`.
7574
7580
 
7575
7581
  Six transactional email templates reside in `libs/db/src/supabase/templates/` — covering confirmation, email change, invitation, magic-link, reauthentication, and password recovery — and are synchronized with the Supabase project via `npm run configure:supabase-auth`.
7576
7582
 
@@ -7652,7 +7658,7 @@ The following sequence diagram depicts the full authentication flow from credent
7652
7658
  sequenceDiagram
7653
7659
  actor User
7654
7660
  participant Browser
7655
- participant AuthPage as /sign-in or OAuth Button
7661
+ participant AuthPage as /sign-in
7656
7662
  participant SbAuth as Supabase Auth (GoTrue)
7657
7663
  participant Callback as /auth/callback/route.ts
7658
7664
  participant Resolver as lib/auth-redirects.ts
@@ -7660,12 +7666,12 @@ sequenceDiagram
7660
7666
  participant Profiles as profiles table
7661
7667
  participant CMS as /cms/*
7662
7668
 
7663
- User->>Browser: Submit credentials or OAuth click
7664
- Browser->>AuthPage: POST (email+password) or redirect (OAuth)
7665
- AuthPage->>SbAuth: signInWithPassword / signInWithOAuth
7666
- SbAuth-->>Browser: OAuth code or Session cookies
7669
+ User->>Browser: Submit credentials
7670
+ Browser->>AuthPage: POST (email+password)
7671
+ AuthPage->>SbAuth: signInWithPassword
7672
+ SbAuth-->>Browser: Session cookies
7667
7673
 
7668
- alt OAuth flow
7674
+ alt Email confirmation / recovery (code exchange)
7669
7675
  Browser->>Callback: GET /auth/callback?code=...
7670
7676
  Callback->>SbAuth: exchangeCodeForSession(code)
7671
7677
  SbAuth-->>Callback: Set-Cookie (HttpOnly, Secure)
@@ -7720,7 +7726,7 @@ Authorization is driven by the `user_role` enum declared in migration `000000000
7720
7726
  | `USER` | Default for new signups | Customer profile, addresses, own orders |
7721
7727
  | `service_role` | Supabase service-role key | Webhooks, cron, admin server actions — bypasses RLS |
7722
7728
 
7723
- Role assignment at bootstrap is governed by the `handle_new_user()` SECURITY DEFINER trigger function bound to `auth.users` via `on_auth_user_created AFTER INSERT ON auth.users FOR EACH ROW`. The trigger acquires a `FOR UPDATE` lock on the `site_settings` row where `setting_key = 'is_admin_created'`, assigns `role='ADMIN'` if the latch is false (and sets the latch), otherwise assigns `role='USER'`, detects GitHub OAuth provider for the `github_username` column, and always creates a `profiles` row. The `FOR UPDATE` lock guarantees race-free first-admin election even under concurrent signup attempts.
7729
+ Role assignment at bootstrap is governed by the `handle_new_user()` SECURITY DEFINER trigger function bound to `auth.users` via `on_auth_user_created AFTER INSERT ON auth.users FOR EACH ROW`. The trigger acquires a `FOR UPDATE` lock on the `site_settings` row where `setting_key = 'is_admin_created'`, assigns `role='ADMIN'` if the latch is false (and sets the latch), otherwise assigns `role='USER'`, and always creates a `profiles` row. The `FOR UPDATE` lock guarantees race-free first-admin election even under concurrent signup attempts.
7724
7730
 
7725
7731
  #### 6.4.3.2 Permission Management and SECURITY DEFINER Helpers
7726
7732
 
@@ -7908,7 +7914,7 @@ The repository does not implement explicit PII masking transforms. The following
7908
7914
  | Error message sanitization | All error responses | Never contains JWT, service role key, or DB connection strings |
7909
7915
  | Admin user list | `/cms/users` | Email addresses displayed (no masking) — audience is ADMIN only |
7910
7916
 
7911
- The CMS admin user-list page uses the service-role client via `supabaseAdmin.auth.admin.listUsers` to enumerate users; since the audience of that surface is already `ADMIN`-gated, no masking transform is applied. PII in `profiles` (`full_name`, `avatar_url`, `phone`, `github_username`) is protected by RLS self-scoping rather than cryptographic masking.
7917
+ The CMS admin user-list page uses the service-role client via `supabaseAdmin.auth.admin.listUsers` to enumerate users; since the audience of that surface is already `ADMIN`-gated, no masking transform is applied. PII in `profiles` (`full_name`, `avatar_url`, `phone`) is protected by RLS self-scoping rather than cryptographic masking.
7912
7918
 
7913
7919
  #### 6.4.4.4 Secure Communication
7914
7920
 
@@ -8202,7 +8208,6 @@ For details beyond the scope of this section, consult:
8202
8208
  - `apps/nextblock/app/(auth-pages)/post-sign-in/page.tsx` — 27-line post-auth routing page
8203
8209
  - `apps/nextblock/app/unauthorized/page.tsx` — 26-line unauthorized display
8204
8210
  - `apps/nextblock/context/AuthContext.tsx` — Client auth state provider
8205
- - `apps/nextblock/components/GitHubLoginButton.tsx` — 37-line OAuth initiation
8206
8211
 
8207
8212
  #### CMS Guards and Admin Self-Protection
8208
8213
 
@@ -9691,7 +9696,7 @@ Four distinct persona classes consume the UI, with role-based affordances enforc
9691
9696
  The UI surface supports six top-level capability families, each rooted in a dedicated route group:
9692
9697
 
9693
9698
  1. **Public Content Delivery (F-001)** — locale-aware marketing, article, and product-detail rendering with SEO metadata, `robots.txt`, and `sitemap.xml`.
9694
- 2. **Authentication & Account (F-002)** — email/password, GitHub OAuth, magic-link, password reset, profile completion.
9699
+ 2. **Authentication & Account (F-002)** — email/password, magic-link, password reset, profile completion.
9695
9700
  3. **CMS Authoring (F-004, F-005, F-006, F-007, F-008, F-009)** — block-based page building, Tiptap rich text, media library, translations, revision history, navigation management.
9696
9701
  4. **Commerce (F-013 through F-021)** — catalog browsing, variant selection, cart, checkout, invoicing, customer order history.
9697
9702
  5. **Administration (F-003, F-030)** — user management, package activation, site settings, shipping/tax configuration.
@@ -9852,8 +9857,8 @@ The auth forms consume a typed message union defined in `apps/nextblock/componen
9852
9857
 
9853
9858
  | Path | File | Purpose |
9854
9859
  |:--|:--|:--|
9855
- | `/sign-in` | `app/(auth-pages)/sign-in/page.tsx` | Email/password + GitHub OAuth, forgot-password link, sandbox credentials alert |
9856
- | `/sign-up` | `app/(auth-pages)/sign-up/page.tsx` | Registration (email or GitHub) with success confirmation view |
9860
+ | `/sign-in` | `app/(auth-pages)/sign-in/page.tsx` | Email/password, forgot-password link, sandbox credentials alert |
9861
+ | `/sign-up` | `app/(auth-pages)/sign-up/page.tsx` | Registration (email) with success confirmation view |
9857
9862
  | `/forgot-password` | `app/(auth-pages)/forgot-password/page.tsx` | Password reset request form |
9858
9863
  | `/post-sign-in` | `app/(auth-pages)/post-sign-in/page.tsx` | Server routing endpoint (non-visual redirector) |
9859
9864
  | `/auth/callback` | `app/auth/callback/route.ts` | OAuth authorization-code exchange handler |
@@ -10046,10 +10051,9 @@ The component consumes `useTheme` from `next-themes`, uses a mounted-state guard
10046
10051
 
10047
10052
  | Interaction | Implementation |
10048
10053
  |:--|:--|
10049
- | Sign-In Form | Email/password + forgot-password link + `GitHubLoginButton` + `SandboxCredentialsAlert` when `NEXT_PUBLIC_IS_SANDBOX === 'true'`. Max-width `160` (mx-auto). Hidden `redirect` field preserved for post-sign-in routing |
10054
+ | Sign-In Form | Email/password + forgot-password link + `SandboxCredentialsAlert` when `NEXT_PUBLIC_IS_SANDBOX === 'true'`. Max-width `160` (mx-auto). Hidden `redirect` field preserved for post-sign-in routing |
10050
10055
  | Sign-Up Form | Shared layout with sign-in. Success view rendered when URL contains success flag: confirmation badge (`rounded-full bg-primary/10 text-primary`), `CheckCircle2` icon, email-check instructions with `Mail` icon, "Back to sign in" / "Use different email" actions |
10051
10056
  | Header Auth (`header-auth.tsx`) | Missing-env warning badge + disabled buttons when env vars absent; avatar dropdown (`h-8 w-8 rounded-full`) with profile/dashboard/logout when authenticated; sign-in/sign-up buttons for guests |
10052
- | GitHub OAuth (`GitHubLoginButton.tsx`) | Outline variant, full-width button with embedded GitHub SVG; invokes `supabase.auth.signInWithOAuth({ provider: 'github' })` with callback URL |
10053
10057
 
10054
10058
  ### 7.7.6 Commerce UI Interactions
10055
10059
 
@@ -10275,7 +10279,7 @@ Server-resolved locale (from `X-User-Locale` header) is bridged to client via `T
10275
10279
 
10276
10280
  #### Authentication Screens
10277
10281
 
10278
- - `apps/nextblock/app/(auth-pages)/sign-in/page.tsx` — Sign-in with GitHub OAuth
10282
+ - `apps/nextblock/app/(auth-pages)/sign-in/page.tsx` — Email/password sign-in
10279
10283
  - `apps/nextblock/app/(auth-pages)/sign-up/page.tsx` — Sign-up with success state
10280
10284
  - `apps/nextblock/app/(auth-pages)/forgot-password/page.tsx` — Password reset
10281
10285
  - `apps/nextblock/app/(auth-pages)/post-sign-in/page.tsx` — Server-routing endpoint
@@ -10298,7 +10302,6 @@ Server-resolved locale (from `X-User-Locale` header) is bridged to client via `T
10298
10302
  - `apps/nextblock/components/ResponsiveNav.tsx` — Hierarchical navigation
10299
10303
  - `apps/nextblock/components/theme-switcher.tsx` — Four-theme dropdown
10300
10304
  - `apps/nextblock/components/header-auth.tsx` — Authenticated vs guest auth UI
10301
- - `apps/nextblock/components/GitHubLoginButton.tsx` — OAuth trigger button
10302
10305
  - `apps/nextblock/components/form-message.tsx` — Typed auth message renderer
10303
10306
 
10304
10307
  #### Client Context Providers
@@ -10627,7 +10630,7 @@ Three distinct cloud providers compose the production footprint. The rationale f
10627
10630
  | Provider | Role | Selection Rationale |
10628
10631
  |:--|:--|:--|
10629
10632
  | Vercel | Compute + Edge + Cron + RUM | Zero-configuration Next.js 16 hosting; built-in image optimization; native App Router support; no container orchestration overhead; cron scheduling integrated with function lifecycle |
10630
- | Supabase | PostgreSQL + Auth + Storage metadata | Managed PostgreSQL 17 with built-in RLS; GoTrue auth service; GitHub OAuth support; SQL migration CLI; PITR included; generous free tier for open-core distribution |
10633
+ | Supabase | PostgreSQL + Auth + Storage metadata | Managed PostgreSQL 17 with built-in RLS; GoTrue auth service; SQL migration CLI; PITR included; generous free tier for open-core distribution |
10631
10634
  | Cloudflare R2 | S3-compatible object storage | Zero egress fees (superior to AWS S3 for media-heavy workloads); S3-compatible API usable via `@aws-sdk/client-s3 ^3.920.0`; global edge presence |
10632
10635
 
10633
10636
  **Documented deviations from the Default Technology Stack** (per §3.7.1):
@@ -10639,7 +10642,7 @@ Three distinct cloud providers compose the production footprint. The rationale f
10639
10642
  | Terraform | Declarative config across `vercel.json`, migrations, `config.toml`, `.env.exemple`, `nx.json` | No multi-service provisioning to orchestrate |
10640
10643
  | GitHub Actions | Vercel Git integration + Node release scripts in `tools/scripts/` | No CI test suite to gate; publication handled imperatively |
10641
10644
  | Python / Flask | TypeScript + Next.js route handlers | Unified stack across client + server |
10642
- | Auth0 | Supabase Auth (GoTrue) + GitHub OAuth | Integrated with RLS at the database layer |
10645
+ | Auth0 | Supabase Auth (GoTrue) | Integrated with RLS at the database layer |
10643
10646
  | MongoDB | Supabase PostgreSQL | Relational data model + RLS + SECURITY DEFINER helpers |
10644
10647
 
10645
10648
  ### 8.3.2 Core Services and Versions
@@ -10663,7 +10666,7 @@ Three distinct cloud providers compose the production footprint. The rationale f
10663
10666
  | PostgreSQL | `postgres` | `^3.8` (inventory dual-path fallback) | Direct SQL when RPC unavailable |
10664
10667
  | SSR client | `@supabase/ssr` | `^0.7.0` | Server-side cookie-based session |
10665
10668
  | Browser client | `@supabase/supabase-js` | `^2.77.0` | Client components + editor |
10666
- | Auth (GoTrue) | via `@supabase/ssr` | bundled | Email/password, GitHub OAuth, password reset |
10669
+ | Auth (GoTrue) | via `@supabase/ssr` | bundled | Email/password, password reset |
10667
10670
  | Realtime | bundled (enabled in `config.toml`) | bundled | WebSocket subscriptions |
10668
10671
  | CLI | `supabase` | invoked via `npx supabase` | Migrations, link, db push, config push |
10669
10672
 
@@ -155,12 +155,12 @@ const RELATION_TARGETS: readonly CustomBlockRelationTarget[] = [
155
155
  valueType: 'string',
156
156
  },
157
157
  {
158
- descriptionColumns: ['github_username', 'website'],
158
+ descriptionColumns: ['website'],
159
159
  displayColumn: 'full_name',
160
160
  label: 'Profiles',
161
161
  orderBy: 'full_name',
162
- searchableColumns: ['full_name', 'github_username', 'website'],
163
- selectColumns: ['id', 'full_name', 'avatar_url', 'github_username', 'website', 'role'],
162
+ searchableColumns: ['full_name', 'website'],
163
+ selectColumns: ['id', 'full_name', 'avatar_url', 'website', 'role'],
164
164
  table: 'profiles',
165
165
  valueColumn: 'id',
166
166
  valueType: 'string',
@@ -0,0 +1,247 @@
1
+ // Ordered ISO 3166-1 alpha-2 country -> primary language candidates (ISO 639-1).
2
+ // Used by the "visitor's country" language-detection mode: candidates are matched
3
+ // against the site's active language codes in order, so multilingual countries list
4
+ // their languages by prevalence (e.g. CH tries de, then fr, then it).
5
+ // This is a pragmatic "most likely language" map, not a census — sites that need a
6
+ // different mapping should simply not enable country-based detection.
7
+ export const COUNTRY_PRIMARY_LANGUAGES: Record<string, string[]> = {
8
+ AD: ['ca', 'es', 'fr'],
9
+ AE: ['ar', 'en'],
10
+ AF: ['fa', 'ps'],
11
+ AG: ['en'],
12
+ AI: ['en'],
13
+ AL: ['sq'],
14
+ AM: ['hy', 'ru'],
15
+ AO: ['pt'],
16
+ AR: ['es'],
17
+ AS: ['en'],
18
+ AT: ['de'],
19
+ AU: ['en'],
20
+ AW: ['nl', 'es'],
21
+ AX: ['sv'],
22
+ AZ: ['az', 'ru'],
23
+ BA: ['bs', 'hr', 'sr'],
24
+ BB: ['en'],
25
+ BD: ['bn'],
26
+ BE: ['nl', 'fr', 'de'],
27
+ BF: ['fr'],
28
+ BG: ['bg'],
29
+ BH: ['ar', 'en'],
30
+ BI: ['fr', 'rn'],
31
+ BJ: ['fr'],
32
+ BM: ['en'],
33
+ BN: ['ms', 'en'],
34
+ BO: ['es'],
35
+ BQ: ['nl'],
36
+ BR: ['pt'],
37
+ BS: ['en'],
38
+ BT: ['dz', 'en'],
39
+ BW: ['en', 'tn'],
40
+ BY: ['ru', 'be'],
41
+ BZ: ['en', 'es'],
42
+ CA: ['en', 'fr'],
43
+ CD: ['fr'],
44
+ CF: ['fr'],
45
+ CG: ['fr'],
46
+ CH: ['de', 'fr', 'it'],
47
+ CI: ['fr'],
48
+ CK: ['en'],
49
+ CL: ['es'],
50
+ CM: ['fr', 'en'],
51
+ CN: ['zh'],
52
+ CO: ['es'],
53
+ CR: ['es'],
54
+ CU: ['es'],
55
+ CV: ['pt'],
56
+ CW: ['nl', 'en', 'es'],
57
+ CY: ['el', 'tr', 'en'],
58
+ CZ: ['cs'],
59
+ DE: ['de'],
60
+ DJ: ['fr', 'ar'],
61
+ DK: ['da'],
62
+ DM: ['en'],
63
+ DO: ['es'],
64
+ DZ: ['ar', 'fr'],
65
+ EC: ['es'],
66
+ EE: ['et', 'ru'],
67
+ EG: ['ar', 'en'],
68
+ EH: ['ar'],
69
+ ER: ['ti', 'ar', 'en'],
70
+ ES: ['es', 'ca', 'gl'],
71
+ ET: ['am', 'en'],
72
+ FI: ['fi', 'sv'],
73
+ FJ: ['en', 'fj'],
74
+ FK: ['en'],
75
+ FM: ['en'],
76
+ FO: ['fo', 'da'],
77
+ FR: ['fr'],
78
+ GA: ['fr'],
79
+ GB: ['en'],
80
+ GD: ['en'],
81
+ GE: ['ka', 'ru'],
82
+ GF: ['fr'],
83
+ GG: ['en'],
84
+ GH: ['en'],
85
+ GI: ['en', 'es'],
86
+ GL: ['kl', 'da'],
87
+ GM: ['en'],
88
+ GN: ['fr'],
89
+ GP: ['fr'],
90
+ GQ: ['es', 'fr', 'pt'],
91
+ GR: ['el'],
92
+ GT: ['es'],
93
+ GU: ['en'],
94
+ GW: ['pt'],
95
+ GY: ['en'],
96
+ HK: ['zh', 'en'],
97
+ HN: ['es'],
98
+ HR: ['hr'],
99
+ HT: ['fr', 'ht'],
100
+ HU: ['hu'],
101
+ ID: ['id'],
102
+ IE: ['en', 'ga'],
103
+ IL: ['he', 'ar', 'en', 'ru'],
104
+ IM: ['en'],
105
+ IN: ['hi', 'en'],
106
+ IQ: ['ar', 'ku'],
107
+ IR: ['fa'],
108
+ IS: ['is'],
109
+ IT: ['it'],
110
+ JE: ['en'],
111
+ JM: ['en'],
112
+ JO: ['ar', 'en'],
113
+ JP: ['ja'],
114
+ KE: ['en', 'sw'],
115
+ KG: ['ky', 'ru'],
116
+ KH: ['km'],
117
+ KI: ['en'],
118
+ KM: ['fr', 'ar'],
119
+ KN: ['en'],
120
+ KP: ['ko'],
121
+ KR: ['ko'],
122
+ KW: ['ar', 'en'],
123
+ KY: ['en'],
124
+ KZ: ['kk', 'ru'],
125
+ LA: ['lo'],
126
+ LB: ['ar', 'fr', 'en'],
127
+ LC: ['en'],
128
+ LI: ['de'],
129
+ LK: ['si', 'ta', 'en'],
130
+ LR: ['en'],
131
+ LS: ['en', 'st'],
132
+ LT: ['lt', 'ru'],
133
+ LU: ['fr', 'de', 'lb'],
134
+ LV: ['lv', 'ru'],
135
+ LY: ['ar'],
136
+ MA: ['ar', 'fr'],
137
+ MC: ['fr'],
138
+ MD: ['ro', 'ru'],
139
+ ME: ['sr'],
140
+ MF: ['fr'],
141
+ MG: ['fr', 'mg'],
142
+ MH: ['en'],
143
+ MK: ['mk', 'sq'],
144
+ ML: ['fr'],
145
+ MM: ['my'],
146
+ MN: ['mn'],
147
+ MO: ['zh', 'pt'],
148
+ MP: ['en'],
149
+ MQ: ['fr'],
150
+ MR: ['ar', 'fr'],
151
+ MS: ['en'],
152
+ MT: ['mt', 'en'],
153
+ MU: ['en', 'fr'],
154
+ MV: ['dv', 'en'],
155
+ MW: ['en', 'ny'],
156
+ MX: ['es'],
157
+ MY: ['ms', 'en', 'zh'],
158
+ MZ: ['pt'],
159
+ NA: ['en', 'af', 'de'],
160
+ NC: ['fr'],
161
+ NE: ['fr'],
162
+ NF: ['en'],
163
+ NG: ['en'],
164
+ NI: ['es'],
165
+ NL: ['nl'],
166
+ NO: ['no', 'nb'],
167
+ NP: ['ne'],
168
+ NR: ['en'],
169
+ NU: ['en'],
170
+ NZ: ['en', 'mi'],
171
+ OM: ['ar', 'en'],
172
+ PA: ['es'],
173
+ PE: ['es'],
174
+ PF: ['fr'],
175
+ PG: ['en'],
176
+ PH: ['en', 'tl'],
177
+ PK: ['ur', 'en'],
178
+ PL: ['pl'],
179
+ PM: ['fr'],
180
+ PR: ['es', 'en'],
181
+ PS: ['ar'],
182
+ PT: ['pt'],
183
+ PW: ['en'],
184
+ PY: ['es', 'gn'],
185
+ QA: ['ar', 'en'],
186
+ RE: ['fr'],
187
+ RO: ['ro'],
188
+ RS: ['sr'],
189
+ RU: ['ru'],
190
+ RW: ['rw', 'en', 'fr'],
191
+ SA: ['ar', 'en'],
192
+ SB: ['en'],
193
+ SC: ['en', 'fr'],
194
+ SD: ['ar', 'en'],
195
+ SE: ['sv'],
196
+ SG: ['en', 'zh', 'ms', 'ta'],
197
+ SH: ['en'],
198
+ SI: ['sl'],
199
+ SJ: ['no', 'nb'],
200
+ SK: ['sk'],
201
+ SL: ['en'],
202
+ SM: ['it'],
203
+ SN: ['fr'],
204
+ SO: ['so', 'ar'],
205
+ SR: ['nl'],
206
+ SS: ['en', 'ar'],
207
+ ST: ['pt'],
208
+ SV: ['es'],
209
+ SX: ['nl', 'en'],
210
+ SY: ['ar'],
211
+ SZ: ['en', 'ss'],
212
+ TC: ['en'],
213
+ TD: ['fr', 'ar'],
214
+ TG: ['fr'],
215
+ TH: ['th'],
216
+ TJ: ['tg', 'ru'],
217
+ TK: ['en'],
218
+ TL: ['pt'],
219
+ TM: ['tk', 'ru'],
220
+ TN: ['ar', 'fr'],
221
+ TO: ['en', 'to'],
222
+ TR: ['tr'],
223
+ TT: ['en'],
224
+ TV: ['en'],
225
+ TW: ['zh'],
226
+ TZ: ['sw', 'en'],
227
+ UA: ['uk', 'ru'],
228
+ UG: ['en', 'sw'],
229
+ UM: ['en'],
230
+ US: ['en', 'es'],
231
+ UY: ['es'],
232
+ UZ: ['uz', 'ru'],
233
+ VA: ['it'],
234
+ VC: ['en'],
235
+ VE: ['es'],
236
+ VG: ['en'],
237
+ VI: ['en'],
238
+ VN: ['vi'],
239
+ VU: ['en', 'fr'],
240
+ WF: ['fr'],
241
+ WS: ['en', 'sm'],
242
+ YE: ['ar'],
243
+ YT: ['fr'],
244
+ ZA: ['en', 'af', 'zu'],
245
+ ZM: ['en'],
246
+ ZW: ['en', 'sn'],
247
+ };