mirror of
https://github.com/reactjs/react.dev.git
synced 2026-02-22 20:01:57 +00:00
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 <rickhanlonii@gmail.com> * Update src/content/community/translations.md --------- Co-authored-by: Ricky <rickhanlonii@gmail.com>
This commit is contained in:
@@ -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';
|
||||
@@ -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(
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
|
||||
14
src/components/MDX/LanguagesContext.tsx
Normal file
14
src/components/MDX/LanguagesContext.tsx
Normal file
@@ -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);
|
||||
@@ -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';
|
||||
@@ -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>{' '}
|
||||
—{' '}
|
||||
<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%]">
|
||||
@@ -442,6 +476,7 @@ export const MDXComponents = {
|
||||
IllustrationBlock,
|
||||
Intro,
|
||||
InlineToc,
|
||||
LanguageList,
|
||||
LearnMore,
|
||||
Math,
|
||||
MathI,
|
||||
|
||||
@@ -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;
|
||||
@@ -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 + '.';
|
||||
@@ -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"
|
||||
|
||||
35
src/content/community/translations.md
Normal file
35
src/content/community/translations.md
Normal file
@@ -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/) — [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,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]
|
||||
@@ -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>
|
||||
);
|
||||
@@ -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,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -31,6 +31,10 @@
|
||||
"title": "Docs Contributors",
|
||||
"path": "/community/docs-contributors"
|
||||
},
|
||||
{
|
||||
"title": "Translations",
|
||||
"path": "/community/translations"
|
||||
},
|
||||
{
|
||||
"title": "Acknowledgements",
|
||||
"path": "/community/acknowledgements"
|
||||
|
||||
@@ -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(
|
||||
@@ -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.
|
||||
|
||||
15
src/utils/finishedTranslations.ts
Normal file
15
src/utils/finishedTranslations.ts
Normal file
@@ -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'
|
||||
];
|
||||
Reference in New Issue
Block a user