From efdf8fbd87a55623b9123f364067c3d3b5db7cf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B2an?= Date: Fri, 12 Jul 2024 09:38:53 +0000 Subject: [PATCH 1/3] feat: finalize basic i18n support --- packages/astro/src/default/utils/content.ts | 5 +++ .../react/src/Panels/EditorPanel.tsx | 16 +++++---- .../react/src/Panels/PreviewPanel.tsx | 8 +++-- .../react/src/Panels/WorkspacePanel.tsx | 9 +++-- packages/types/src/entities/index.ts | 3 ++ packages/types/src/schemas/i18n.ts | 35 +++++++++++++++++++ 6 files changed, 64 insertions(+), 12 deletions(-) diff --git a/packages/astro/src/default/utils/content.ts b/packages/astro/src/default/utils/content.ts index 8826f4bfb..b103c4882 100644 --- a/packages/astro/src/default/utils/content.ts +++ b/packages/astro/src/default/utils/content.ts @@ -44,6 +44,11 @@ export async function getTutorial(): Promise { partTemplate: 'Part ${index}: ${title}', noPreviewNorStepsText: 'No preview to run nor steps to show', startWebContainerText: 'Run this tutorial', + filesTitleText: 'Files', + prepareEnvironmentTitleText: 'Preparing Environment', + toggleTerminalButtonText: 'Toggle Terminal', + solveButtonText: 'Solve', + resetButtonText: 'Reset', } satisfies Lesson['data']['i18n'], tutorialMetaData.i18n, ); diff --git a/packages/components/react/src/Panels/EditorPanel.tsx b/packages/components/react/src/Panels/EditorPanel.tsx index 5feb9e875..f11a767d3 100644 --- a/packages/components/react/src/Panels/EditorPanel.tsx +++ b/packages/components/react/src/Panels/EditorPanel.tsx @@ -1,3 +1,4 @@ +import type { I18n } from '@tutorialkit/types'; import { useEffect, useRef } from 'react'; import { Panel, PanelGroup, PanelResizeHandle, type ImperativePanelHandle } from 'react-resizable-panels'; import { @@ -7,8 +8,8 @@ import { type OnScrollCallback as OnEditorScroll, } from '../core/CodeMirrorEditor/index.js'; import { FileTree } from '../core/FileTree.js'; -import resizePanelStyles from '../styles/resize-panel.module.css'; import type { Theme } from '../core/types.js'; +import resizePanelStyles from '../styles/resize-panel.module.css'; import { isMobile } from '../utils/mobile.js'; const DEFAULT_FILE_TREE_SIZE = 25; @@ -17,6 +18,7 @@ interface Props { theme: Theme; id: unknown; files: string[]; + i18n: I18n; hideRoot?: boolean; fileTreeScope?: string; showFileTree?: boolean; @@ -33,6 +35,7 @@ export function EditorPanel({ theme, id, files, + i18n, hideRoot, fileTreeScope, showFileTree = true, @@ -68,7 +71,7 @@ export function EditorPanel({
- Files + {i18n.filesTitleText}
- +
void; } -function FileTab({ editorDocument, helpAction, onHelpClick }: FileTabProps) { +function FileTab({ i18n, editorDocument, helpAction, onHelpClick }: FileTabProps) { const filePath = editorDocument?.filePath; const fileName = filePath?.split('/').at(-1) ?? ''; const icon = fileName ? getFileIcon(fileName) : ''; @@ -123,9 +127,9 @@ function FileTab({ editorDocument, helpAction, onHelpClick }: FileTabProps) { {!!helpAction && ( )}
diff --git a/packages/components/react/src/Panels/PreviewPanel.tsx b/packages/components/react/src/Panels/PreviewPanel.tsx index 0cec500ec..a0b6b088c 100644 --- a/packages/components/react/src/Panels/PreviewPanel.tsx +++ b/packages/components/react/src/Panels/PreviewPanel.tsx @@ -1,5 +1,6 @@ import { useStore } from '@nanostores/react'; import type { PreviewInfo, TutorialStore } from '@tutorialkit/runtime'; +import type { I18n } from '@tutorialkit/types'; import { createElement, forwardRef, memo, useCallback, useEffect, useImperativeHandle, useRef } from 'react'; import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels'; import { BootScreen } from '../BootScreen.js'; @@ -10,6 +11,7 @@ interface Props { showToggleTerminal?: boolean; toggleTerminal?: () => void; tutorialStore: TutorialStore; + i18n: I18n; } const previewsContainer = globalThis.document ? document.getElementById('previews-container')! : ({} as HTMLElement); @@ -21,7 +23,7 @@ export type ImperativePreviewHandle = { }; export const PreviewPanel = memo( - forwardRef(({ showToggleTerminal, toggleTerminal, tutorialStore }, ref) => { + forwardRef(({ showToggleTerminal, toggleTerminal, i18n, tutorialStore }, ref) => { const expectedPreviews = useStore(tutorialStore.previews); const iframeRefs = useRef([]); @@ -86,7 +88,7 @@ export const PreviewPanel = memo(
- Preparing Environment + {i18n.prepareEnvironmentTitleText}
{showToggleTerminal && ( )}
diff --git a/packages/components/react/src/Panels/WorkspacePanel.tsx b/packages/components/react/src/Panels/WorkspacePanel.tsx index c39b40aa5..7ce9c9ae1 100644 --- a/packages/components/react/src/Panels/WorkspacePanel.tsx +++ b/packages/components/react/src/Panels/WorkspacePanel.tsx @@ -1,17 +1,18 @@ +import { useStore } from '@nanostores/react'; import { TutorialStore } from '@tutorialkit/runtime'; +import type { I18n } from '@tutorialkit/types'; import { useCallback, useEffect, useRef, useState } from 'react'; -import { useStore } from '@nanostores/react'; import { Panel, PanelGroup, PanelResizeHandle, type ImperativePanelHandle } from 'react-resizable-panels'; import type { OnChangeCallback as OnEditorChange, OnScrollCallback as OnEditorScroll, } from '../core/CodeMirrorEditor/index.js'; -import resizePanelStyles from '../styles/resize-panel.module.css'; import type { Theme } from '../core/types.js'; +import resizePanelStyles from '../styles/resize-panel.module.css'; +import { classNames } from '../utils/classnames.js'; import { EditorPanel } from './EditorPanel.js'; import { PreviewPanel, type ImperativePreviewHandle } from './PreviewPanel.js'; import { TerminalPanel } from './TerminalPanel.js'; -import { classNames } from '../utils/classnames.js'; const DEFAULT_TERMINAL_SIZE = 25; @@ -154,6 +155,7 @@ export function WorkspacePanel({ tutorialStore, theme }: Props) { showFileTree={fileTree} editorDocument={currentDocument} files={lesson.files[1]} + i18n={lesson.data.i18n as I18n} hideRoot={lesson.data.hideRoot} helpAction={helpAction} onHelpClick={onHelpClick} @@ -181,6 +183,7 @@ export function WorkspacePanel({ tutorialStore, theme }: Props) { > { Markdown: T; } +export type I18n = Required>; + export interface Tutorial { logoLink?: string; firstPartId?: string; diff --git a/packages/types/src/schemas/i18n.ts b/packages/types/src/schemas/i18n.ts index 17e07659b..1f3b83a58 100644 --- a/packages/types/src/schemas/i18n.ts +++ b/packages/types/src/schemas/i18n.ts @@ -22,6 +22,41 @@ export const i18nSchema = z.object({ * @default 'No preview to run nor steps to show' */ noPreviewNorStepsText: z.string().optional(), + + /** + * Text shown on top of the file tree. + * + * @default 'Files' + */ + filesTitleText: z.string().optional(), + + /** + * Text shown on top of the steps section. + * + * @default 'Preparing Environment' + */ + prepareEnvironmentTitleText: z.string().optional(), + + /** + * Text shown for the toggle terminal button. + * + * @default 'Toggle Terminal' + */ + toggleTerminalButtonText: z.string().optional(), + + /** + * Text shown for the solve button. + * + * @default 'Solve' + */ + solveButtonText: z.string().optional(), + + /** + * Text shown for the reset button. + * + * @default 'Reset' + */ + resetButtonText: z.string().optional(), }); export type I18nSchema = z.infer; From b8c18c85a2625d474eb1de92d70a39e5454d3462 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B2an?= Date: Fri, 12 Jul 2024 10:11:53 +0000 Subject: [PATCH 2/3] fix: type error --- packages/astro/src/default/utils/content/squash.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/astro/src/default/utils/content/squash.spec.ts b/packages/astro/src/default/utils/content/squash.spec.ts index 9798e0c57..e93d8716a 100644 --- a/packages/astro/src/default/utils/content/squash.spec.ts +++ b/packages/astro/src/default/utils/content/squash.spec.ts @@ -91,7 +91,7 @@ describe('squash', () => { data: { template: 'default', i18n: { - nextLessonPrefix: 'Next lesson: ', + startWebContainerText: 'Run this tutorial', partTemplate: 'Part ${index}: ${title}', }, } satisfies Metadata, @@ -107,7 +107,7 @@ describe('squash', () => { expect(squash([lesson1.data, tutorial.data], ['i18n'])).toEqual({ i18n: { - nextLessonPrefix: 'Next lesson: ', + startWebContainerText: 'Run this tutorial', partTemplate: 'Foobar: ${title}', }, }); From ccee5663ac2320a8b9c7b073664c694eab09652b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B2an?= Date: Fri, 12 Jul 2024 11:11:50 +0000 Subject: [PATCH 3/3] fix: code review --- packages/astro/src/default/utils/content.ts | 16 ++-------------- .../utils/content/default-localization.ts | 13 +++++++++++++ .../__snapshots__/create-tutorial.test.ts.snap | 1 + 3 files changed, 16 insertions(+), 14 deletions(-) create mode 100644 packages/astro/src/default/utils/content/default-localization.ts diff --git a/packages/astro/src/default/utils/content.ts b/packages/astro/src/default/utils/content.ts index 35b74f203..ea8aaa4bb 100644 --- a/packages/astro/src/default/utils/content.ts +++ b/packages/astro/src/default/utils/content.ts @@ -12,6 +12,7 @@ import { getCollection } from 'astro:content'; import glob from 'fast-glob'; import path from 'node:path'; import { IGNORED_FILES } from './constants'; +import { DEFAULT_LOCALIZATION } from './content/default-localization'; import { squash } from './content/squash.js'; import { logger } from './logger'; import { joinPaths } from './url'; @@ -39,20 +40,7 @@ export async function getTutorial(): Promise { // default template if not specified tutorialMetaData.template ??= 'default'; - tutorialMetaData.i18n = Object.assign( - { - partTemplate: 'Part ${index}: ${title}', - noPreviewNorStepsText: 'No preview to run nor steps to show', - startWebContainerText: 'Run this tutorial', - editPageText: 'Edit this page', - filesTitleText: 'Files', - prepareEnvironmentTitleText: 'Preparing Environment', - toggleTerminalButtonText: 'Toggle Terminal', - solveButtonText: 'Solve', - resetButtonText: 'Reset', - } satisfies Lesson['data']['i18n'], - tutorialMetaData.i18n, - ); + tutorialMetaData.i18n = Object.assign({ ...DEFAULT_LOCALIZATION }, tutorialMetaData.i18n); _tutorial.logoLink = data.logoLink; } else if (type === 'part') { diff --git a/packages/astro/src/default/utils/content/default-localization.ts b/packages/astro/src/default/utils/content/default-localization.ts new file mode 100644 index 000000000..decd68756 --- /dev/null +++ b/packages/astro/src/default/utils/content/default-localization.ts @@ -0,0 +1,13 @@ +import type { Lesson } from '@tutorialkit/types'; + +export const DEFAULT_LOCALIZATION = { + partTemplate: 'Part ${index}: ${title}', + noPreviewNorStepsText: 'No preview to run nor steps to show', + startWebContainerText: 'Run this tutorial', + editPageText: 'Edit this page', + filesTitleText: 'Files', + prepareEnvironmentTitleText: 'Preparing Environment', + toggleTerminalButtonText: 'Toggle Terminal', + solveButtonText: 'Solve', + resetButtonText: 'Reset', +} satisfies Lesson['data']['i18n']; diff --git a/packages/cli/tests/__snapshots__/create-tutorial.test.ts.snap b/packages/cli/tests/__snapshots__/create-tutorial.test.ts.snap index 3efc469d5..43d276a58 100644 --- a/packages/cli/tests/__snapshots__/create-tutorial.test.ts.snap +++ b/packages/cli/tests/__snapshots__/create-tutorial.test.ts.snap @@ -244,6 +244,7 @@ exports[`create and eject a project 1`] = ` "src/utils/constants.ts", "src/utils/content", "src/utils/content.ts", + "src/utils/content/default-localization.ts", "src/utils/content/squash.ts", "src/utils/logger.ts", "src/utils/nav.ts",