update arabic to be default
Build & Push / Pipeline Tests (push) Successful in 1m56s
Test / Type Check (all packages) (push) Successful in 56s
Build & Push / Build & Push Docker Image (push) Successful in 3m39s
Test / API Unit Tests (push) Successful in 1m12s
Test / Homepage Unit Tests (push) Successful in 52s
Test / Carplace Unit Tests (push) Successful in 43s
Test / Admin Unit Tests (push) Successful in 52s
Test / Dashboard Unit Tests (push) Successful in 45s
Test / API Integration Tests (push) Successful in 1m8s
Build & Push / Pipeline Tests (push) Successful in 1m56s
Test / Type Check (all packages) (push) Successful in 56s
Build & Push / Build & Push Docker Image (push) Successful in 3m39s
Test / API Unit Tests (push) Successful in 1m12s
Test / Homepage Unit Tests (push) Successful in 52s
Test / Carplace Unit Tests (push) Successful in 43s
Test / Admin Unit Tests (push) Successful in 52s
Test / Dashboard Unit Tests (push) Successful in 45s
Test / API Integration Tests (push) Successful in 1m8s
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import type { Metadata } from 'next'
|
||||
import { cookies } from 'next/headers'
|
||||
import Script from 'next/script'
|
||||
import CarplaceShell from '@/components/CarplaceShell'
|
||||
import { getCarplaceMessages } from '@/lib/carplace/messages'
|
||||
import { getCarplaceLanguage } from '@/lib/i18n.server'
|
||||
@@ -36,8 +37,9 @@ export default async function RootLayout({ children }: { children: React.ReactNo
|
||||
return (
|
||||
<html lang={language} dir={language === 'ar' ? 'rtl' : 'ltr'} suppressHydrationWarning>
|
||||
<head>
|
||||
{/* Runs before hydration to prevent flash of wrong theme */}
|
||||
<script
|
||||
<Script
|
||||
id="carplace-theme-bootstrap"
|
||||
strategy="beforeInteractive"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html:
|
||||
"(function(){try{var m=document.cookie.match(/(?:^|; )rentaldrivego-theme=([^;]+)/);var theme=m?decodeURIComponent(m[1]):localStorage.getItem('rentaldrivego-theme');if(theme!=='light'&&theme!=='dark'){theme='dark'}document.documentElement.classList.toggle('dark',theme==='dark');document.documentElement.style.colorScheme=theme;document.body&&document.body.setAttribute('data-theme',theme)}catch(e){}})();",
|
||||
|
||||
@@ -118,15 +118,6 @@ export function getFooterContent(language: CarplaceLanguage): FooterContent {
|
||||
return footerLabels[language]
|
||||
}
|
||||
|
||||
function detectBrowserLanguage(): CarplaceLanguage | null {
|
||||
if (typeof navigator === 'undefined') return null
|
||||
for (const language of Array.from(navigator.languages ?? [navigator.language])) {
|
||||
const code = language.split('-')[0].toLowerCase()
|
||||
if (isCarplaceLanguage(code)) return code
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
type PreferencesContextValue = {
|
||||
language: CarplaceLanguage
|
||||
theme: Theme
|
||||
@@ -145,7 +136,7 @@ export function useCarplacePreferences() {
|
||||
|
||||
export default function CarplaceShell({
|
||||
children,
|
||||
initialLanguage = 'en',
|
||||
initialLanguage = 'ar',
|
||||
initialTheme = 'dark',
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
@@ -180,7 +171,7 @@ export default function CarplaceShell({
|
||||
|
||||
useEffect(() => {
|
||||
const storedLanguage = readScopedPreference(SHARED_LANGUAGE_KEY)
|
||||
const resolvedLanguage = isCarplaceLanguage(storedLanguage) ? storedLanguage : detectBrowserLanguage()
|
||||
const resolvedLanguage = isCarplaceLanguage(storedLanguage) ? storedLanguage : 'ar'
|
||||
if (resolvedLanguage) setLanguageState(resolvedLanguage)
|
||||
|
||||
const storedTheme = readScopedPreference(SHARED_THEME_KEY)
|
||||
|
||||
@@ -22,9 +22,9 @@ describe('getCarplaceLanguage', () => {
|
||||
await expect(getCarplaceLanguage()).resolves.toBe(language)
|
||||
})
|
||||
|
||||
it('defaults to English when the shared cookie is absent or invalid', async () => {
|
||||
await expect(getCarplaceLanguage()).resolves.toBe('en')
|
||||
it('defaults to Arabic when the shared cookie is absent or invalid', async () => {
|
||||
await expect(getCarplaceLanguage()).resolves.toBe('ar')
|
||||
cookieValues.set(SHARED_LANGUAGE_COOKIE, 'es')
|
||||
await expect(getCarplaceLanguage()).resolves.toBe('en')
|
||||
await expect(getCarplaceLanguage()).resolves.toBe('ar')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3,5 +3,5 @@ import { SHARED_LANGUAGE_COOKIE, isCarplaceLanguage, type CarplaceLanguage } fro
|
||||
|
||||
export async function getCarplaceLanguage(): Promise<CarplaceLanguage> {
|
||||
const cookieValue = (await cookies()).get(SHARED_LANGUAGE_COOKIE)?.value
|
||||
return isCarplaceLanguage(cookieValue) ? cookieValue : 'en'
|
||||
return isCarplaceLanguage(cookieValue) ? cookieValue : 'ar'
|
||||
}
|
||||
|
||||
@@ -42,8 +42,8 @@ describe('Carplace language bootstrap', () => {
|
||||
|
||||
it.each([
|
||||
['ar-MA,fr;q=0.8', 'ar'],
|
||||
['de-DE,fr-FR;q=0.7', 'fr'],
|
||||
['de-DE,es;q=0.9', 'en'],
|
||||
['de-DE,fr-FR;q=0.7', 'ar'],
|
||||
['de-DE,es;q=0.9', 'ar'],
|
||||
])('resolves %s as %s', async (acceptLanguage, expected) => {
|
||||
const { proxy } = await import('./proxy')
|
||||
const response = proxy(request({ headers: { 'accept-language': acceptLanguage } }) as never) as any
|
||||
|
||||
@@ -2,6 +2,7 @@ import { NextResponse } from 'next/server'
|
||||
import type { NextRequest } from 'next/server'
|
||||
|
||||
const SHARED_LANGUAGE_COOKIE = 'rentaldrivego-language'
|
||||
const DEFAULT_LANGUAGE: Language = 'ar'
|
||||
|
||||
type Language = 'en' | 'fr' | 'ar'
|
||||
|
||||
@@ -9,15 +10,6 @@ function isValidLanguage(value: string | null | undefined): value is Language {
|
||||
return value === 'en' || value === 'fr' || value === 'ar'
|
||||
}
|
||||
|
||||
function detectFromAcceptLanguage(header: string | null): Language {
|
||||
if (!header) return 'en'
|
||||
for (const entry of header.split(',')) {
|
||||
const code = entry.split(';')[0].trim().split('-')[0].toLowerCase()
|
||||
if (isValidLanguage(code)) return code
|
||||
}
|
||||
return 'en'
|
||||
}
|
||||
|
||||
export function proxy(request: NextRequest) {
|
||||
if (request.headers.has('x-middleware-subrequest')) {
|
||||
return new NextResponse('Unsupported internal request header', { status: 400 })
|
||||
@@ -26,7 +18,7 @@ export function proxy(request: NextRequest) {
|
||||
const currentLanguage = request.cookies.get(SHARED_LANGUAGE_COOKIE)?.value
|
||||
if (isValidLanguage(currentLanguage)) return NextResponse.next()
|
||||
|
||||
const language = detectFromAcceptLanguage(request.headers.get('accept-language'))
|
||||
const language = DEFAULT_LANGUAGE
|
||||
const requestHeaders = new Headers(request.headers)
|
||||
const existingCookies = request.headers.get('cookie') ?? ''
|
||||
const languageCookie = `${SHARED_LANGUAGE_COOKIE}=${language}`
|
||||
|
||||
Reference in New Issue
Block a user