Overview
Excalidraw supports over 30 languages out of the box. You can easily set the language for the editor interface, and Excalidraw handles all UI translations automatically.
Quick Start
Setting the Language
Use the langCode prop to set the interface language:
import { Excalidraw } from "@excalidraw/excalidraw" ;
function App () {
return (
< Excalidraw langCode = "es-ES" />
);
}
Supported Languages
Excalidraw includes 30+ languages with at least 85% translation completion:
Western European
Eastern European
Asian Languages
Middle Eastern
import { Excalidraw } from "@excalidraw/excalidraw" ;
// English (default)
< Excalidraw langCode = "en" />
// Spanish
< Excalidraw langCode = "es-ES" />
// French
< Excalidraw langCode = "fr-FR" />
// German
< Excalidraw langCode = "de-DE" />
// Italian
< Excalidraw langCode = "it-IT" />
// Portuguese (Brazil)
< Excalidraw langCode = "pt-BR" />
// Portuguese (Portugal)
< Excalidraw langCode = "pt-PT" />
From packages/excalidraw/i18n.ts:21-75, only languages with at least 85% completion (defined by COMPLETION_THRESHOLD) are included by default.
Available Languages List
Importing Language Data
Access the full list of supported languages:
import { Excalidraw , languages , defaultLang } from "@excalidraw/excalidraw" ;
function App () {
return (
<>
< select onChange = { ( e ) => setLang ( e . target . value ) } >
{ languages . map (( lang ) => (
< option key = { lang . code } value = { lang . code } >
{ lang . label }
</ option >
)) }
</ select >
< Excalidraw langCode = { lang } />
</>
);
}
Language Object Structure
Each language object contains:
interface Language {
code : string ; // e.g., "en", "es-ES"
label : string ; // e.g., "English", "Español"
rtl ?: boolean ; // true for right-to-left languages
}
Example from source (packages/excalidraw/i18n.ts:11-15):
export interface Language {
code : string ;
label : string ;
rtl ?: boolean ;
}
Right-to-Left (RTL) Languages
RTL Support
Excalidraw automatically handles RTL languages:
import { Excalidraw } from "@excalidraw/excalidraw" ;
function App () {
return (
< Excalidraw
langCode = "ar-SA" // Arabic - automatically sets RTL
/>
);
}
From packages/excalidraw/i18n.ts:92-95, when you set an RTL language, Excalidraw automatically updates document.documentElement.dir to "rtl" and sets the language attribute.
Supported RTL Languages
Arabic: ar-SA
Hebrew: he-IL
Persian: fa-IR
Dynamic Language Switching
User Language Selector
Let users choose their preferred language:
import { Excalidraw , languages } from "@excalidraw/excalidraw" ;
import { useState } from "react" ;
function App () {
const [ langCode , setLangCode ] = useState ( "en" );
return (
< div style = { { height: "100vh" } } >
< div style = { { padding: "10px" } } >
< label >
Language:
< select
value = { langCode }
onChange = { ( e ) => setLangCode ( e . target . value ) }
>
{ languages . map (( lang ) => (
< option key = { lang . code } value = { lang . code } >
{ lang . label }
</ option >
)) }
</ select >
</ label >
</ div >
< Excalidraw langCode = { langCode } />
</ div >
);
}
Browser Language Detection
Automatic language detection based on browser settings:
import { Excalidraw , languages , defaultLang } from "@excalidraw/excalidraw" ;
import { useState , useEffect } from "react" ;
function App () {
const [ langCode , setLangCode ] = useState ( defaultLang . code );
useEffect (() => {
// Get browser language
const browserLang = navigator . language ;
// Find matching language in supported languages
const matchedLang = languages . find (
( lang ) => lang . code === browserLang || lang . code . startsWith ( browserLang . split ( "-" )[ 0 ])
);
if ( matchedLang ) {
setLangCode ( matchedLang . code );
}
}, []);
return < Excalidraw langCode = { langCode } /> ;
}
Persisting Language Preference
Save and restore the user’s language choice:
import { Excalidraw , languages } from "@excalidraw/excalidraw" ;
import { useState , useEffect } from "react" ;
function App () {
const [ langCode , setLangCode ] = useState (() => {
// Load from localStorage
const saved = localStorage . getItem ( "excalidraw-lang" );
return saved || "en" ;
});
useEffect (() => {
// Save to localStorage when language changes
localStorage . setItem ( "excalidraw-lang" , langCode );
}, [ langCode ]);
return (
<>
< select value = { langCode } onChange = { ( e ) => setLangCode ( e . target . value ) } >
{ languages . map (( lang ) => (
< option key = { lang . code } value = { lang . code } >
{ lang . label }
</ option >
)) }
</ select >
< Excalidraw langCode = { langCode } />
</>
);
}
Using the i18n Hook
useI18n Hook
For custom components that need translations:
import { useI18n } from "@excalidraw/excalidraw" ;
function CustomComponent () {
const { t , langCode } = useI18n ();
return (
< div >
< p > Current language: { langCode } </ p >
{ /* Use t() function for translations if you have access to translation keys */ }
</ div >
);
}
From packages/excalidraw/i18n.ts:165-172, the useI18n hook should be used in components rendered as Excalidraw children or memoized internal components.
Translation Function
Using the t() Function
The translation function from the source code (packages/excalidraw/i18n.ts:127-160):
export const t = (
path : string ,
replacement ?: { [ key : string ] : string | number } | null ,
fallback ?: string ,
) => {
// Returns translated string based on current language
}
Example usage with replacements:
// With variable replacement
t ( "labels.exportedElements" , { count: 5 })
// Returns: "5 elements exported" (translated)
// With fallback
t ( "labels.customLabel" , null , "Default text" )
// Returns: "Default text" if translation not found
Advanced Configuration
Language Setting Function
Programmatically set the language:
import { setLanguage , languages } from "@excalidraw/excalidraw" ;
// Set language (typically handled internally by Excalidraw)
const spanish = languages . find ( lang => lang . code === "es-ES" );
if ( spanish ) {
await setLanguage ( spanish );
}
The setLanguage function is typically called internally by Excalidraw. Use the langCode prop instead for standard use cases.
Default Language
Get the default language:
import { defaultLang } from "@excalidraw/excalidraw" ;
console . log ( defaultLang );
// Output: { code: "en", label: "English" }
Initial Language in AppState
Setting Initial Language
You can also set the language through initialData:
import { Excalidraw } from "@excalidraw/excalidraw" ;
function App () {
return (
< Excalidraw
langCode = "fr-FR"
initialData = { {
appState: {
// Other app state properties
},
} }
/>
);
}
Best Practices
Detect browser language
Use navigator.language to detect and set the initial language.
Provide language selector
Give users an easy way to change the language.
Persist preference
Save the user’s language choice to localStorage.
Test RTL languages
Ensure your custom UI components work correctly with RTL languages.
Fallback to English
Always have English as a fallback option.
Complete Example
Full i18n Implementation
import { Excalidraw , languages , defaultLang } from "@excalidraw/excalidraw" ;
import { useState , useEffect } from "react" ;
function App () {
const [ langCode , setLangCode ] = useState (() => {
// Try to load from localStorage
const saved = localStorage . getItem ( "excalidraw-lang" );
if ( saved ) return saved ;
// Try to detect browser language
const browserLang = navigator . language ;
const matched = languages . find (
( lang ) => lang . code === browserLang || lang . code . startsWith ( browserLang . split ( "-" )[ 0 ])
);
return matched ?. code || defaultLang . code ;
});
useEffect (() => {
localStorage . setItem ( "excalidraw-lang" , langCode );
}, [ langCode ]);
const currentLang = languages . find (( lang ) => lang . code === langCode );
return (
< div style = { { height: "100vh" , display: "flex" , flexDirection: "column" } } >
< header style = { { padding: "10px" , background: "#f0f0f0" } } >
< label >
{ currentLang ?. rtl ? "اللغة:" : "Language:" }
< select
value = { langCode }
onChange = { ( e ) => setLangCode ( e . target . value ) }
style = { { marginLeft: currentLang ?. rtl ? 0 : "10px" , marginRight: currentLang ?. rtl ? "10px" : 0 } }
>
{ languages . map (( lang ) => (
< option key = { lang . code } value = { lang . code } >
{ lang . label }
</ option >
)) }
</ select >
</ label >
</ header >
< div style = { { flex: 1 } } >
< Excalidraw langCode = { langCode } />
</ div >
</ div >
);
}
export default App ;
Next Steps