Skip to content

Commit efdf8fb

Browse files
committed
feat: finalize basic i18n support
1 parent f85e8eb commit efdf8fb

File tree

6 files changed

+64
-12
lines changed

6 files changed

+64
-12
lines changed

packages/astro/src/default/utils/content.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ export async function getTutorial(): Promise<Tutorial> {
4444
partTemplate: 'Part ${index}: ${title}',
4545
noPreviewNorStepsText: 'No preview to run nor steps to show',
4646
startWebContainerText: 'Run this tutorial',
47+
filesTitleText: 'Files',
48+
prepareEnvironmentTitleText: 'Preparing Environment',
49+
toggleTerminalButtonText: 'Toggle Terminal',
50+
solveButtonText: 'Solve',
51+
resetButtonText: 'Reset',
4752
} satisfies Lesson['data']['i18n'],
4853
tutorialMetaData.i18n,
4954
);

packages/components/react/src/Panels/EditorPanel.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { I18n } from '@tutorialkit/types';
12
import { useEffect, useRef } from 'react';
23
import { Panel, PanelGroup, PanelResizeHandle, type ImperativePanelHandle } from 'react-resizable-panels';
34
import {
@@ -7,8 +8,8 @@ import {
78
type OnScrollCallback as OnEditorScroll,
89
} from '../core/CodeMirrorEditor/index.js';
910
import { FileTree } from '../core/FileTree.js';
10-
import resizePanelStyles from '../styles/resize-panel.module.css';
1111
import type { Theme } from '../core/types.js';
12+
import resizePanelStyles from '../styles/resize-panel.module.css';
1213
import { isMobile } from '../utils/mobile.js';
1314

1415
const DEFAULT_FILE_TREE_SIZE = 25;
@@ -17,6 +18,7 @@ interface Props {
1718
theme: Theme;
1819
id: unknown;
1920
files: string[];
21+
i18n: I18n;
2022
hideRoot?: boolean;
2123
fileTreeScope?: string;
2224
showFileTree?: boolean;
@@ -33,6 +35,7 @@ export function EditorPanel({
3335
theme,
3436
id,
3537
files,
38+
i18n,
3639
hideRoot,
3740
fileTreeScope,
3841
showFileTree = true,
@@ -68,7 +71,7 @@ export function EditorPanel({
6871
<div className="panel-header border-r border-b border-tk-elements-app-borderColor">
6972
<div className="panel-title">
7073
<div className="panel-icon i-ph-tree-structure-duotone shrink-0"></div>
71-
<span className="text-sm">Files</span>
74+
<span className="text-sm">{i18n.filesTitleText}</span>
7275
</div>
7376
</div>
7477
<FileTree
@@ -86,7 +89,7 @@ export function EditorPanel({
8689
hitAreaMargins={{ fine: 8, coarse: 8 }}
8790
/>
8891
<Panel className="flex flex-col" defaultSize={100} minSize={10}>
89-
<FileTab editorDocument={editorDocument} onHelpClick={onHelpClick} helpAction={helpAction} />
92+
<FileTab i18n={i18n} editorDocument={editorDocument} onHelpClick={onHelpClick} helpAction={helpAction} />
9093
<div className="h-full flex-1 overflow-hidden">
9194
<CodeMirrorEditor
9295
className="h-full"
@@ -104,12 +107,13 @@ export function EditorPanel({
104107
}
105108

106109
interface FileTabProps {
110+
i18n: I18n;
107111
editorDocument: EditorDocument | undefined;
108112
helpAction?: 'reset' | 'solve';
109113
onHelpClick?: () => void;
110114
}
111115

112-
function FileTab({ editorDocument, helpAction, onHelpClick }: FileTabProps) {
116+
function FileTab({ i18n, editorDocument, helpAction, onHelpClick }: FileTabProps) {
113117
const filePath = editorDocument?.filePath;
114118
const fileName = filePath?.split('/').at(-1) ?? '';
115119
const icon = fileName ? getFileIcon(fileName) : '';
@@ -123,9 +127,9 @@ function FileTab({ editorDocument, helpAction, onHelpClick }: FileTabProps) {
123127
{!!helpAction && (
124128
<button onClick={onHelpClick} className="panel-button px-2 py-0.5 -mr-1 -my-1">
125129
{helpAction === 'solve' && <div className="i-ph-lightbulb-duotone text-lg" />}
126-
{helpAction === 'solve' && 'Solve'}
130+
{helpAction === 'solve' && i18n.solveButtonText}
127131
{helpAction === 'reset' && <div className="i-ph-clock-counter-clockwise-duotone" />}
128-
{helpAction === 'reset' && 'Reset'}
132+
{helpAction === 'reset' && i18n.resetButtonText}
129133
</button>
130134
)}
131135
</div>

packages/components/react/src/Panels/PreviewPanel.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useStore } from '@nanostores/react';
22
import type { PreviewInfo, TutorialStore } from '@tutorialkit/runtime';
3+
import type { I18n } from '@tutorialkit/types';
34
import { createElement, forwardRef, memo, useCallback, useEffect, useImperativeHandle, useRef } from 'react';
45
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
56
import { BootScreen } from '../BootScreen.js';
@@ -10,6 +11,7 @@ interface Props {
1011
showToggleTerminal?: boolean;
1112
toggleTerminal?: () => void;
1213
tutorialStore: TutorialStore;
14+
i18n: I18n;
1315
}
1416

1517
const previewsContainer = globalThis.document ? document.getElementById('previews-container')! : ({} as HTMLElement);
@@ -21,7 +23,7 @@ export type ImperativePreviewHandle = {
2123
};
2224

2325
export const PreviewPanel = memo(
24-
forwardRef<ImperativePreviewHandle, Props>(({ showToggleTerminal, toggleTerminal, tutorialStore }, ref) => {
26+
forwardRef<ImperativePreviewHandle, Props>(({ showToggleTerminal, toggleTerminal, i18n, tutorialStore }, ref) => {
2527
const expectedPreviews = useStore(tutorialStore.previews);
2628
const iframeRefs = useRef<IFrameRef[]>([]);
2729

@@ -86,7 +88,7 @@ export const PreviewPanel = memo(
8688
<div className="panel-header border-b border-tk-elements-app-borderColor justify-between">
8789
<div className="panel-title">
8890
<div className="panel-icon i-ph-lightning-duotone"></div>
89-
<span className="text-sm">Preparing Environment</span>
91+
<span className="text-sm">{i18n.prepareEnvironmentTitleText}</span>
9092
</div>
9193
{showToggleTerminal && (
9294
<button
@@ -95,7 +97,7 @@ export const PreviewPanel = memo(
9597
onClick={() => toggleTerminal?.()}
9698
>
9799
<span className="panel-button-icon i-ph-terminal-window-duotone"></span>
98-
<span className="text-sm">Toggle Terminal</span>
100+
<span className="text-sm">{i18n.toggleTerminalButtonText}</span>
99101
</button>
100102
)}
101103
</div>

packages/components/react/src/Panels/WorkspacePanel.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
1+
import { useStore } from '@nanostores/react';
12
import { TutorialStore } from '@tutorialkit/runtime';
3+
import type { I18n } from '@tutorialkit/types';
24
import { useCallback, useEffect, useRef, useState } from 'react';
3-
import { useStore } from '@nanostores/react';
45
import { Panel, PanelGroup, PanelResizeHandle, type ImperativePanelHandle } from 'react-resizable-panels';
56
import type {
67
OnChangeCallback as OnEditorChange,
78
OnScrollCallback as OnEditorScroll,
89
} from '../core/CodeMirrorEditor/index.js';
9-
import resizePanelStyles from '../styles/resize-panel.module.css';
1010
import type { Theme } from '../core/types.js';
11+
import resizePanelStyles from '../styles/resize-panel.module.css';
12+
import { classNames } from '../utils/classnames.js';
1113
import { EditorPanel } from './EditorPanel.js';
1214
import { PreviewPanel, type ImperativePreviewHandle } from './PreviewPanel.js';
1315
import { TerminalPanel } from './TerminalPanel.js';
14-
import { classNames } from '../utils/classnames.js';
1516

1617
const DEFAULT_TERMINAL_SIZE = 25;
1718

@@ -154,6 +155,7 @@ export function WorkspacePanel({ tutorialStore, theme }: Props) {
154155
showFileTree={fileTree}
155156
editorDocument={currentDocument}
156157
files={lesson.files[1]}
158+
i18n={lesson.data.i18n as I18n}
157159
hideRoot={lesson.data.hideRoot}
158160
helpAction={helpAction}
159161
onHelpClick={onHelpClick}
@@ -181,6 +183,7 @@ export function WorkspacePanel({ tutorialStore, theme }: Props) {
181183
>
182184
<PreviewPanel
183185
tutorialStore={tutorialStore}
186+
i18n={lesson.data.i18n as I18n}
184187
ref={previewRef}
185188
showToggleTerminal={!hideTerminalPanel}
186189
toggleTerminal={toggleTerminal}

packages/types/src/entities/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { I18nSchema } from '../schemas/i18n.js';
12
import type { ChapterSchema, LessonSchema, PartSchema } from '../schemas/index.js';
23

34
export type * from './nav.js';
@@ -49,6 +50,8 @@ export interface Lesson<T = unknown> {
4950
Markdown: T;
5051
}
5152

53+
export type I18n = Required<NonNullable<I18nSchema>>;
54+
5255
export interface Tutorial {
5356
logoLink?: string;
5457
firstPartId?: string;

packages/types/src/schemas/i18n.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,41 @@ export const i18nSchema = z.object({
2222
* @default 'No preview to run nor steps to show'
2323
*/
2424
noPreviewNorStepsText: z.string().optional(),
25+
26+
/**
27+
* Text shown on top of the file tree.
28+
*
29+
* @default 'Files'
30+
*/
31+
filesTitleText: z.string().optional(),
32+
33+
/**
34+
* Text shown on top of the steps section.
35+
*
36+
* @default 'Preparing Environment'
37+
*/
38+
prepareEnvironmentTitleText: z.string().optional(),
39+
40+
/**
41+
* Text shown for the toggle terminal button.
42+
*
43+
* @default 'Toggle Terminal'
44+
*/
45+
toggleTerminalButtonText: z.string().optional(),
46+
47+
/**
48+
* Text shown for the solve button.
49+
*
50+
* @default 'Solve'
51+
*/
52+
solveButtonText: z.string().optional(),
53+
54+
/**
55+
* Text shown for the reset button.
56+
*
57+
* @default 'Reset'
58+
*/
59+
resetButtonText: z.string().optional(),
2560
});
2661

2762
export type I18nSchema = z.infer<typeof i18nSchema>;

0 commit comments

Comments
 (0)