Skip to content

Commit 9e1f5cd

Browse files
Add "Languages" navigation and article (#6382)
* Add new article "Translations" * Add "languages" button in TopNav * Add link to English (main) site * Split deployedTranslations into multiple lines * Fix build error regarding types * Address some review comments - deployedTranslations => finishedTranslations - showTranslated => progress - English fixes * Update src/content/community/translations.md Co-authored-by: Ricky <[email protected]> * Update src/content/community/translations.md --------- Co-authored-by: Ricky <[email protected]>
1 parent 0dbd67a commit 9e1f5cd

File tree

10 files changed

+166
-18
lines changed

10 files changed

+166
-18
lines changed

src/components/Layout/Page.tsx

+15-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {IconNavArrow} from 'components/Icon/IconNavArrow';
1616
import PageHeading from 'components/PageHeading';
1717
import {getRouteMeta} from './getRouteMeta';
1818
import {TocContext} from '../MDX/TocContext';
19+
import {Languages, LanguagesContext} from '../MDX/LanguagesContext';
1920
import type {TocItem} from 'components/MDX/TocContext';
2021
import type {RouteItem} from 'components/Layout/getRouteMeta';
2122
import {HomeContent} from './HomeContent';
@@ -36,9 +37,17 @@ interface PageProps {
3637
description?: string;
3738
};
3839
section: 'learn' | 'reference' | 'community' | 'blog' | 'home' | 'unknown';
40+
languages?: Languages | null;
3941
}
4042

41-
export function Page({children, toc, routeTree, meta, section}: PageProps) {
43+
export function Page({
44+
children,
45+
toc,
46+
routeTree,
47+
meta,
48+
section,
49+
languages = null,
50+
}: PageProps) {
4251
const {asPath} = useRouter();
4352
const cleanedPath = asPath.split(/[\?\#]/)[0];
4453
const {route, nextRoute, prevRoute, breadcrumbs, order} = getRouteMeta(
@@ -75,7 +84,11 @@ export function Page({children, toc, routeTree, meta, section}: PageProps) {
7584
'max-w-7xl mx-auto',
7685
section === 'blog' && 'lg:flex lg:flex-col lg:items-center'
7786
)}>
78-
<TocContext.Provider value={toc}>{children}</TocContext.Provider>
87+
<TocContext.Provider value={toc}>
88+
<LanguagesContext.Provider value={languages}>
89+
{children}
90+
</LanguagesContext.Provider>
91+
</TocContext.Provider>
7992
</div>
8093
{!isBlogIndex && (
8194
<DocsPageFooter

src/components/Layout/TopNav/TopNav.tsx

+21
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,19 @@ const lightIcon = (
7979
</svg>
8080
);
8181

82+
const languageIcon = (
83+
<svg
84+
xmlns="http://www.w3.org/2000/svg"
85+
width="24"
86+
height="24"
87+
viewBox="0 0 24 24">
88+
<path
89+
fill="currentColor"
90+
d=" M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z "
91+
/>
92+
</svg>
93+
);
94+
8295
const githubIcon = (
8396
<svg
8497
xmlns="http://www.w3.org/2000/svg"
@@ -352,6 +365,14 @@ export default function TopNav({
352365
{lightIcon}
353366
</button>
354367
</div>
368+
<div className="flex">
369+
<Link
370+
href="/community/translations"
371+
aria-label="Translations"
372+
className="active:scale-95 transition-transform flex w-12 h-12 rounded-full items-center justify-center hover:bg-primary/5 hover:dark:bg-primary-dark/5 outline-link">
373+
{languageIcon}
374+
</Link>
375+
</div>
355376
<div className="flex">
356377
<Link
357378
href="https://github.com/facebook/react/releases"
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/*
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*/
4+
5+
import {createContext} from 'react';
6+
7+
export type LanguageItem = {
8+
code: string;
9+
name: string;
10+
enName: string;
11+
};
12+
export type Languages = Array<LanguageItem>;
13+
14+
export const LanguagesContext = createContext<Languages | null>(null);

src/components/MDX/MDXComponents.tsx

+35
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ import ButtonLink from 'components/ButtonLink';
3131
import {TocContext} from './TocContext';
3232
import type {Toc, TocItem} from './TocContext';
3333
import {TeamMember} from './TeamMember';
34+
import {LanguagesContext} from './LanguagesContext';
35+
import {finishedTranslations} from 'utils/finishedTranslations';
3436

3537
import ErrorDecoder from './ErrorDecoder';
3638
import {IconCanary} from '../Icon/IconCanary';
@@ -380,6 +382,38 @@ function InlineTocItem({items}: {items: Array<NestedTocNode>}) {
380382
);
381383
}
382384

385+
type TranslationProgress = 'complete' | 'in-progress';
386+
387+
function LanguageList({progress}: {progress: TranslationProgress}) {
388+
const allLanguages = React.useContext(LanguagesContext) ?? [];
389+
const languages = allLanguages
390+
.filter(
391+
({code}) =>
392+
code !== 'en' &&
393+
(progress === 'complete'
394+
? finishedTranslations.includes(code)
395+
: !finishedTranslations.includes(code))
396+
)
397+
.sort((a, b) => a.enName.localeCompare(b.enName));
398+
return (
399+
<UL>
400+
{languages.map(({code, name, enName}) => {
401+
return (
402+
<LI key={code}>
403+
<Link href={`https://${code}.react.dev/`}>
404+
{enName} ({name})
405+
</Link>{' '}
406+
&mdash;{' '}
407+
<Link href={`https://github.com/reactjs/${code}.react.dev`}>
408+
Contribute
409+
</Link>
410+
</LI>
411+
);
412+
})}
413+
</UL>
414+
);
415+
}
416+
383417
function YouTubeIframe(props: any) {
384418
return (
385419
<div className="relative h-0 overflow-hidden pt-[56.25%]">
@@ -442,6 +476,7 @@ export const MDXComponents = {
442476
IllustrationBlock,
443477
Intro,
444478
InlineToc,
479+
LanguageList,
445480
LearnMore,
446481
Math,
447482
MathI,

src/components/Seo.tsx

+4-12
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as React from 'react';
66
import Head from 'next/head';
77
import {withRouter, Router} from 'next/router';
88
import {siteConfig} from '../siteConfig';
9+
import {finishedTranslations} from 'utils/finishedTranslations';
910

1011
export interface SeoProps {
1112
title: string;
@@ -18,17 +19,8 @@ export interface SeoProps {
1819
searchOrder?: number;
1920
}
2021

21-
const deployedTranslations = [
22-
'en',
23-
'zh-hans',
24-
'es',
25-
'fr',
26-
'ja',
27-
'tr',
28-
// We'll add more languages when they have enough content.
29-
// Please DO NOT edit this list without a discussion in the reactjs/react.dev repo.
30-
// It must be the same between all translations.
31-
];
22+
// If you are a maintainer of a language fork,
23+
// deployedTranslations has been moved to src/utils/finishedTranslations.ts.
3224

3325
function getDomain(languageCode: string): string {
3426
const subdomain = languageCode === 'en' ? '' : languageCode + '.';
@@ -71,7 +63,7 @@ export const Seo = withRouter(
7163
href={canonicalUrl.replace(siteDomain, getDomain('en'))}
7264
hrefLang="x-default"
7365
/>
74-
{deployedTranslations.map((languageCode) => (
66+
{finishedTranslations.map((languageCode) => (
7567
<link
7668
key={'alt-' + languageCode}
7769
rel="alternate"

src/content/community/translations.md

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
title: Translations
3+
---
4+
5+
<Intro>
6+
7+
React docs are translated by the global community into many languages all over the world.
8+
9+
</Intro>
10+
11+
## Source site {/*main-site*/}
12+
13+
All translations are provided from the canonical source docs:
14+
15+
- [English](https://react.dev/) &mdash; [Contribute](https://github.com/reactjs/react.dev/)
16+
17+
## Full translations {/*full-translations*/}
18+
19+
{/* If you are a language maintainer and want to add your language here, finish the "Core" translations and edit `deployedTranslations` under `src/utils`. */}
20+
21+
<LanguageList progress="complete" />
22+
23+
## In-progress translations {/*in-progress-translations*/}
24+
25+
For the progress of each translation, see: [Is React Translated Yet?](https://translations.react.dev/)
26+
27+
<LanguageList progress="in-progress" />
28+
29+
## How to contribute {/*how-to-contribute*/}
30+
31+
You can contribute to the translation efforts!
32+
33+
The community conducts the translation work for the React docs on each language-specific fork of react.dev. Typical translation work involves directly translating a Markdown file and creating a pull request. Click the "contribute" link above to the GitHub repository for your language, and follow the instructions there to help with the translation effort.
34+
35+
If you want to start a new translation for your language, visit: [translations.react.dev](https://github.com/reactjs/translations.react.dev)

src/pages/[[...markdownPath]].js

+10-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import sidebarBlog from '../sidebarBlog.json';
1313
import {MDXComponents} from 'components/MDX/MDXComponents';
1414
import compileMDX from 'utils/compileMDX';
1515
import {generateRssFeed} from '../utils/rss';
16-
export default function Layout({content, toc, meta}) {
16+
17+
export default function Layout({content, toc, meta, languages}) {
1718
const parsedContent = useMemo(
1819
() => JSON.parse(content, reviveNodeOnClient),
1920
[content]
@@ -40,7 +41,12 @@ export default function Layout({content, toc, meta}) {
4041
break;
4142
}
4243
return (
43-
<Page toc={parsedToc} routeTree={routeTree} meta={meta} section={section}>
44+
<Page
45+
toc={parsedToc}
46+
routeTree={routeTree}
47+
meta={meta}
48+
section={section}
49+
languages={languages}>
4450
{parsedContent}
4551
</Page>
4652
);
@@ -110,12 +116,13 @@ export async function getStaticProps(context) {
110116
mdx = fs.readFileSync(rootDir + path + '/index.md', 'utf8');
111117
}
112118

113-
const {toc, content, meta} = await compileMDX(mdx, path, {});
119+
const {toc, content, meta, languages} = await compileMDX(mdx, path, {});
114120
return {
115121
props: {
116122
toc,
117123
content,
118124
meta,
125+
languages,
119126
},
120127
};
121128
}

src/sidebarCommunity.json

+4
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@
3131
"title": "Docs Contributors",
3232
"path": "/community/docs-contributors"
3333
},
34+
{
35+
"title": "Translations",
36+
"path": "/community/translations"
37+
},
3438
{
3539
"title": "Acknowledgements",
3640
"path": "/community/acknowledgements"

src/utils/compileMDX.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import {LanguageItem} from 'components/MDX/LanguagesContext';
12
import {MDXComponents} from 'components/MDX/MDXComponents';
23

34
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
45
// ~~~~ IMPORTANT: BUMP THIS IF YOU CHANGE ANY CODE BELOW ~~~
5-
const DISK_CACHE_BREAKER = 8;
6+
const DISK_CACHE_BREAKER = 9;
67
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
78

89
export default async function compileMDX(
@@ -124,10 +125,21 @@ export default async function compileMDX(
124125
const fm = require('gray-matter');
125126
const meta = fm(mdx).data;
126127

128+
// Load the list of translated languages conditionally.
129+
let languages: Array<LanguageItem> | null = null;
130+
if (typeof path === 'string' && path.endsWith('/translations')) {
131+
languages = await (
132+
await fetch(
133+
'https://raw.githubusercontent.com/reactjs/translations.react.dev/main/langs/langs.json'
134+
)
135+
).json(); // { code: string; name: string; enName: string}[]
136+
}
137+
127138
const output = {
128139
content: JSON.stringify(children, stringifyNodeOnServer),
129140
toc: JSON.stringify(toc, stringifyNodeOnServer),
130141
meta,
142+
languages,
131143
};
132144

133145
// Serialize a server React tree node to JSON.

src/utils/finishedTranslations.ts

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// This is a list of languages with enough translated content.
2+
// Add more languages here when they have enough content.
3+
// Please DO NOT edit this list without a discussion in the reactjs/react.dev repo.
4+
// It must be the same between all translations.
5+
// This will also affect the 'Translations' article.
6+
7+
// prettier-ignore
8+
export const finishedTranslations = [
9+
'en',
10+
'zh-hans',
11+
'es',
12+
'fr',
13+
'ja',
14+
'tr'
15+
];

0 commit comments

Comments
 (0)