|  | @@ -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);
 |