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

This commit is contained in:
root
2026-08-31 19:50:27 -04:00
parent f00d2a8aea
commit cb8bf63218
23 changed files with 58 additions and 77 deletions
+4 -2
View File
@@ -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){}})();",
+2 -11
View File
@@ -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)
+3 -3
View File
@@ -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')
})
})
+1 -1
View File
@@ -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'
}
+2 -2
View File
@@ -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 -10
View File
@@ -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}`