Skip to content

Commit 881a6d2

Browse files
author
Peter Bengtsson
authored
Revert "redirect to your preferred language (#25664)" (#25869)
This reverts commit a9947c0.
1 parent 9fd4fe0 commit 881a6d2

6 files changed

Lines changed: 44 additions & 158 deletions

File tree

components/page-header/LanguagePicker.tsx

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
11
import { useRouter } from 'next/router'
2-
import Cookies from 'js-cookie'
3-
42
import { Link } from 'components/Link'
53
import { useLanguages } from 'components/context/LanguagesContext'
64
import { Picker } from 'components/ui/Picker'
75
import { useTranslation } from 'components/hooks/useTranslation'
86

9-
// This value is replicated in two places! See middleware/detect-language.js
10-
const PREFERRED_LOCALE_COOKIE_NAME = 'preferredlang'
11-
127
type Props = {
138
variant?: 'inline'
149
}
@@ -27,22 +22,6 @@ export const LanguagePicker = ({ variant }: Props) => {
2722
// in a "denormalized" way.
2823
const routerPath = router.asPath.split('#')[0]
2924

30-
function rememberPreferredLanguage(code: string) {
31-
try {
32-
Cookies.set(PREFERRED_LOCALE_COOKIE_NAME, code, {
33-
expires: 365,
34-
secure: document.location.protocol !== 'http:',
35-
})
36-
} catch (err) {
37-
// You can never be too careful because setting a cookie
38-
// can fail. For example, some browser
39-
// extensions disallow all setting of cookies and attempts
40-
// at the `document.cookie` setter could throw. Just swallow
41-
// and move on.
42-
console.warn('Unable to set preferred language cookie', err)
43-
}
44-
}
45-
4625
return (
4726
<Picker
4827
variant={variant}
@@ -54,13 +33,7 @@ export const LanguagePicker = ({ variant }: Props) => {
5433
text: lang.nativeName || lang.name,
5534
selected: lang === selectedLang,
5635
item: (
57-
<Link
58-
href={routerPath}
59-
locale={lang.code}
60-
onClick={() => {
61-
rememberPreferredLanguage(lang.code)
62-
}}
63-
>
36+
<Link href={routerPath} locale={lang.code}>
6437
{lang.nativeName ? (
6538
<>
6639
<span lang={lang.code}>{lang.nativeName}</span> (

lib/get-redirect.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ const nonEnterpriseDefaultVersionPrefix = `/${nonEnterpriseDefaultVersion}`
99

1010
// Return the new URI if there is one, otherwise return undefined.
1111
export default function getRedirect(uri, context) {
12-
const { redirects, userLanguage } = context
12+
const { redirects, pages } = context
1313

14-
let language = userLanguage || 'en'
14+
let language = 'en'
1515
let withoutLanguage = uri
1616
if (languagePrefixRegex.test(uri)) {
1717
language = uri.match(languagePrefixRegex)[1]
@@ -109,7 +109,12 @@ export default function getRedirect(uri, context) {
109109
}
110110

111111
if (basicCorrection) {
112-
return getRedirect(basicCorrection, context) || basicCorrection
112+
return (
113+
getRedirect(basicCorrection, {
114+
redirects,
115+
pages,
116+
}) || basicCorrection
117+
)
113118
}
114119

115120
if (withoutLanguage.startsWith('/admin/')) {

middleware/detect-language.js

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
1-
import languages, { languageKeys } from '../lib/languages.js'
1+
import libLanguages from '../lib/languages.js'
22
import parser from 'accept-language-parser'
3+
const languageCodes = Object.keys(libLanguages)
34

45
const chineseRegions = ['CN', 'HK']
56

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'
9-
107
function translationExists(language) {
118
if (language.code === 'zh') {
129
return chineseRegions.includes(language.region)
1310
}
14-
return languageKeys.includes(language.code)
11+
return languageCodes.includes(language.code)
1512
}
1613

1714
function getLanguageCode(language) {
@@ -20,41 +17,33 @@ function getLanguageCode(language) {
2017

2118
function getUserLanguage(browserLanguages) {
2219
try {
20+
let userLanguage = getLanguageCode(browserLanguages[0])
2321
let numTopPreferences = 1
2422
for (let lang = 0; lang < browserLanguages.length; lang++) {
2523
// If language has multiple regions, Chrome adds the non-region language to list
2624
if (lang > 0 && browserLanguages[lang].code !== browserLanguages[lang - 1].code)
2725
numTopPreferences++
2826
if (translationExists(browserLanguages[lang]) && numTopPreferences < 3) {
29-
return getLanguageCode(browserLanguages[lang])
27+
userLanguage = getLanguageCode(browserLanguages[lang])
28+
break
3029
}
3130
}
31+
return userLanguage
3232
} catch {
3333
return undefined
3434
}
3535
}
3636

37-
function getUserLanguageFromCookie(req) {
38-
const value = req.cookies[PREFERRED_LOCALE_COOKIE_NAME]
39-
// But if it's a WIP language, reject it.
40-
if (value && languages[value] && !languages[value].wip) {
41-
return value
42-
}
43-
}
44-
4537
// determine language code from a path. Default to en if no valid match
4638
export function getLanguageCodeFromPath(path) {
4739
const maybeLanguage = (path.split('/')[path.startsWith('/_next/data/') ? 4 : 1] || '').slice(0, 2)
48-
return languageKeys.includes(maybeLanguage) ? maybeLanguage : 'en'
40+
return languageCodes.includes(maybeLanguage) ? maybeLanguage : 'en'
4941
}
5042

5143
export default function detectLanguage(req, res, next) {
5244
req.language = getLanguageCodeFromPath(req.path)
5345
// Detecting browser language by user preference
54-
req.userLanguage = getUserLanguageFromCookie(req)
55-
if (!req.userLanguage) {
56-
const browserLanguages = parser.parse(req.headers['accept-language'])
57-
req.userLanguage = getUserLanguage(browserLanguages)
58-
}
46+
const browserLanguages = parser.parse(req.headers['accept-language'])
47+
req.userLanguage = getUserLanguage(browserLanguages)
5948
return next()
6049
}

middleware/redirects/handle-redirects.js

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import patterns from '../../lib/patterns.js'
22
import { URL } from 'url'
3-
import { pathLanguagePrefixed } from '../../lib/languages.js'
3+
import languages, { pathLanguagePrefixed } from '../../lib/languages.js'
44
import getRedirect from '../../lib/get-redirect.js'
55
import { cacheControlFactory } from '../cache-control.js'
66

@@ -13,7 +13,16 @@ export default function handleRedirects(req, res, next) {
1313

1414
// blanket redirects for languageless homepage
1515
if (req.path === '/') {
16-
const language = getLanguage(req)
16+
let language = 'en'
17+
18+
// if set, redirect to user's preferred language translation or else English
19+
if (
20+
req.context.userLanguage &&
21+
languages[req.context.userLanguage] &&
22+
!languages[req.context.userLanguage].wip
23+
) {
24+
language = req.context.userLanguage
25+
}
1726

1827
// Undo the cookie setting that CSRF sets.
1928
res.removeHeader('set-cookie')
@@ -61,12 +70,17 @@ export default function handleRedirects(req, res, next) {
6170
// needs to become `/en/authentication/connecting-to-github-with-ssh`
6271
const possibleRedirectTo = `/en${req.path}`
6372
if (possibleRedirectTo in req.context.pages) {
64-
const language = getLanguage(req)
65-
73+
// As of Jan 2022 we always redirect to `/en` if the URL doesn't
74+
// specify a language. ...except for the root home page (`/`).
75+
// It's unfortunate but that's how it currently works.
76+
// It's tracked in #1145
77+
// Perhaps a more ideal solution would be to do something similar to
78+
// the code above for `req.path === '/'` where we look at the user
79+
// agent for a header and/or cookie.
6680
// Note, it's important to use `req.url` here and not `req.path`
6781
// because the full URL can contain query strings.
6882
// E.g. `/foo?json=breadcrumbs`
69-
redirect = `/${language}${req.url}`
83+
redirect = `/en${req.url}`
7084
}
7185
}
7286

@@ -98,13 +112,6 @@ export default function handleRedirects(req, res, next) {
98112
return res.redirect(permanent ? 301 : 302, redirect)
99113
}
100114

101-
function getLanguage(req, default_ = 'en') {
102-
// req.context.userLanguage, if it truthy, is always a valid supported
103-
// language. It's whatever was in the user's request but filtered
104-
// based on non-WIP languages in lib/languages.js
105-
return req.context.userLanguage || default_
106-
}
107-
108115
function usePermanentRedirect(req) {
109116
// If the redirect was to essentially swap `enterprise-server@latest`
110117
// for `enterprise-server@3.x` then, we definitely don't want to

tests/routing/redirects.js

Lines changed: 6 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,12 @@ import { fileURLToPath } from 'url'
22
import path from 'path'
33
import { isPlainObject } from 'lodash-es'
44
import supertest from 'supertest'
5-
import { jest } from '@jest/globals'
6-
75
import createApp from '../../lib/app.js'
86
import enterpriseServerReleases from '../../lib/enterprise-server-releases.js'
97
import Page from '../../lib/page.js'
108
import { get } from '../helpers/supertest.js'
119
import versionSatisfiesRange from '../../lib/version-satisfies-range.js'
12-
import { PREFERRED_LOCALE_COOKIE_NAME } from '../../middleware/detect-language.js'
10+
import { jest } from '@jest/globals'
1311

1412
const __dirname = path.dirname(fileURLToPath(import.meta.url))
1513

@@ -134,28 +132,6 @@ describe('redirects', () => {
134132
expect(res.headers.location).toBe('/ja')
135133
expect(res.headers['cache-control']).toBe('private, no-store')
136134
})
137-
test('homepage redirects to preferred language by cookie', async () => {
138-
const res = await get('/', {
139-
headers: {
140-
Cookie: `${PREFERRED_LOCALE_COOKIE_NAME}=ja`,
141-
'Accept-Language': 'es', // note how this is going to be ignored
142-
},
143-
})
144-
expect(res.statusCode).toBe(302)
145-
expect(res.headers.location).toBe('/ja')
146-
expect(res.headers['cache-control']).toBe('private, no-store')
147-
})
148-
test('homepage redirects to preferred language by cookie if valid', async () => {
149-
const res = await get('/', {
150-
headers: {
151-
Cookie: `${PREFERRED_LOCALE_COOKIE_NAME}=xy`,
152-
'Accept-Language': 'ja', // note how this is going to be ignored
153-
},
154-
})
155-
expect(res.statusCode).toBe(302)
156-
expect(res.headers.location).toBe('/ja')
157-
expect(res.headers['cache-control']).toBe('private, no-store')
158-
})
159135
})
160136

161137
describe('external redirects', () => {
@@ -173,63 +149,13 @@ describe('redirects', () => {
173149
})
174150

175151
describe('localized redirects', () => {
176-
const redirectFrom =
177-
'/desktop/contributing-to-projects/changing-a-remote-s-url-from-github-desktop'
178-
const redirectTo =
179-
'/desktop/contributing-and-collaborating-using-github-desktop/working-with-your-remote-repository-on-github-or-github-enterprise/changing-a-remotes-url-from-github-desktop'
180-
181152
test('redirect_from for renamed pages', async () => {
182-
const { res } = await get(`/ja${redirectFrom}`)
153+
const { res } = await get(
154+
'/ja/desktop/contributing-to-projects/changing-a-remote-s-url-from-github-desktop'
155+
)
183156
expect(res.statusCode).toBe(301)
184-
const expected = `/ja${redirectTo}`
185-
expect(res.headers.location).toBe(expected)
186-
})
187-
188-
test('redirect_from for renamed pages by Accept-Language header', async () => {
189-
const { res } = await get(redirectFrom, {
190-
headers: {
191-
'Accept-Language': 'ja',
192-
},
193-
})
194-
expect(res.statusCode).toBe(302)
195-
const expected = `/ja${redirectTo}`
196-
expect(res.headers.location).toBe(expected)
197-
})
198-
199-
test('redirect_from for renamed pages but ignore Accept-Language header if not recognized', async () => {
200-
const { res } = await get(redirectFrom, {
201-
headers: {
202-
// None of these are recognized
203-
'Accept-Language': 'sv,fr,gr',
204-
},
205-
})
206-
expect(res.statusCode).toBe(302)
207-
const expected = `/en${redirectTo}`
208-
expect(res.headers.location).toBe(expected)
209-
})
210-
211-
test('redirect_from for renamed pages but ignore unrecognized Accept-Language header values', async () => {
212-
const { res } = await get(redirectFrom, {
213-
headers: {
214-
// Only the last one is recognized
215-
'Accept-Language': 'sv,ja',
216-
},
217-
})
218-
expect(res.statusCode).toBe(302)
219-
const expected = `/ja${redirectTo}`
220-
expect(res.headers.location).toBe(expected)
221-
})
222-
223-
test('will inject the preferred language from cookie', async () => {
224-
const { res } = await get(redirectFrom, {
225-
headers: {
226-
Cookie: `${PREFERRED_LOCALE_COOKIE_NAME}=ja`,
227-
'Accept-Language': 'es', // note how this is going to be ignored
228-
},
229-
})
230-
// 302 because the redirect depended on cookie
231-
expect(res.statusCode).toBe(302)
232-
const expected = `/ja${redirectTo}`
157+
const expected =
158+
'/ja/desktop/contributing-and-collaborating-using-github-desktop/working-with-your-remote-repository-on-github-or-github-enterprise/changing-a-remotes-url-from-github-desktop'
233159
expect(res.headers.location).toBe(expected)
234160
})
235161
})

tests/unit/get-redirect.js

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -148,18 +148,4 @@ describe('getRedirect basics', () => {
148148
// it already has the enterprise-server prefix.
149149
expect(getRedirect('/enterprise-server/foo', ctx)).toBe(`/en/enterprise-server@${latest}/bar`)
150150
})
151-
152-
it('should redirect according to the req.context.userLanguage', () => {
153-
const ctx = {
154-
pages: {},
155-
redirects: {
156-
'/foo': '/bar',
157-
},
158-
userLanguage: 'ja',
159-
}
160-
expect(getRedirect('/foo', ctx)).toBe(`/ja/bar`)
161-
// falls back to 'en' if it's falsy
162-
ctx.userLanguage = null
163-
expect(getRedirect('/foo', ctx)).toBe(`/en/bar`)
164-
})
165151
})

0 commit comments

Comments
 (0)