diff --git a/components/hooks/useUserLanguage.ts b/components/hooks/useUserLanguage.ts index 430142311527..4e2f570508ab 100644 --- a/components/hooks/useUserLanguage.ts +++ b/components/hooks/useUserLanguage.ts @@ -7,7 +7,7 @@ import { USER_LANGUAGE_COOKIE_NAME } from '../../lib/constants.js' export function useUserLanguage() { const { locale } = useRouter() - const [userLanguage, setUserLanguage] = useState('en') + const [userLanguage, setUserLanguage] = useState('en') const { languages } = useLanguages() useEffect(() => { @@ -28,5 +28,10 @@ export function useUserLanguage() { } }, [locale]) - return { userLanguage } + function setUserLanguageCookie(language: string) { + Cookies.set(USER_LANGUAGE_COOKIE_NAME, language) + setUserLanguage(language) + } + + return { userLanguage, setUserLanguageCookie } } diff --git a/components/page-header/HeaderNotifications.tsx b/components/page-header/HeaderNotifications.tsx index 7eba342c1a29..562c4f8cd909 100644 --- a/components/page-header/HeaderNotifications.tsx +++ b/components/page-header/HeaderNotifications.tsx @@ -1,5 +1,6 @@ import { useRouter } from 'next/router' import cx from 'classnames' +import { XIcon } from '@primer/octicons-react' import { useLanguages } from 'components/context/LanguagesContext' import { useMainContext } from 'components/context/MainContext' @@ -18,12 +19,14 @@ enum NotificationType { type Notif = { content: string type?: NotificationType + onClose?: () => void } + export const HeaderNotifications = () => { const router = useRouter() const { currentVersion } = useVersion() const { relativePath, allVersions, data, currentPathWithoutLanguage, page } = useMainContext() - const { userLanguage } = useUserLanguage() + const { userLanguage, setUserLanguageCookie } = useUserLanguage() const { languages } = useLanguages() const { t } = useTranslation('header') @@ -38,6 +41,18 @@ export const HeaderNotifications = () => { translationNotices.push({ type: NotificationType.TRANSLATION, content: `This article is also available in ${languages[userLanguage]?.name}.`, + onClose: () => { + try { + setUserLanguageCookie('en') + } catch (err) { + // You can never be too careful because setting a cookie + // can fail. For example, some browser + // extensions disallow all setting of cookies and attempts + // at the `document.cookie` setter could throw. Just swallow + // and move on. + console.warn('Unable to set cookie', err) + } + }, }) } } else { @@ -75,8 +90,9 @@ export const HeaderNotifications = () => { return (
- {allNotifications.map(({ type, content }, i) => { + {allNotifications.map(({ type, content, onClose }, i) => { const isLast = i === allNotifications.length - 1 + return (
{ type === NotificationType.EARLY_ACCESS && 'color-bg-danger', !isLast && 'border-bottom color-border-default', )} - dangerouslySetInnerHTML={{ __html: content }} - /> + > + {onClose && ( + + )} +

+

) })}
diff --git a/components/page-header/LanguagePicker.tsx b/components/page-header/LanguagePicker.tsx index 612a4e49c794..0cbfb45f2bed 100644 --- a/components/page-header/LanguagePicker.tsx +++ b/components/page-header/LanguagePicker.tsx @@ -1,34 +1,11 @@ import { useRouter } from 'next/router' -import Cookies from 'components/lib/cookies' import { GlobeIcon } from '@primer/octicons-react' import { useLanguages } from 'components/context/LanguagesContext' import { useTranslation } from 'components/hooks/useTranslation' +import { useUserLanguage } from 'components/hooks/useUserLanguage' import { Picker } from 'components/ui/Picker' -import { USER_LANGUAGE_COOKIE_NAME } from '../../lib/constants.js' - -function rememberPreferredLanguage(value: string) { - try { - // The reason we use a cookie and not local storage is because - // this cookie value is used and needed by the server. For - // example, when doing `GET /some/page` we need the cookie - // to redirect to `Location: /ja/some/page`. - // It's important it's *not* an HttpOnly cookie because we - // need this in the client-side which is used to determine - // the UI about displaying notifications about preferred - // language if your cookie doesn't match the current URL. - Cookies.set(USER_LANGUAGE_COOKIE_NAME, value) - } catch (err) { - // You can never be too careful because setting a cookie - // can fail. For example, some browser - // extensions disallow all setting of cookies and attempts - // at the `document.cookie` setter could throw. Just swallow - // and move on. - console.warn('Unable to set preferred language cookie', err) - } -} - type Props = { mediumOrLower?: boolean } @@ -36,6 +13,7 @@ type Props = { export const LanguagePicker = ({ mediumOrLower }: Props) => { const router = useRouter() const { languages } = useLanguages() + const { setUserLanguageCookie } = useUserLanguage() const locale = router.locale || 'en' @@ -74,7 +52,18 @@ export const LanguagePicker = ({ mediumOrLower }: Props) => { pickerLabel={mediumOrLower ? 'Language' : ''} iconButton={mediumOrLower ? undefined : GlobeIcon} onSelect={(item) => { - if (item.extra?.locale) rememberPreferredLanguage(item.extra.locale) + if (item.extra?.locale) { + try { + setUserLanguageCookie(item.extra.locale) + } catch (err) { + // You can never be too careful because setting a cookie + // can fail. For example, some browser + // extensions disallow all setting of cookies and attempts + // at the `document.cookie` setter could throw. Just swallow + // and move on. + console.warn('Unable to set preferred language cookie', err) + } + } }} buttonBorder={mediumOrLower} dataTestId="default-language"