mirror of
https://github.com/stan-smith/FossFLOW.git
synced 2025-12-24 06:58:48 -05:00
feat: add i18n to main menu & docs: update Chinese README (#130)
* docs: update Chinese README * feat: add i18n to main menu * feat: add i18n for 3 components in fossflow-lib (ConnectorHintTooltip, HelpDialog, ImportHintTooltip)
This commit is contained in:
@@ -4,6 +4,19 @@
|
||||
<a href="../README.md">English</a> | <a href="README.cn.md">简体中文</a>
|
||||
</p>
|
||||
|
||||
<b>嗨!</b> 我是 Stan,如果您使用过 FossFLOW 并觉得它对您有帮助,<b>我会非常感激您能捐助一点点 :)</b> 我全职工作,抽时间来维护这个项目已经很不容易了。
|
||||
如果我为您实现了某个功能,或者修复了某个 bug,能得到您的支持将非常棒 :) 如果不能,也没关系,这个软件将永远免费!
|
||||
|
||||
[](https://ko-fi.com/P5P61KBXA3)
|
||||
|
||||
<img width="30" height="30" alt="image" src="https://github.com/user-attachments/assets/dc6ec9ca-48d7-4047-94cf-5c4f7ed63b84" /> <b> https://buymeacoffee.com/stan.smith </b>
|
||||
|
||||
感谢,
|
||||
|
||||
-Stan
|
||||
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------------
|
||||
FossFLOW 是一款功能强大的、开源的渐进式 Web 应用(PWA),专为创建精美的等距图表而设计。它基于 React 和 Isoflow(现已 fork 并以 fossflow 名称发布到 NPM)库构建,完全在浏览器中运行,并支持离线使用。
|
||||
|
||||

|
||||
|
||||
@@ -6,6 +6,7 @@ import { IsoflowProps } from 'src/types';
|
||||
import { setWindowCursor, modelFromModelStore } from 'src/utils';
|
||||
import { useModelStore, ModelProvider } from 'src/stores/modelStore';
|
||||
import { SceneProvider } from 'src/stores/sceneStore';
|
||||
import { LocaleProvider } from 'src/stores/localeStore';
|
||||
import { GlobalStyles } from 'src/styles/GlobalStyles';
|
||||
import { Renderer } from 'src/components/Renderer/Renderer';
|
||||
import { UiOverlay } from 'src/components/UiOverlay/UiOverlay';
|
||||
@@ -84,13 +85,15 @@ const App = ({
|
||||
export const Isoflow = (props: IsoflowProps) => {
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<ModelProvider>
|
||||
<SceneProvider>
|
||||
<UiStateProvider>
|
||||
<App {...props} />
|
||||
</UiStateProvider>
|
||||
</SceneProvider>
|
||||
</ModelProvider>
|
||||
<LocaleProvider locale={props.locale || enUS}>
|
||||
<ModelProvider>
|
||||
<SceneProvider>
|
||||
<UiStateProvider>
|
||||
<App {...props} />
|
||||
</UiStateProvider>
|
||||
</SceneProvider>
|
||||
</ModelProvider>
|
||||
</LocaleProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react';
|
||||
import { Box, IconButton, Paper, Typography, useTheme } from '@mui/material';
|
||||
import { Close as CloseIcon } from '@mui/icons-material';
|
||||
import { useUiStateStore } from 'src/stores/uiStateStore';
|
||||
import { useTranslation } from 'src/stores/localeStore';
|
||||
|
||||
const STORAGE_KEY = 'fossflow_connector_hint_dismissed';
|
||||
|
||||
@@ -10,6 +11,7 @@ interface Props {
|
||||
}
|
||||
|
||||
export const ConnectorHintTooltip = ({ toolMenuRef }: Props) => {
|
||||
const { t } = useTranslation('connectorHintTooltip');
|
||||
const theme = useTheme();
|
||||
const connectorInteractionMode = useUiStateStore((state) => state.connectorInteractionMode);
|
||||
const mode = useUiStateStore((state) => state.mode);
|
||||
@@ -85,30 +87,28 @@ export const ConnectorHintTooltip = ({ toolMenuRef }: Props) => {
|
||||
</IconButton>
|
||||
|
||||
<Typography variant="subtitle2" gutterBottom sx={{ fontWeight: 600 }}>
|
||||
{connectorInteractionMode === 'click' ? 'Tip: Creating Connectors' : 'Tip: Connector Tools'}
|
||||
{connectorInteractionMode === 'click' ? t('tipCreatingConnectors') : t('tipConnectorTools')}
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}>
|
||||
{connectorInteractionMode === 'click' ? (
|
||||
<>
|
||||
<strong>Click</strong> on the first node or point, then <strong>click</strong> on
|
||||
the second node or point to create a connection.
|
||||
<strong>{t('clickInstructionStart')}</strong> {t('clickInstructionMiddle')} <strong>{t('clickInstructionStart')}</strong> {t('clickInstructionEnd')}
|
||||
{mode.type === 'CONNECTOR' && mode.isConnecting && (
|
||||
<Box component="span" sx={{ display: 'block', mt: 1, color: 'primary.main' }}>
|
||||
Now click on the target to complete the connection.
|
||||
{t('nowClickTarget')}
|
||||
</Box>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<strong>Drag</strong> from the first node to the second node to create a connection.
|
||||
<strong>{t('dragStart')}</strong> {t('dragEnd')}
|
||||
</>
|
||||
)}
|
||||
</Typography>
|
||||
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
To reroute a connector, <strong>left-click</strong> on any point
|
||||
along the connector line and drag to create or move anchor points.
|
||||
{t('rerouteStart')} <strong>{t('rerouteMiddle')}</strong> {t('rerouteEnd')}
|
||||
</Typography>
|
||||
</Paper>
|
||||
</Box>
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
import { Close as CloseIcon } from '@mui/icons-material';
|
||||
import { useUiStateStore } from 'src/stores/uiStateStore';
|
||||
import { DialogTypeEnum } from 'src/types/ui';
|
||||
import { useTranslation } from 'src/stores/localeStore';
|
||||
|
||||
interface ShortcutItem {
|
||||
action: string;
|
||||
@@ -26,83 +27,9 @@ interface ShortcutItem {
|
||||
description: string;
|
||||
}
|
||||
|
||||
const keyboardShortcuts: ShortcutItem[] = [
|
||||
{
|
||||
action: 'Undo',
|
||||
shortcut: 'Ctrl+Z',
|
||||
description: 'Undo the last action'
|
||||
},
|
||||
{
|
||||
action: 'Redo',
|
||||
shortcut: 'Ctrl+Y',
|
||||
description: 'Redo the last undone action'
|
||||
},
|
||||
{
|
||||
action: 'Redo (Alternative)',
|
||||
shortcut: 'Ctrl+Shift+Z',
|
||||
description: 'Alternative redo shortcut'
|
||||
},
|
||||
{
|
||||
action: 'Help',
|
||||
shortcut: 'F1',
|
||||
description: 'Open help dialog with keyboard shortcuts'
|
||||
},
|
||||
{
|
||||
action: 'Zoom In',
|
||||
shortcut: 'Mouse Wheel Up',
|
||||
description: 'Zoom in on the canvas'
|
||||
},
|
||||
{
|
||||
action: 'Zoom Out',
|
||||
shortcut: 'Mouse Wheel Down',
|
||||
description: 'Zoom out from the canvas'
|
||||
},
|
||||
{
|
||||
action: 'Pan Canvas',
|
||||
shortcut: 'Left-click + Drag',
|
||||
description: 'Pan the canvas when in Pan mode'
|
||||
},
|
||||
{
|
||||
action: 'Context Menu',
|
||||
shortcut: 'Right-click',
|
||||
description: 'Open context menu for items or empty space'
|
||||
}
|
||||
];
|
||||
|
||||
const mouseInteractions: ShortcutItem[] = [
|
||||
{
|
||||
action: 'Select Tool',
|
||||
shortcut: 'Click Select button',
|
||||
description: 'Switch to selection mode'
|
||||
},
|
||||
{
|
||||
action: 'Pan Tool',
|
||||
shortcut: 'Click Pan button',
|
||||
description: 'Switch to pan mode for moving canvas'
|
||||
},
|
||||
{
|
||||
action: 'Add Item',
|
||||
shortcut: 'Click Add item button',
|
||||
description: 'Open icon picker to add new items'
|
||||
},
|
||||
{
|
||||
action: 'Draw Rectangle',
|
||||
shortcut: 'Click Rectangle button',
|
||||
description: 'Switch to rectangle drawing mode'
|
||||
},
|
||||
{
|
||||
action: 'Create Connector',
|
||||
shortcut: 'Click Connector button',
|
||||
description: 'Switch to connector mode'
|
||||
},
|
||||
{
|
||||
action: 'Add Text',
|
||||
shortcut: 'Click Text button',
|
||||
description: 'Create a new text box'
|
||||
}
|
||||
];
|
||||
|
||||
export const HelpDialog = () => {
|
||||
const { t } = useTranslation('helpDialog');
|
||||
|
||||
const dialog = useUiStateStore((state) => {
|
||||
return state.dialog;
|
||||
});
|
||||
@@ -116,6 +43,82 @@ export const HelpDialog = () => {
|
||||
setDialog(null);
|
||||
};
|
||||
|
||||
const keyboardShortcuts = [
|
||||
{
|
||||
action: t('undoAction'),
|
||||
shortcut: 'Ctrl+Z',
|
||||
description: t('undoDescription')
|
||||
},
|
||||
{
|
||||
action: t('redoAction'),
|
||||
shortcut: 'Ctrl+Y',
|
||||
description: t('redoDescription')
|
||||
},
|
||||
{
|
||||
action: t('redoAltAction'),
|
||||
shortcut: 'Ctrl+Shift+Z',
|
||||
description: t('redoAltDescription')
|
||||
},
|
||||
{
|
||||
action: t('helpAction'),
|
||||
shortcut: 'F1',
|
||||
description: t('helpDescription')
|
||||
},
|
||||
{
|
||||
action: t('zoomInAction'),
|
||||
shortcut: t('zoomInShortcut'),
|
||||
description: t('zoomInDescription')
|
||||
},
|
||||
{
|
||||
action: t('zoomOutAction'),
|
||||
shortcut: t('zoomOutShortcut'),
|
||||
description: t('zoomOutDescription')
|
||||
},
|
||||
{
|
||||
action: t('panCanvasAction'),
|
||||
shortcut: t('panCanvasShortcut'),
|
||||
description: t('panCanvasDescription')
|
||||
},
|
||||
{
|
||||
action: t('contextMenuAction'),
|
||||
shortcut: t('contextMenuShortcut'),
|
||||
description: t('contextMenuDescription')
|
||||
}
|
||||
];
|
||||
|
||||
const mouseInteractions = [
|
||||
{
|
||||
action: t('selectToolAction'),
|
||||
shortcut: t('selectToolShortcut'),
|
||||
description: t('selectToolDescription')
|
||||
},
|
||||
{
|
||||
action: t('panToolAction'),
|
||||
shortcut: t('panToolShortcut'),
|
||||
description: t('panToolDescription')
|
||||
},
|
||||
{
|
||||
action: t('addItemAction'),
|
||||
shortcut: t('addItemShortcut'),
|
||||
description: t('addItemDescription')
|
||||
},
|
||||
{
|
||||
action: t('drawRectangleAction'),
|
||||
shortcut: t('drawRectangleShortcut'),
|
||||
description: t('drawRectangleDescription')
|
||||
},
|
||||
{
|
||||
action: t('createConnectorAction'),
|
||||
shortcut: t('createConnectorShortcut'),
|
||||
description: t('createConnectorDescription')
|
||||
},
|
||||
{
|
||||
action: t('addTextAction'),
|
||||
shortcut: t('addTextShortcut'),
|
||||
description: t('addTextDescription')
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={isOpen}
|
||||
@@ -131,7 +134,7 @@ export const HelpDialog = () => {
|
||||
<DialogTitle>
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
<Typography variant="h6" component="div">
|
||||
Keyboard Shortcuts & Help
|
||||
{t('title')}
|
||||
</Typography>
|
||||
<Button
|
||||
onClick={handleClose}
|
||||
@@ -153,15 +156,15 @@ export const HelpDialog = () => {
|
||||
<DialogContent>
|
||||
<Box sx={{ mb: 3 }}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Keyboard Shortcuts
|
||||
{t('keyboardShortcuts')}
|
||||
</Typography>
|
||||
<TableContainer component={Paper} variant="outlined">
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell sx={{ fontWeight: 'bold' }}>Action</TableCell>
|
||||
<TableCell sx={{ fontWeight: 'bold' }}>Shortcut</TableCell>
|
||||
<TableCell sx={{ fontWeight: 'bold' }}>Description</TableCell>
|
||||
<TableCell sx={{ fontWeight: 'bold' }}>{t('action')}</TableCell>
|
||||
<TableCell sx={{ fontWeight: 'bold' }}>{t('shortcut')}</TableCell>
|
||||
<TableCell sx={{ fontWeight: 'bold' }}>{t('description')}</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
@@ -194,15 +197,15 @@ export const HelpDialog = () => {
|
||||
|
||||
<Box>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Mouse Interactions
|
||||
{t('mouseInteractions')}
|
||||
</Typography>
|
||||
<TableContainer component={Paper} variant="outlined">
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell sx={{ fontWeight: 'bold' }}>Action</TableCell>
|
||||
<TableCell sx={{ fontWeight: 'bold' }}>Method</TableCell>
|
||||
<TableCell sx={{ fontWeight: 'bold' }}>Description</TableCell>
|
||||
<TableCell sx={{ fontWeight: 'bold' }}>{t('action')}</TableCell>
|
||||
<TableCell sx={{ fontWeight: 'bold' }}>{t('method')}</TableCell>
|
||||
<TableCell sx={{ fontWeight: 'bold' }}>{t('description')}</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
@@ -233,16 +236,14 @@ export const HelpDialog = () => {
|
||||
|
||||
<Box sx={{ mt: 3, p: 2, bgcolor: 'info.light', borderRadius: 1 }}>
|
||||
<Typography variant="body2" color="info.contrastText">
|
||||
<strong>Note:</strong> Keyboard shortcuts are disabled when typing
|
||||
in input fields, text areas, or content-editable elements to prevent
|
||||
conflicts.
|
||||
<strong>{t('note')}</strong> {t('noteContent')}
|
||||
</Typography>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
|
||||
<DialogActions>
|
||||
<Button onClick={handleClose} variant="contained">
|
||||
Close
|
||||
{t('close')}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Box, IconButton, Paper, Typography } from '@mui/material';
|
||||
import { Close as CloseIcon, FolderOpen as FolderOpenIcon } from '@mui/icons-material';
|
||||
import { useTranslation } from 'src/stores/localeStore';
|
||||
|
||||
const STORAGE_KEY = 'fossflow_import_hint_dismissed';
|
||||
|
||||
export const ImportHintTooltip = () => {
|
||||
const { t } = useTranslation('importHintTooltip');
|
||||
const [isDismissed, setIsDismissed] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -59,13 +61,12 @@ export const ImportHintTooltip = () => {
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 1 }}>
|
||||
<FolderOpenIcon sx={{ mr: 1, color: 'info.main' }} />
|
||||
<Typography variant="subtitle2" sx={{ fontWeight: 600 }}>
|
||||
Import Diagrams
|
||||
{t('title')}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
To import diagrams, click the <strong>menu button</strong> (☰) in the top left corner,
|
||||
then select <strong>"Open"</strong> to load your diagram files.
|
||||
{t('instructionStart')} <strong>{t('menuButton')}</strong> {t('instructionMiddle')} <strong>{t('openButton')}</strong> {t('instructionEnd')}
|
||||
</Typography>
|
||||
</Paper>
|
||||
</Box>
|
||||
|
||||
@@ -25,6 +25,7 @@ import { useModelStore } from 'src/stores/modelStore';
|
||||
import { useHistory } from 'src/hooks/useHistory';
|
||||
import { DialogTypeEnum } from 'src/types/ui';
|
||||
import { MenuItem } from './MenuItem';
|
||||
import { useTranslation } from 'src/stores/localeStore';
|
||||
|
||||
export const MainMenu = () => {
|
||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||
@@ -43,6 +44,8 @@ export const MainMenu = () => {
|
||||
const initialDataManager = useInitialDataManager();
|
||||
const { undo, redo, canUndo, canRedo, clearHistory } = useHistory();
|
||||
|
||||
const { t } = useTranslation('mainMenu');
|
||||
|
||||
const onToggleMenu = useCallback(
|
||||
(event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
@@ -183,7 +186,7 @@ export const MainMenu = () => {
|
||||
Icon={<UndoIcon />}
|
||||
disabled={!canUndo}
|
||||
>
|
||||
Undo
|
||||
{t('undo')}
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem
|
||||
@@ -191,7 +194,7 @@ export const MainMenu = () => {
|
||||
Icon={<RedoIcon />}
|
||||
disabled={!canRedo}
|
||||
>
|
||||
Redo
|
||||
{t('redo')}
|
||||
</MenuItem>
|
||||
|
||||
{(canUndo || canRedo) && sectionVisibility.actions && <Divider />}
|
||||
@@ -199,38 +202,38 @@ export const MainMenu = () => {
|
||||
{/* File Actions */}
|
||||
{mainMenuOptions.includes('ACTION.OPEN') && (
|
||||
<MenuItem onClick={onOpenModel} Icon={<FolderOpenIcon />}>
|
||||
Open
|
||||
{t('open')}
|
||||
</MenuItem>
|
||||
)}
|
||||
|
||||
{mainMenuOptions.includes('EXPORT.JSON') && (
|
||||
<MenuItem onClick={onExportAsJSON} Icon={<ExportJsonIcon />}>
|
||||
Export as JSON
|
||||
{t('exportJson')}
|
||||
</MenuItem>
|
||||
)}
|
||||
|
||||
{mainMenuOptions.includes('EXPORT.JSON') && (
|
||||
<MenuItem onClick={onExportAsCompactJSON} Icon={<ExportJsonIcon />}>
|
||||
Export as Compact JSON
|
||||
{t('exportCompactJson')}
|
||||
</MenuItem>
|
||||
)}
|
||||
|
||||
{mainMenuOptions.includes('EXPORT.PNG') && (
|
||||
<MenuItem onClick={onExportAsImage} Icon={<ExportImageIcon />}>
|
||||
Export as image
|
||||
{t('exportImage')}
|
||||
</MenuItem>
|
||||
)}
|
||||
|
||||
{mainMenuOptions.includes('ACTION.CLEAR_CANVAS') && (
|
||||
<MenuItem onClick={onClearCanvas} Icon={<DeleteOutlineIcon />}>
|
||||
Clear the canvas
|
||||
{t('clearCanvas')}
|
||||
</MenuItem>
|
||||
)}
|
||||
|
||||
<Divider />
|
||||
|
||||
<MenuItem onClick={onOpenSettings} Icon={<SettingsIcon />}>
|
||||
Settings
|
||||
{t('settings')}
|
||||
</MenuItem>
|
||||
|
||||
{sectionVisibility.links && (
|
||||
@@ -244,7 +247,7 @@ export const MainMenu = () => {
|
||||
}}
|
||||
Icon={<GitHubIcon />}
|
||||
>
|
||||
GitHub
|
||||
{t('gitHub')}
|
||||
</MenuItem>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -1,7 +1,93 @@
|
||||
import { LocaleProps } from '../types/isoflowProps';
|
||||
|
||||
const locale: LocaleProps = {
|
||||
"exampleText": "This is an example text"
|
||||
common: {
|
||||
exampleText: "This is an example text"
|
||||
},
|
||||
mainMenu: {
|
||||
undo: "Undo",
|
||||
redo: "Redo",
|
||||
open: "Open",
|
||||
exportJson: "Export as JSON",
|
||||
exportCompactJson: "Export as Compact JSON",
|
||||
exportImage: "Export as image",
|
||||
clearCanvas: "Clear the canvas",
|
||||
settings: "Settings",
|
||||
gitHub: "GitHub"
|
||||
},
|
||||
helpDialog: {
|
||||
title: "Keyboard Shortcuts & Help",
|
||||
close: "Close",
|
||||
keyboardShortcuts: "Keyboard Shortcuts",
|
||||
mouseInteractions: "Mouse Interactions",
|
||||
action: "Action",
|
||||
shortcut: "Shortcut",
|
||||
method: "Method",
|
||||
description: "Description",
|
||||
note: "Note:",
|
||||
noteContent: "Keyboard shortcuts are disabled when typing in input fields, text areas, or content-editable elements to prevent conflicts.",
|
||||
// Keyboard shortcuts
|
||||
undoAction: "Undo",
|
||||
undoDescription: "Undo the last action",
|
||||
redoAction: "Redo",
|
||||
redoDescription: "Redo the last undone action",
|
||||
redoAltAction: "Redo (Alternative)",
|
||||
redoAltDescription: "Alternative redo shortcut",
|
||||
helpAction: "Help",
|
||||
helpDescription: "Open help dialog with keyboard shortcuts",
|
||||
zoomInAction: "Zoom In",
|
||||
zoomInShortcut: "Mouse Wheel Up",
|
||||
zoomInDescription: "Zoom in on the canvas",
|
||||
zoomOutAction: "Zoom Out",
|
||||
zoomOutShortcut: "Mouse Wheel Down",
|
||||
zoomOutDescription: "Zoom out from the canvas",
|
||||
panCanvasAction: "Pan Canvas",
|
||||
panCanvasShortcut: "Left-click + Drag",
|
||||
panCanvasDescription: "Pan the canvas when in Pan mode",
|
||||
contextMenuAction: "Context Menu",
|
||||
contextMenuShortcut: "Right-click",
|
||||
contextMenuDescription: "Open context menu for items or empty space",
|
||||
// Mouse interactions
|
||||
selectToolAction: "Select Tool",
|
||||
selectToolShortcut: "Click Select button",
|
||||
selectToolDescription: "Switch to selection mode",
|
||||
panToolAction: "Pan Tool",
|
||||
panToolShortcut: "Click Pan button",
|
||||
panToolDescription: "Switch to pan mode for moving canvas",
|
||||
addItemAction: "Add Item",
|
||||
addItemShortcut: "Click Add item button",
|
||||
addItemDescription: "Open icon picker to add new items",
|
||||
drawRectangleAction: "Draw Rectangle",
|
||||
drawRectangleShortcut: "Click Rectangle button",
|
||||
drawRectangleDescription: "Switch to rectangle drawing mode",
|
||||
createConnectorAction: "Create Connector",
|
||||
createConnectorShortcut: "Click Connector button",
|
||||
createConnectorDescription: "Switch to connector mode",
|
||||
addTextAction: "Add Text",
|
||||
addTextShortcut: "Click Text button",
|
||||
addTextDescription: "Create a new text box"
|
||||
},
|
||||
connectorHintTooltip: {
|
||||
tipCreatingConnectors: "Tip: Creating Connectors",
|
||||
tipConnectorTools: "Tip: Connector Tools",
|
||||
clickInstructionStart: "Click",
|
||||
clickInstructionMiddle: "on the first node or point, then",
|
||||
clickInstructionEnd: "on the second node or point to create a connection.",
|
||||
nowClickTarget: "Now click on the target to complete the connection.",
|
||||
dragStart: "Drag",
|
||||
dragEnd: "from the first node to the second node to create a connection.",
|
||||
rerouteStart: "To reroute a connector,",
|
||||
rerouteMiddle: "left-click",
|
||||
rerouteEnd: "on any point along the connector line and drag to create or move anchor points."
|
||||
},
|
||||
importHintTooltip: {
|
||||
title: "Import Diagrams",
|
||||
instructionStart: "To import diagrams, click the",
|
||||
menuButton: "menu button",
|
||||
instructionMiddle: "(☰) in the top left corner, then select",
|
||||
openButton: "\"Open\"",
|
||||
instructionEnd: "to load your diagram files."
|
||||
}
|
||||
};
|
||||
|
||||
export default locale;
|
||||
|
||||
@@ -1,7 +1,93 @@
|
||||
import { LocaleProps } from '../types/isoflowProps';
|
||||
|
||||
const locale: LocaleProps = {
|
||||
"exampleText": "这是一段示例文本"
|
||||
common: {
|
||||
exampleText: "这是一段示例文本"
|
||||
},
|
||||
mainMenu: {
|
||||
undo: "撤销",
|
||||
redo: "重做",
|
||||
open: "打开",
|
||||
exportJson: "导出为 JSON",
|
||||
exportCompactJson: "导出为紧凑 JSON",
|
||||
exportImage: "导出为图片",
|
||||
clearCanvas: "清空画布",
|
||||
settings: "设置",
|
||||
gitHub: "GitHub"
|
||||
},
|
||||
helpDialog: {
|
||||
title: "键盘快捷键和帮助",
|
||||
close: "关闭",
|
||||
keyboardShortcuts: "键盘快捷键",
|
||||
mouseInteractions: "鼠标交互",
|
||||
action: "操作",
|
||||
shortcut: "快捷键",
|
||||
method: "方法",
|
||||
description: "描述",
|
||||
note: "注意:",
|
||||
noteContent: "在输入框、文本区域或可编辑内容元素中键入时,键盘快捷键会被禁用,以防止冲突。",
|
||||
// Keyboard shortcuts
|
||||
undoAction: "撤销",
|
||||
undoDescription: "撤销上一个操作",
|
||||
redoAction: "重做",
|
||||
redoDescription: "重做上一个撤销的操作",
|
||||
redoAltAction: "重做(备选)",
|
||||
redoAltDescription: "备选重做快捷键",
|
||||
helpAction: "帮助",
|
||||
helpDescription: "打开包含键盘快捷键的帮助对话框",
|
||||
zoomInAction: "放大",
|
||||
zoomInShortcut: "鼠标滚轮向上",
|
||||
zoomInDescription: "放大画布",
|
||||
zoomOutAction: "缩小",
|
||||
zoomOutShortcut: "鼠标滚轮向下",
|
||||
zoomOutDescription: "缩小画布",
|
||||
panCanvasAction: "平移画布",
|
||||
panCanvasShortcut: "左键拖拽",
|
||||
panCanvasDescription: "在平移模式下移动画布",
|
||||
contextMenuAction: "上下文菜单",
|
||||
contextMenuShortcut: "右键点击",
|
||||
contextMenuDescription: "为项目或空白区域打开上下文菜单",
|
||||
// Mouse interactions
|
||||
selectToolAction: "选择工具",
|
||||
selectToolShortcut: "点击选择按钮",
|
||||
selectToolDescription: "切换到选择模式",
|
||||
panToolAction: "平移工具",
|
||||
panToolShortcut: "点击平移按钮",
|
||||
panToolDescription: "切换到平移模式以移动画布",
|
||||
addItemAction: "添加项目",
|
||||
addItemShortcut: "点击添加项目按钮",
|
||||
addItemDescription: "打开图标选择器以添加新项目",
|
||||
drawRectangleAction: "绘制矩形",
|
||||
drawRectangleShortcut: "点击矩形按钮",
|
||||
drawRectangleDescription: "切换到矩形绘制模式",
|
||||
createConnectorAction: "创建连接器",
|
||||
createConnectorShortcut: "点击连接器按钮",
|
||||
createConnectorDescription: "切换到连接器模式",
|
||||
addTextAction: "添加文本",
|
||||
addTextShortcut: "点击文本按钮",
|
||||
addTextDescription: "创建新的文本框"
|
||||
},
|
||||
connectorHintTooltip: {
|
||||
tipCreatingConnectors: "提示:创建连接器",
|
||||
tipConnectorTools: "提示:连接器工具",
|
||||
clickInstructionStart: "点击",
|
||||
clickInstructionMiddle: "第一个节点或点,然后",
|
||||
clickInstructionEnd: "第二个节点或点来创建连接。",
|
||||
nowClickTarget: "现在点击目标以完成连接。",
|
||||
dragStart: "拖拽",
|
||||
dragEnd: "从第一个节点到第二个节点来创建连接。",
|
||||
rerouteStart: "要重新规划连接器线路,请",
|
||||
rerouteMiddle: "左键点击",
|
||||
rerouteEnd: "连接器线上的任何点并拖拽以创建或移动锚点。"
|
||||
},
|
||||
importHintTooltip: {
|
||||
title: "导入图表",
|
||||
instructionStart: "要导入图表,请点击左上角的",
|
||||
menuButton: "菜单按钮",
|
||||
instructionMiddle: "(☰),然后选择",
|
||||
openButton: "\"打开\"",
|
||||
instructionEnd: "来加载您的图表文件。"
|
||||
}
|
||||
};
|
||||
|
||||
export default locale;
|
||||
|
||||
75
packages/fossflow-lib/src/stores/localeStore.tsx
Normal file
75
packages/fossflow-lib/src/stores/localeStore.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import React, { createContext, useContext, ReactNode } from 'react';
|
||||
import { LocaleProps } from '../types/isoflowProps';
|
||||
import enUS from '../i18n/en-US';
|
||||
|
||||
const LocaleContext = createContext<LocaleProps>(enUS);
|
||||
|
||||
interface LocaleProviderProps {
|
||||
locale: LocaleProps;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const LocaleProvider: React.FC<LocaleProviderProps> = ({ locale, children }) => {
|
||||
return (
|
||||
<LocaleContext.Provider value={locale}>
|
||||
{children}
|
||||
</LocaleContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useLocale = (): LocaleProps => {
|
||||
const context = useContext(LocaleContext);
|
||||
if (!context) {
|
||||
throw new Error('useLocale must be used within a LocaleProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
// Generic type helper for nested object access
|
||||
type NestedKeyOf<ObjectType extends object> = {
|
||||
[Key in keyof ObjectType & (string | number)]: ObjectType[Key] extends object
|
||||
? `${Key}.${NestedKeyOf<ObjectType[Key]>}`
|
||||
: `${Key}`;
|
||||
}[keyof ObjectType & (string | number)];
|
||||
|
||||
// Overloaded useTranslation function
|
||||
export function useTranslation(): {
|
||||
t: (key: NestedKeyOf<LocaleProps>) => string;
|
||||
};
|
||||
|
||||
export function useTranslation<K extends keyof LocaleProps>(
|
||||
namespace: K
|
||||
): {
|
||||
t: (key: keyof LocaleProps[K]) => string;
|
||||
};
|
||||
|
||||
export function useTranslation<K extends keyof LocaleProps>(namespace?: K) {
|
||||
const locale = useLocale();
|
||||
|
||||
if (namespace) {
|
||||
// Return scoped translation function for specific namespace
|
||||
const namespaceData = locale[namespace];
|
||||
const t = (key: keyof LocaleProps[K]): string => {
|
||||
const value = namespaceData[key];
|
||||
return typeof value === 'string' ? value : String(key);
|
||||
};
|
||||
return { t };
|
||||
} else {
|
||||
// Return global translation function with dot notation
|
||||
const t = (key: NestedKeyOf<LocaleProps>): string => {
|
||||
const parts = key.split('.');
|
||||
let current: any = locale;
|
||||
|
||||
for (const part of parts) {
|
||||
if (current && typeof current === 'object' && part in current) {
|
||||
current = current[part];
|
||||
} else {
|
||||
return key; // Return key if path not found
|
||||
}
|
||||
}
|
||||
|
||||
return typeof current === 'string' ? current : key;
|
||||
};
|
||||
return { t };
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,94 @@ export type InitialData = Model & {
|
||||
};
|
||||
|
||||
export interface LocaleProps {
|
||||
exampleText: string;
|
||||
// other locale keys
|
||||
common: {
|
||||
exampleText: string;
|
||||
};
|
||||
mainMenu: {
|
||||
undo: string;
|
||||
redo: string;
|
||||
open: string;
|
||||
exportJson: string;
|
||||
exportCompactJson: string;
|
||||
exportImage: string;
|
||||
clearCanvas: string;
|
||||
settings: string;
|
||||
gitHub: string;
|
||||
};
|
||||
helpDialog: {
|
||||
title: string;
|
||||
close: string;
|
||||
keyboardShortcuts: string;
|
||||
mouseInteractions: string;
|
||||
action: string;
|
||||
shortcut: string;
|
||||
method: string;
|
||||
description: string;
|
||||
note: string;
|
||||
noteContent: string;
|
||||
// Keyboard shortcuts
|
||||
undoAction: string;
|
||||
undoDescription: string;
|
||||
redoAction: string;
|
||||
redoDescription: string;
|
||||
redoAltAction: string;
|
||||
redoAltDescription: string;
|
||||
helpAction: string;
|
||||
helpDescription: string;
|
||||
zoomInAction: string;
|
||||
zoomInShortcut: string;
|
||||
zoomInDescription: string;
|
||||
zoomOutAction: string;
|
||||
zoomOutShortcut: string;
|
||||
zoomOutDescription: string;
|
||||
panCanvasAction: string;
|
||||
panCanvasShortcut: string;
|
||||
panCanvasDescription: string;
|
||||
contextMenuAction: string;
|
||||
contextMenuShortcut: string;
|
||||
contextMenuDescription: string;
|
||||
// Mouse interactions
|
||||
selectToolAction: string;
|
||||
selectToolShortcut: string;
|
||||
selectToolDescription: string;
|
||||
panToolAction: string;
|
||||
panToolShortcut: string;
|
||||
panToolDescription: string;
|
||||
addItemAction: string;
|
||||
addItemShortcut: string;
|
||||
addItemDescription: string;
|
||||
drawRectangleAction: string;
|
||||
drawRectangleShortcut: string;
|
||||
drawRectangleDescription: string;
|
||||
createConnectorAction: string;
|
||||
createConnectorShortcut: string;
|
||||
createConnectorDescription: string;
|
||||
addTextAction: string;
|
||||
addTextShortcut: string;
|
||||
addTextDescription: string;
|
||||
};
|
||||
connectorHintTooltip: {
|
||||
tipCreatingConnectors: string;
|
||||
tipConnectorTools: string;
|
||||
clickInstructionStart: string;
|
||||
clickInstructionMiddle: string;
|
||||
clickInstructionEnd: string;
|
||||
nowClickTarget: string;
|
||||
dragStart: string;
|
||||
dragEnd: string;
|
||||
rerouteStart: string;
|
||||
rerouteMiddle: string;
|
||||
rerouteEnd: string;
|
||||
};
|
||||
importHintTooltip: {
|
||||
title: string;
|
||||
instructionStart: string;
|
||||
menuButton: string;
|
||||
instructionMiddle: string;
|
||||
openButton: string;
|
||||
instructionEnd: string;
|
||||
};
|
||||
// other namespaces can be added here
|
||||
}
|
||||
|
||||
export interface IsoflowProps {
|
||||
|
||||
Reference in New Issue
Block a user