Skip to content

Commit

Permalink
Add "Languages" navigation and article (#6382)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
smikitky and rickhanlonii authored May 1, 2024
1 parent 0dbd67a commit 9e1f5cd
Show file tree
Hide file tree
Showing 10 changed files with 166 additions and 18 deletions.
17 changes: 15 additions & 2 deletions src/components/Layout/Page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {IconNavArrow} from 'components/Icon/IconNavArrow';
import PageHeading from 'components/PageHeading';
import {getRouteMeta} from './getRouteMeta';
import {TocContext} from '../MDX/TocContext';
import {Languages, LanguagesContext} from '../MDX/LanguagesContext';
import type {TocItem} from 'components/MDX/TocContext';
import type {RouteItem} from 'components/Layout/getRouteMeta';
import {HomeContent} from './HomeContent';
Expand All @@ -36,9 +37,17 @@ interface PageProps {
description?: string;
};
section: 'learn' | 'reference' | 'community' | 'blog' | 'home' | 'unknown';
languages?: Languages | null;
}

export function Page({children, toc, routeTree, meta, section}: PageProps) {
export function Page({
children,
toc,
routeTree,
meta,
section,
languages = null,
}: PageProps) {
const {asPath} = useRouter();
const cleanedPath = asPath.split(/[\?\#]/)[0];
const {route, nextRoute, prevRoute, breadcrumbs, order} = getRouteMeta(
Expand Down Expand Up @@ -75,7 +84,11 @@ export function Page({children, toc, routeTree, meta, section}: PageProps) {
'max-w-7xl mx-auto',
section === 'blog' && 'lg:flex lg:flex-col lg:items-center'
)}>
<TocContext.Provider value={toc}>{children}</TocContext.Provider>
<TocContext.Provider value={toc}>
<LanguagesContext.Provider value={languages}>
{children}
</LanguagesContext.Provider>
</TocContext.Provider>
</div>
{!isBlogIndex && (
<DocsPageFooter
Expand Down
21 changes: 21 additions & 0 deletions src/components/Layout/TopNav/TopNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,19 @@ const lightIcon = (
</svg>
);

const languageIcon = (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24">
<path
fill="currentColor"
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 "
/>
</svg>
);

const githubIcon = (
<svg
xmlns="http://www.w3.org/2000/svg"
Expand Down Expand Up @@ -352,6 +365,14 @@ export default function TopNav({
{lightIcon}
</button>
</div>
<div className="flex">
<Link
href="/community/translations"
aria-label="Translations"
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">
{languageIcon}
</Link>
</div>
<div className="flex">
<Link
href="https://github.com/facebook/react/releases"
Expand Down
14 changes: 14 additions & 0 deletions src/components/MDX/LanguagesContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/

import {createContext} from 'react';

export type LanguageItem = {
code: string;
name: string;
enName: string;
};
export type Languages = Array<LanguageItem>;

export const LanguagesContext = createContext<Languages | null>(null);
35 changes: 35 additions & 0 deletions src/components/MDX/MDXComponents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import ButtonLink from 'components/ButtonLink';
import {TocContext} from './TocContext';
import type {Toc, TocItem} from './TocContext';
import {TeamMember} from './TeamMember';
import {LanguagesContext} from './LanguagesContext';
import {finishedTranslations} from 'utils/finishedTranslations';

import ErrorDecoder from './ErrorDecoder';
import {IconCanary} from '../Icon/IconCanary';
Expand Down Expand Up @@ -380,6 +382,38 @@ function InlineTocItem({items}: {items: Array<NestedTocNode>}) {
);
}

type TranslationProgress = 'complete' | 'in-progress';

function LanguageList({progress}: {progress: TranslationProgress}) {
const allLanguages = React.useContext(LanguagesContext) ?? [];
const languages = allLanguages
.filter(
({code}) =>
code !== 'en' &&
(progress === 'complete'
? finishedTranslations.includes(code)
: !finishedTranslations.includes(code))
)
.sort((a, b) => a.enName.localeCompare(b.enName));
return (
<UL>
{languages.map(({code, name, enName}) => {
return (
<LI key={code}>
<Link href={`https://${code}.react.dev/`}>
{enName} ({name})
</Link>{' '}
&mdash;{' '}
<Link href={`https://github.com/reactjs/${code}.react.dev`}>
Contribute
</Link>
</LI>
);
})}
</UL>
);
}

function YouTubeIframe(props: any) {
return (
<div className="relative h-0 overflow-hidden pt-[56.25%]">
Expand Down Expand Up @@ -442,6 +476,7 @@ export const MDXComponents = {
IllustrationBlock,
Intro,
InlineToc,
LanguageList,
LearnMore,
Math,
MathI,
Expand Down
16 changes: 4 additions & 12 deletions src/components/Seo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import * as React from 'react';
import Head from 'next/head';
import {withRouter, Router} from 'next/router';
import {siteConfig} from '../siteConfig';
import {finishedTranslations} from 'utils/finishedTranslations';

export interface SeoProps {
title: string;
Expand All @@ -18,17 +19,8 @@ export interface SeoProps {
searchOrder?: number;
}

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

function getDomain(languageCode: string): string {
const subdomain = languageCode === 'en' ? '' : languageCode + '.';
Expand Down Expand Up @@ -71,7 +63,7 @@ export const Seo = withRouter(
href={canonicalUrl.replace(siteDomain, getDomain('en'))}
hrefLang="x-default"
/>
{deployedTranslations.map((languageCode) => (
{finishedTranslations.map((languageCode) => (
<link
key={'alt-' + languageCode}
rel="alternate"
Expand Down
35 changes: 35 additions & 0 deletions src/content/community/translations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
title: Translations
---

<Intro>

React docs are translated by the global community into many languages all over the world.

</Intro>

## Source site {/*main-site*/}

All translations are provided from the canonical source docs:

- [English](https://react.dev/) &mdash; [Contribute](https://github.com/reactjs/react.dev/)

## Full translations {/*full-translations*/}

{/* If you are a language maintainer and want to add your language here, finish the "Core" translations and edit `deployedTranslations` under `src/utils`. */}

<LanguageList progress="complete" />

## In-progress translations {/*in-progress-translations*/}

For the progress of each translation, see: [Is React Translated Yet?](https://translations.react.dev/)

<LanguageList progress="in-progress" />

## How to contribute {/*how-to-contribute*/}

You can contribute to the translation efforts!

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.

If you want to start a new translation for your language, visit: [translations.react.dev](https://github.com/reactjs/translations.react.dev)
13 changes: 10 additions & 3 deletions src/pages/[[...markdownPath]].js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import sidebarBlog from '../sidebarBlog.json';
import {MDXComponents} from 'components/MDX/MDXComponents';
import compileMDX from 'utils/compileMDX';
import {generateRssFeed} from '../utils/rss';
export default function Layout({content, toc, meta}) {

export default function Layout({content, toc, meta, languages}) {
const parsedContent = useMemo(
() => JSON.parse(content, reviveNodeOnClient),
[content]
Expand All @@ -40,7 +41,12 @@ export default function Layout({content, toc, meta}) {
break;
}
return (
<Page toc={parsedToc} routeTree={routeTree} meta={meta} section={section}>
<Page
toc={parsedToc}
routeTree={routeTree}
meta={meta}
section={section}
languages={languages}>
{parsedContent}
</Page>
);
Expand Down Expand Up @@ -110,12 +116,13 @@ export async function getStaticProps(context) {
mdx = fs.readFileSync(rootDir + path + '/index.md', 'utf8');
}

const {toc, content, meta} = await compileMDX(mdx, path, {});
const {toc, content, meta, languages} = await compileMDX(mdx, path, {});
return {
props: {
toc,
content,
meta,
languages,
},
};
}
Expand Down
4 changes: 4 additions & 0 deletions src/sidebarCommunity.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
"title": "Docs Contributors",
"path": "/community/docs-contributors"
},
{
"title": "Translations",
"path": "/community/translations"
},
{
"title": "Acknowledgements",
"path": "/community/acknowledgements"
Expand Down
14 changes: 13 additions & 1 deletion src/utils/compileMDX.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import {LanguageItem} from 'components/MDX/LanguagesContext';
import {MDXComponents} from 'components/MDX/MDXComponents';

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ~~~~ IMPORTANT: BUMP THIS IF YOU CHANGE ANY CODE BELOW ~~~
const DISK_CACHE_BREAKER = 8;
const DISK_CACHE_BREAKER = 9;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

export default async function compileMDX(
Expand Down Expand Up @@ -124,10 +125,21 @@ export default async function compileMDX(
const fm = require('gray-matter');
const meta = fm(mdx).data;

// Load the list of translated languages conditionally.
let languages: Array<LanguageItem> | null = null;
if (typeof path === 'string' && path.endsWith('/translations')) {
languages = await (
await fetch(
'https://raw.githubusercontent.com/reactjs/translations.react.dev/main/langs/langs.json'
)
).json(); // { code: string; name: string; enName: string}[]
}

const output = {
content: JSON.stringify(children, stringifyNodeOnServer),
toc: JSON.stringify(toc, stringifyNodeOnServer),
meta,
languages,
};

// Serialize a server React tree node to JSON.
Expand Down
15 changes: 15 additions & 0 deletions src/utils/finishedTranslations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// This is a list of languages with enough translated content.
// Add more languages here when they have enough content.
// Please DO NOT edit this list without a discussion in the reactjs/react.dev repo.
// It must be the same between all translations.
// This will also affect the 'Translations' article.

// prettier-ignore
export const finishedTranslations = [
'en',
'zh-hans',
'es',
'fr',
'ja',
'tr'
];

0 comments on commit 9e1f5cd

Please sign in to comment.