Skip to content

Commit 101e715

Browse files
heiskrpeterbe
andauthored
userLanguage in browser instead of XHR (github#29782)
* userLanguage in browser instead of XHR * refactors on pr 29782 (github#29798) Co-authored-by: Peter Bengtsson <[email protected]>
1 parent b98a79b commit 101e715

10 files changed

+46
-15
lines changed

components/hooks/useSession.ts

-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ export default async function fetcher<JSON = any>(
1212
export type Session = {
1313
isSignedIn: boolean
1414
csrfToken?: string
15-
userLanguage: string // en, es, ja, cn
1615
}
1716

1817
// React hook version

components/hooks/useUserLanguage.ts

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { useState, useEffect } from 'react'
2+
import Cookies from 'js-cookie'
3+
4+
import { languageKeys } from '../../lib/languages.js'
5+
import { PREFERRED_LOCALE_COOKIE_NAME } from '../../lib/constants.js'
6+
7+
export function useUserLanguage() {
8+
const [userLanguage, setUserLanguage] = useState<string>('en')
9+
10+
useEffect(() => {
11+
const languagePreferred = [
12+
Cookies.get(PREFERRED_LOCALE_COOKIE_NAME),
13+
navigator.language,
14+
...navigator.languages,
15+
]
16+
.filter(Boolean)
17+
// If it comes from `navigator.language` it most likely will contain
18+
// the region. E.g. `en-US` but in our application, we don't use
19+
// the region.
20+
.map((lang) => lang && lang.slice(0, 2).toLowerCase())
21+
.find((lang) => lang && languageKeys.includes(lang))
22+
if (languagePreferred) {
23+
setUserLanguage(languagePreferred)
24+
}
25+
}, [])
26+
27+
return { userLanguage }
28+
}

components/page-header/HeaderNotifications.tsx

+2-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { useMainContext } from 'components/context/MainContext'
66
import { useTranslation } from 'components/hooks/useTranslation'
77
import { ExcludesNull } from 'components/lib/ExcludesNull'
88
import { useVersion } from 'components/hooks/useVersion'
9-
import { useSession } from 'components/hooks/useSession'
9+
import { useUserLanguage } from 'components/hooks/useUserLanguage'
1010
import styles from './HeaderNotifications.module.scss'
1111

1212
enum NotificationType {
@@ -23,8 +23,7 @@ export const HeaderNotifications = () => {
2323
const router = useRouter()
2424
const { currentVersion } = useVersion()
2525
const { relativePath, allVersions, data, currentPathWithoutLanguage, page } = useMainContext()
26-
const { session } = useSession()
27-
const userLanguage = session?.userLanguage
26+
const { userLanguage } = useUserLanguage()
2827
const { languages } = useLanguages()
2928

3029
const { t } = useTranslation('header')

components/page-header/LanguagePicker.tsx

+9-3
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ import Cookies from 'js-cookie'
44
import { useLanguages } from 'components/context/LanguagesContext'
55
import { Picker } from 'components/ui/Picker'
66
import { useTranslation } from 'components/hooks/useTranslation'
7-
8-
// This value is replicated in two places! See middleware/detect-language.js
9-
const PREFERRED_LOCALE_COOKIE_NAME = 'preferredlang'
7+
import { PREFERRED_LOCALE_COOKIE_NAME } from '../../lib/constants.js'
108

119
type Props = {
1210
variant?: 'inline'
@@ -31,6 +29,14 @@ export const LanguagePicker = ({ variant }: Props) => {
3129

3230
function rememberPreferredLanguage(value: string) {
3331
try {
32+
// The reason we use a cookie and not local storage is because
33+
// this cookie value is used and needed by the server. For
34+
// example, when doing `GET /some/page` we need the cookie
35+
// to redirect to `Location: /ja/some/page`.
36+
// It's important it's *not* an HttpOnly cookie because we
37+
// need this in the client-side which is used to determine
38+
// the UI about displaying notifications about preferred
39+
// language if your cookie doesn't match the current URL.
3440
Cookies.set(PREFERRED_LOCALE_COOKIE_NAME, value, {
3541
expires: 365,
3642
secure: document.location.protocol !== 'http:',

lib/constants.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const PREFERRED_LOCALE_COOKIE_NAME = 'preferredlang'

middleware/api/session.js

-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ router.get('/', (req, res) => {
99
res.json({
1010
isSignedIn: Boolean(req.cookies?.dotcom_user),
1111
csrfToken: req.csrfToken?.() || '',
12-
userLanguage: req.userLanguage,
1312
})
1413
})
1514

middleware/detect-language.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
import languages, { languageKeys } from '../lib/languages.js'
22
import parser from 'accept-language-parser'
33

4-
const chineseRegions = ['CN', 'HK']
4+
import { PREFERRED_LOCALE_COOKIE_NAME } from '../lib/constants.js'
55

6-
// This value is replicated in two places! See <LanguagePicker/> component.
7-
// Note, the only reason this is exported is to benefit the tests.
8-
export const PREFERRED_LOCALE_COOKIE_NAME = 'preferredlang'
6+
const chineseRegions = ['CN', 'HK']
97

108
function translationExists(language) {
119
if (language.code === 'zh') {

middleware/fast-root-redirect.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { cacheControlFactory } from './cache-control.js'
2-
import { PREFERRED_LOCALE_COOKIE_NAME, getLanguageCodeFromHeader } from './detect-language.js'
2+
import { getLanguageCodeFromHeader } from './detect-language.js'
3+
import { PREFERRED_LOCALE_COOKIE_NAME } from '../lib/constants.js'
34

45
const cacheControl = cacheControlFactory(0)
56

tests/routing/deprecated-enterprise-versions.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { describe, jest, test } from '@jest/globals'
33
import enterpriseServerReleases from '../../lib/enterprise-server-releases.js'
44
import { get, getDOM } from '../helpers/e2etest.js'
55
import { SURROGATE_ENUMS } from '../../middleware/set-fastly-surrogate-key.js'
6-
import { PREFERRED_LOCALE_COOKIE_NAME } from '../../middleware/detect-language.js'
6+
import { PREFERRED_LOCALE_COOKIE_NAME } from '../../lib/constants.js'
77

88
jest.useFakeTimers({ legacyFakeTimers: true })
99

tests/routing/redirects.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import enterpriseServerReleases, {
99
import Page from '../../lib/page.js'
1010
import { get, head } from '../helpers/e2etest.js'
1111
import versionSatisfiesRange from '../../lib/version-satisfies-range.js'
12-
import { PREFERRED_LOCALE_COOKIE_NAME } from '../../middleware/detect-language.js'
12+
import { PREFERRED_LOCALE_COOKIE_NAME } from '../../lib/constants.js'
1313

1414
const __dirname = path.dirname(fileURLToPath(import.meta.url))
1515

0 commit comments

Comments
 (0)