mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
Internationalization support (#477)
* add i18next lib add some translations * add translations * fix font-family * fix pin versions
This commit is contained in:
parent
1a03a29025
commit
ff7a340d2f
15 changed files with 286 additions and 162 deletions
105
src/index.tsx
105
src/index.tsx
|
@ -77,6 +77,8 @@ import Stack from "./components/Stack";
|
|||
import { FixedSideContainer } from "./components/FixedSideContainer";
|
||||
import { ToolIcon } from "./components/ToolIcon";
|
||||
import { ExportDialog } from "./components/ExportDialog";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import "./i18n";
|
||||
|
||||
let { elements } = createScene();
|
||||
const { history } = createHistory();
|
||||
|
@ -129,7 +131,7 @@ export function viewportCoordsToSceneCoords(
|
|||
return { x, y };
|
||||
}
|
||||
|
||||
export class App extends React.Component<{}, AppState> {
|
||||
export class App extends React.Component<any, AppState> {
|
||||
canvas: HTMLCanvasElement | null = null;
|
||||
rc: RoughCanvas | null = null;
|
||||
|
||||
|
@ -359,6 +361,7 @@ export class App extends React.Component<{}, AppState> {
|
|||
};
|
||||
|
||||
private renderSelectedShapeActions(elements: readonly ExcalidrawElement[]) {
|
||||
const { t } = this.props;
|
||||
const { elementType, editingElement } = this.state;
|
||||
const selectedElements = elements.filter(el => el.isSelected);
|
||||
const hasSelectedElements = selectedElements.length > 0;
|
||||
|
@ -381,7 +384,8 @@ export class App extends React.Component<{}, AppState> {
|
|||
"changeStrokeColor",
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult
|
||||
this.syncActionResult,
|
||||
t
|
||||
)}
|
||||
|
||||
{(hasBackground(elements) ||
|
||||
|
@ -391,14 +395,16 @@ export class App extends React.Component<{}, AppState> {
|
|||
"changeBackgroundColor",
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult
|
||||
this.syncActionResult,
|
||||
t
|
||||
)}
|
||||
|
||||
{this.actionManager.renderAction(
|
||||
"changeFillStyle",
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult
|
||||
this.syncActionResult,
|
||||
t
|
||||
)}
|
||||
<hr />
|
||||
</>
|
||||
|
@ -411,14 +417,16 @@ export class App extends React.Component<{}, AppState> {
|
|||
"changeStrokeWidth",
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult
|
||||
this.syncActionResult,
|
||||
t
|
||||
)}
|
||||
|
||||
{this.actionManager.renderAction(
|
||||
"changeSloppiness",
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult
|
||||
this.syncActionResult,
|
||||
t
|
||||
)}
|
||||
<hr />
|
||||
</>
|
||||
|
@ -430,14 +438,16 @@ export class App extends React.Component<{}, AppState> {
|
|||
"changeFontSize",
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult
|
||||
this.syncActionResult,
|
||||
t
|
||||
)}
|
||||
|
||||
{this.actionManager.renderAction(
|
||||
"changeFontFamily",
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult
|
||||
this.syncActionResult,
|
||||
t
|
||||
)}
|
||||
<hr />
|
||||
</>
|
||||
|
@ -447,14 +457,16 @@ export class App extends React.Component<{}, AppState> {
|
|||
"changeOpacity",
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult
|
||||
this.syncActionResult,
|
||||
t
|
||||
)}
|
||||
|
||||
{this.actionManager.renderAction(
|
||||
"deleteSelectedElements",
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult
|
||||
this.syncActionResult,
|
||||
t
|
||||
)}
|
||||
</div>
|
||||
</Island>
|
||||
|
@ -462,32 +474,38 @@ export class App extends React.Component<{}, AppState> {
|
|||
}
|
||||
|
||||
private renderShapesSwitcher() {
|
||||
const { t } = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
{SHAPES.map(({ value, icon }, index) => (
|
||||
<ToolIcon
|
||||
key={value}
|
||||
type="radio"
|
||||
icon={icon}
|
||||
checked={this.state.elementType === value}
|
||||
name="editor-current-shape"
|
||||
title={`${capitalizeString(value)} — ${
|
||||
capitalizeString(value)[0]
|
||||
}, ${index + 1}`}
|
||||
onChange={() => {
|
||||
this.setState({ elementType: value });
|
||||
elements = clearSelection(elements);
|
||||
document.documentElement.style.cursor =
|
||||
value === "text" ? "text" : "crosshair";
|
||||
this.forceUpdate();
|
||||
}}
|
||||
></ToolIcon>
|
||||
))}
|
||||
{SHAPES.map(({ value, icon }, index) => {
|
||||
const label = t(`toolBar.${value}`);
|
||||
return (
|
||||
<ToolIcon
|
||||
key={value}
|
||||
type="radio"
|
||||
icon={icon}
|
||||
checked={this.state.elementType === value}
|
||||
name="editor-current-shape"
|
||||
title={`${capitalizeString(label)} — ${
|
||||
capitalizeString(label)[0]
|
||||
}, ${index + 1}`}
|
||||
onChange={() => {
|
||||
this.setState({ elementType: value });
|
||||
elements = clearSelection(elements);
|
||||
document.documentElement.style.cursor =
|
||||
value === "text" ? "text" : "crosshair";
|
||||
this.forceUpdate();
|
||||
}}
|
||||
></ToolIcon>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
private renderCanvasActions() {
|
||||
const { t } = this.props;
|
||||
return (
|
||||
<Stack.Col gap={4}>
|
||||
<Stack.Row justifyContent={"space-between"}>
|
||||
|
@ -495,13 +513,15 @@ export class App extends React.Component<{}, AppState> {
|
|||
"loadScene",
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult
|
||||
this.syncActionResult,
|
||||
t
|
||||
)}
|
||||
{this.actionManager.renderAction(
|
||||
"saveScene",
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult
|
||||
this.syncActionResult,
|
||||
t
|
||||
)}
|
||||
<ExportDialog
|
||||
elements={elements}
|
||||
|
@ -540,14 +560,16 @@ export class App extends React.Component<{}, AppState> {
|
|||
"clearCanvas",
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult
|
||||
this.syncActionResult,
|
||||
t
|
||||
)}
|
||||
</Stack.Row>
|
||||
{this.actionManager.renderAction(
|
||||
"changeViewBackgroundColor",
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult
|
||||
this.syncActionResult,
|
||||
t
|
||||
)}
|
||||
</Stack.Col>
|
||||
);
|
||||
|
@ -556,6 +578,7 @@ export class App extends React.Component<{}, AppState> {
|
|||
public render() {
|
||||
const canvasWidth = window.innerWidth - CANVAS_WINDOW_OFFSET_LEFT;
|
||||
const canvasHeight = window.innerHeight - CANVAS_WINDOW_OFFSET_TOP;
|
||||
const { t } = this.props;
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
|
@ -624,14 +647,15 @@ export class App extends React.Component<{}, AppState> {
|
|||
ContextMenu.push({
|
||||
options: [
|
||||
navigator.clipboard && {
|
||||
label: "Paste",
|
||||
label: t("labels.paste"),
|
||||
action: () => this.pasteFromClipboard()
|
||||
},
|
||||
...this.actionManager.getContextMenuItems(
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult,
|
||||
action => this.canvasOnlyActions.includes(action)
|
||||
action => this.canvasOnlyActions.includes(action),
|
||||
t
|
||||
)
|
||||
],
|
||||
top: e.clientY,
|
||||
|
@ -649,18 +673,19 @@ export class App extends React.Component<{}, AppState> {
|
|||
ContextMenu.push({
|
||||
options: [
|
||||
navigator.clipboard && {
|
||||
label: "Copy",
|
||||
label: t("labels.copy"),
|
||||
action: this.copyToClipboard
|
||||
},
|
||||
navigator.clipboard && {
|
||||
label: "Paste",
|
||||
label: t("labels.paste"),
|
||||
action: () => this.pasteFromClipboard()
|
||||
},
|
||||
...this.actionManager.getContextMenuItems(
|
||||
elements,
|
||||
this.state,
|
||||
this.syncActionResult,
|
||||
action => !this.canvasOnlyActions.includes(action)
|
||||
action => !this.canvasOnlyActions.includes(action),
|
||||
t
|
||||
)
|
||||
],
|
||||
top: e.clientY,
|
||||
|
@ -1333,5 +1358,7 @@ export class App extends React.Component<{}, AppState> {
|
|||
}
|
||||
}
|
||||
|
||||
const AppWithTrans = withTranslation()(App);
|
||||
|
||||
const rootElement = document.getElementById("root");
|
||||
ReactDOM.render(<App />, rootElement);
|
||||
ReactDOM.render(<AppWithTrans />, rootElement);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue