Start translations

This commit is contained in:
Samy Pessé
2023-10-29 16:29:59 -04:00
parent af09b108d1
commit b5b7090bb9
11 changed files with 129 additions and 25 deletions
+2 -2
View File
@@ -5,7 +5,7 @@ import React from 'react';
import { CONTAINER_MAX_WIDTH_NORMAL, CONTAINER_PADDING } from '../layout';
export function Footer(props: { space: Space; asFullWidth: boolean }) {
const { asFullWidth } = props;
const { space, asFullWidth } = props;
return (
<div
@@ -31,7 +31,7 @@ export function Footer(props: { space: Space; asFullWidth: boolean }) {
<div className={tcls('flex-1')} />
<div>
<React.Suspense fallback={null}>
<ThemeToggler />
<ThemeToggler space={space} />
</React.Suspense>
</div>
</div>
+2 -1
View File
@@ -6,6 +6,7 @@ import Link from 'next/link';
import { Suspense } from 'react';
import { SearchButton } from '../Search';
import { CONTAINER_MAX_WIDTH_NORMAL, CONTAINER_PADDING } from '@/components/layout';
import { t } from '@/lib/intl';
/**
* Render the header for the space.
@@ -50,7 +51,7 @@ export function Header(props: { space: Space; asFullWidth: boolean }) {
</Link>
<div>
<Suspense fallback={null}>
<SearchButton />
<SearchButton>{t({ space }, 'search')}</SearchButton>
</Suspense>
</div>
</div>
+5 -4
View File
@@ -1,14 +1,15 @@
import { getDocumentSections } from '@/lib/document';
import { tcls } from '@/lib/tailwind';
import { RevisionPageDocument } from '@gitbook/api';
import { RevisionPageDocument, Space } from '@gitbook/api';
import React from 'react';
import { ScrollSectionsList } from './ScrollSectionsList';
import { t } from '@/lib/intl';
/**
* Aside listing the headings in the document.
*/
export function PageAside(props: { page: RevisionPageDocument; document: any }) {
const { document } = props;
export function PageAside(props: { space: Space; page: RevisionPageDocument; document: any }) {
const { space, document } = props;
const sections = getDocumentSections(document);
return (
@@ -29,7 +30,7 @@ export function PageAside(props: { page: RevisionPageDocument; document: any })
{sections.length > 0 ? (
<>
<div className={tcls('text-sm', 'text-slate-500', 'font-semibold', 'pb-3')}>
On this page
{t({ space }, 'on_this_page')}
</div>
<div className={tcls('overflow-auto', 'flex-1')}>
<React.Suspense fallback={null}>
+3 -3
View File
@@ -7,8 +7,8 @@ import { IconSearch } from '@/components/icons/IconSearch';
/**
* Button to open the search modal.
*/
export function SearchButton(props: { style?: ClassValue }) {
const { style } = props;
export function SearchButton(props: { children: React.ReactNode; style?: ClassValue }) {
const { style, children } = props;
const router = useRouter();
const pathname = usePathname();
@@ -41,7 +41,7 @@ export function SearchButton(props: { style?: ClassValue }) {
)}
>
<IconSearch className={tcls('w-4', 'h-4', 'me-2')} />
Search
{children}
</button>
);
}
+7 -2
View File
@@ -35,9 +35,14 @@ export function SpaceContent(props: {
asFullWidth ? null : [CONTAINER_MAX_WIDTH_NORMAL, 'mx-auto'],
)}
>
<TableOfContents revision={revision} activePage={page} ancestors={ancestors} />
<TableOfContents
space={space}
revision={revision}
activePage={page}
ancestors={ancestors}
/>
<PageBody space={space} revision={revision} page={page} document={document} />
<PageAside page={page} document={document} />
<PageAside space={space} page={page} document={document} />
</div>
<Footer space={space} asFullWidth={asFullWidth} />
<React.Suspense fallback={null}>
@@ -2,13 +2,16 @@ import { Revision, RevisionPageDocument, RevisionPageGroup } from '@gitbook/api'
import { tcls } from '@/lib/tailwind';
import { PagesList } from './PagesList';
import { Trademark } from './Trademark';
import { IntlContext } from '@/lib/intl';
export function TableOfContents(props: {
revision: Revision;
activePage: RevisionPageDocument;
ancestors: Array<RevisionPageDocument | RevisionPageGroup>;
}) {
const { revision, activePage, ancestors } = props;
export function TableOfContents(
props: IntlContext & {
revision: Revision;
activePage: RevisionPageDocument;
ancestors: Array<RevisionPageDocument | RevisionPageGroup>;
},
) {
const { space, revision, activePage, ancestors } = props;
return (
<aside
@@ -29,7 +32,7 @@ export function TableOfContents(props: {
<div className={tcls('flex-1', 'overflow-y-auto', 'pt-6', 'pb-14', 'pr-4')}>
<PagesList pages={revision.pages} activePage={activePage} ancestors={ancestors} />
</div>
<Trademark />
<Trademark space={space} />
</aside>
);
}
+3 -2
View File
@@ -1,9 +1,10 @@
import { IntlContext, t } from '@/lib/intl';
import { tcls } from '@/lib/tailwind';
/**
* Link to the GitBook platform.
*/
export function Trademark() {
export function Trademark(props: IntlContext) {
return (
<div className={tcls('absolute', 'bottom-0', 'right-0', 'left-0', 'flex', 'flex-col')}>
<a
@@ -22,7 +23,7 @@ export function Trademark() {
'hover:bg-slate-100',
)}
>
Powered by GitBook
{t(props, 'powered_by_gitbook')}
</a>
</div>
);
+5 -4
View File
@@ -5,6 +5,7 @@ import React from 'react';
import Moon from '@geist-ui/icons/moon';
import Sun from '@geist-ui/icons/sun';
import Monitor from '@geist-ui/icons/monitor';
import { IntlContext, tString } from '@/lib/intl';
type ThemeMode = 'light' | 'system' | 'dark';
@@ -13,7 +14,7 @@ const LOCALSTORAGE_KEY = 'color-theme';
/**
* Buttons to toggle between light/system/dark modes.
*/
export function ThemeToggler(props: {}) {
export function ThemeToggler(props: IntlContext) {
const [mode, setMode] = React.useState<ThemeMode>('system');
const onSwitchMode = (to: ThemeMode) => {
@@ -59,19 +60,19 @@ export function ThemeToggler(props: {}) {
active={mode === 'light'}
icon={Sun}
onClick={() => onSwitchMode('light')}
title="Switch to light theme"
title={tString(props, 'switch_to_light_theme')}
/>
<ThemeButton
active={mode === 'system'}
icon={Monitor}
onClick={() => onSwitchMode('system')}
title="Switch to system theme"
title={tString(props, 'switch_to_system_theme')}
/>
<ThemeButton
active={mode === 'dark'}
icon={Moon}
onClick={() => onSwitchMode('dark')}
title="Switch to dark theme"
title={tString(props, 'switch_to_dark_theme')}
/>
</div>
);
+81
View File
@@ -0,0 +1,81 @@
import * as translations from '@/translations';
import { Space } from '@gitbook/api';
import React from 'react';
export interface IntlContext {
space: Space;
}
/**
* Translate a string.
*/
export function t(
context: IntlContext,
id: keyof typeof translations.en,
...args: React.ReactNode[]
): React.ReactNode {
const locale = 'en'; // TODO
// fallback to english if no translation
const string = translations[locale]?.[id] || translations.en[id];
// Now we are going to replace the arguments
// but we want to return a string as long as it's possible
// (eg. if there isn't any argument that is a ReactNode)
const parts: React.ReactNode[] = [];
let currentStringToReplace: string = string;
args.forEach((arg, i) => {
if (typeof arg === 'string') {
currentStringToReplace = currentStringToReplace.replace(`\${${i + 1}}`, arg);
} else {
const [partToPush, partToReplace] = currentStringToReplace.split(`\${${i + 1}}`);
parts.push(<React.Fragment key={`string-${i}`}>{partToPush}</React.Fragment>);
parts.push(<React.Fragment key={`arg-${i}`}>{arg}</React.Fragment>);
currentStringToReplace = partToReplace;
}
});
if (!parts.length) {
return currentStringToReplace;
}
return (
<>
{parts}
{currentStringToReplace}
</>
);
}
/**
* Version of `t` that returns a string.
*/
export function tString(
context: IntlContext,
id: keyof typeof translations.en,
...args: React.ReactNode[]
): string {
const result = t(context, id, ...args);
return reactToString(result);
}
function reactToString(el: React.ReactNode): string {
if (typeof el === 'string' || typeof el === 'number' || typeof el === 'boolean') {
return `${el}`;
}
if (el === null || el === undefined) {
return '';
}
if (Array.isArray(el)) {
return el.map(reactToString).join('');
}
if ('props' in el) {
return el.props.children.map(reactToString).join('');
}
throw new Error(`Unsupported type ${typeof el}`);
}
+8
View File
@@ -0,0 +1,8 @@
{
"powered_by_gitbook": "Powered by GitBook",
"switch_to_dark_theme": "Switch to dark theme",
"switch_to_light_theme": "Switch to light theme",
"switch_to_system_theme": "Switch to system theme",
"search": "Search",
"on_this_page": "On this page"
}
+3
View File
@@ -0,0 +1,3 @@
import en from './en.json';
export { en };