Skip to content

Commit

Permalink
Add <meta> tags for translations (#5930)
Browse files Browse the repository at this point in the history
* Add site config with domain name

* Add <meta> tags for languages

* Move legacy redirect out of vercel config

This lets us give it a more specific subdomain per lang.

* Support sites with no legacy

* Fix types

* Undo unrelated change
  • Loading branch information
gaearon authored Apr 18, 2023
1 parent 6c096e0 commit a35b64c
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 26 deletions.
18 changes: 18 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
* Copyright (c) Facebook, Inc. and its affiliates.
*/

const siteConfig = require('./src/siteConfig').siteConfig;

/**
* @type {import('next').NextConfig}
**/
Expand All @@ -14,6 +16,22 @@ const nextConfig = {
legacyBrowsers: false,
browsersListForSwc: true,
},
redirects() {
const redirects = [];
const languageCode = siteConfig.languageCode;
const subdomain =
languageCode === 'en' || !siteConfig.hasLegacySite
? ''
: languageCode + '.';
return [
{
// Assume all *.html links are legacy site URLs.
source: '/:path*(\\.html)',
destination: `https://${subdomain}legacy.reactjs.org/:path*.html`,
permanent: false,
},
];
},
env: {
SANDPACK_BARE_COMPONENTS: process.env.SANDPACK_BARE_COMPONENTS,
},
Expand Down
49 changes: 32 additions & 17 deletions src/components/Seo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import * as React from 'react';
import Head from 'next/head';
import {withRouter, Router} from 'next/router';
import {siteConfig} from '../siteConfig';

export interface SeoProps {
title: string;
Expand All @@ -16,6 +17,16 @@ export interface SeoProps {
searchOrder?: number;
}

const deployedTranslations = [
'en',
// We'll add more languages when they have enough content.
];

function getDomain(languageCode: string): string {
const subdomain = languageCode === 'en' ? '' : languageCode + '.';
return subdomain + 'react.dev';
}

export const Seo = withRouter(
({
title,
Expand All @@ -26,29 +37,37 @@ export const Seo = withRouter(
isHomePage,
searchOrder,
}: SeoProps & {router: Router}) => {
const siteDomain = getDomain(siteConfig.languageCode);
const canonicalUrl = `https://${siteDomain}${
router.asPath.split(/[\?\#]/)[0]
}`;
const pageTitle = isHomePage ? 'React' : title + ' – React';
// Twitter's meta parser is not very good.
const twitterTitle = pageTitle.replace(/[<>]/g, '');
return (
<Head>
{/* DEFAULT */}

<meta name="viewport" content="width=device-width, initial-scale=1" />

{title != null && <title key="title">{pageTitle}</title>}
{description != null && (
<meta name="description" key="description" content={description} />
)}
{/* <link rel="icon" type="image/x-icon" href={favicon} />
<link rel="apple-touch-icon" href={favicon} /> @todo favicon */}
<link rel="canonical" href={canonicalUrl} />
<link
rel="alternate"
href={canonicalUrl.replace(siteDomain, getDomain('en'))}
hrefLang="x-default"
/>
{deployedTranslations.map((languageCode) => (
<link
key={'alt-' + languageCode}
rel="alternate"
hrefLang={languageCode}
href={canonicalUrl.replace(siteDomain, getDomain(languageCode))}
/>
))}
<meta property="fb:app_id" content="623268441017527" />
{/* OPEN GRAPH */}
<meta property="og:type" key="og:type" content="website" />
<meta
property="og:url"
key="og:url"
content={`https://react.dev${router.asPath.split(/[\?\#]/)[0]}`}
/>
<meta property="og:url" key="og:url" content={canonicalUrl} />
{title != null && (
<meta property="og:title" content={pageTitle} key="og:title" />
)}
Expand All @@ -59,14 +78,11 @@ export const Seo = withRouter(
content={description}
/>
)}

<meta
property="og:image"
key="og:image"
content={`https://react.dev${image}`}
content={`https://${siteDomain}${image}`}
/>

{/* TWITTER */}
<meta
name="twitter:card"
key="twitter:card"
Expand All @@ -88,11 +104,10 @@ export const Seo = withRouter(
content={description}
/>
)}

<meta
name="twitter:image"
key="twitter:image"
content={`https://react.dev${image}`}
content={`https://${siteDomain}${image}`}
/>
<meta
name="google-site-verification"
Expand Down
4 changes: 2 additions & 2 deletions src/pages/_document.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
*/

import {Html, Head, Main, NextScript} from 'next/document';
import {siteConfig} from '../siteConfig';

const MyDocument = () => {
// @todo specify language in HTML?
return (
<Html lang="en">
<Html lang={siteConfig.languageCode}>
<Head />
<body className="font-text font-medium antialiased text-lg bg-wash dark:bg-wash-dark text-secondary dark:text-secondary-dark leading-base">
<script
Expand Down
8 changes: 6 additions & 2 deletions src/siteConfig.ts → src/siteConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@
* Copyright (c) Facebook, Inc. and its affiliates.
*/

export const siteConfig = {
editUrl: 'https://github.com/reactjs/react.dev/tree/main/src/pages',
exports.siteConfig = {
// --------------------------------------
// Translations should replace these lines:
languageCode: 'en',
hasLegacySite: true,
// --------------------------------------
copyright: `Copyright © ${new Date().getFullYear()} Facebook Inc. All Rights Reserved.`,
repoUrl: 'https://github.com/facebook/react',
twitterUrl: 'https://twitter.com/reactjs',
Expand Down
5 changes: 0 additions & 5 deletions vercel.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,6 @@
"destination": "/blog/2020/12/21/data-fetching-with-react-server-components",
"permanent": false
},
{
"source": "/:path*(\\.html)",
"destination": "https://legacy.reactjs.org/:path*.html",
"permanent": false
},
{
"source": "/tips/controlled-input-null-value.html",
"destination": "/reference/react-dom/components/input#im-getting-an-error-a-component-is-changing-an-uncontrolled-input-to-be-controlled",
Expand Down

0 comments on commit a35b64c

Please sign in to comment.