Internationalization (i18n)

Design on Web supports multiple languages with a built-in translation system. The editor ships with English and Indonesian locales, and you can add custom locales for any language.

Built-in Locales

Supported Locales

NameTypeDefaultDescription
enlocaleEnglish (default)
idlocaleIndonesian (Bahasa Indonesia)

Changing Language

Switch the editor language at runtime using the I18nManager singleton. You also need to update the Zustand store so that React components re-render with the new locale.

Changing localetypescript
import { I18nManager } from '@design-on-web/shared';

// Change to Indonesian
I18nManager.instance.setLocale('id');

// Also update the store so UI re-renders
engine.getStore().getState().setLocale('id');

// Switch back to English
I18nManager.instance.setLocale('en');
engine.getStore().getState().setLocale('en');

// Get current locale
const currentLocale = I18nManager.instance.getLocale();
// Returns: 'en' | 'id'
ℹ️
Two-step update
Both I18nManager.setLocale() and store.setLocale() must be called. The I18nManager handles the actual translation lookup, while the store triggers React re-renders.

Adding Custom Locales

Register a custom locale by providing a translation object with all required keys. Missing keys will fall back to the English translation.

Custom localetypescript
import { I18nManager } from '@design-on-web/shared';

// Register a custom locale (French)
I18nManager.instance.registerLocale('fr', {
  // Toolbar
  'toolbar.select': 'S\u00e9lectionner (V)',
  'toolbar.text': 'Texte (T)',
  'toolbar.shapes': 'Formes (S)',
  'toolbar.draw': 'Dessiner (D)',
  'toolbar.image': 'Image (I)',
  'toolbar.undo': 'Annuler',
  'toolbar.redo': 'R\u00e9tablir',
  'toolbar.export': 'Exporter',
  'toolbar.zoom_in': 'Zoom avant',
  'toolbar.zoom_out': 'Zoom arri\u00e8re',
  'toolbar.zoom_fit': 'Ajuster',
  'toolbar.zoom_reset': 'R\u00e9initialiser le zoom',

  // Panels
  'panel.templates': 'Mod\u00e8les',
  'panel.elements': '\u00c9l\u00e9ments',
  'panel.uploads': 'T\u00e9l\u00e9chargements',
  'panel.pages': 'Pages',
  'panel.layers': 'Calques',
  'panel.properties': 'Propri\u00e9t\u00e9s',
  'panel.history': 'Historique',
  'panel.draw': 'Dessin',

  // Properties
  'property.position': 'Position',
  'property.size': 'Taille',
  'property.rotation': 'Rotation',
  'property.opacity': 'Opacit\u00e9',
  'property.fill': 'Remplissage',
  'property.stroke': 'Contour',
  'property.corner_radius': 'Rayon de coin',
  'property.font_family': 'Police',
  'property.font_size': 'Taille de police',
  'property.font_weight': 'Graisse',
  'property.text_align': 'Alignement',
  'property.line_height': 'Interligne',
  'property.char_spacing': 'Espacement',

  // ... remaining keys
});

// Activate the new locale
I18nManager.instance.setLocale('fr');
engine.getStore().getState().setLocale('fr');
💡
Fallback behavior
If a translation key is missing in the active locale, the English translation is used automatically. This means you can incrementally translate the editor without needing all ~150 keys upfront.

useTranslation Hook

Use the useTranslation hook in custom React components to access the translation function.

useTranslation hooktsx
import { useTranslation } from '@design-on-web/ui';

function MyCustomPanel() {
  const { t, locale } = useTranslation();

  return (
    <div>
      <h3>{t('panel.properties')}</h3>
      <label>{t('property.fill')}</label>
      <p>Current locale: {locale}</p>
    </div>
  );
}

useTranslation Return Value

NameTypeDefaultDescription
t(key)(key: string) => stringTranslation function — returns the translated string for the given key
localestringCurrent active locale code

Translation Keys Reference

The editor uses approximately 150 translation keys organized into these categories:

  • Toolbar — ~15 keys for toolbar buttons and tooltips
  • Panel titles — ~10 keys for sidebar panel headings
  • Property labels — ~25 keys for the property panel
  • Layer panel — ~10 keys for layer actions
  • Export dialog — ~15 keys for export UI
  • Drawing — ~8 keys for the draw panel
  • Common actions — ~15 keys for shared UI strings
  • Version history — ~8 keys for the history panel
  • Templates — ~6 keys for the template panel
All translation keystypescript
// Translation keys are organized by category:

// Toolbar (~15 keys)
'toolbar.select'           // "Select (V)"
'toolbar.text'             // "Text (T)"
'toolbar.shapes'           // "Shapes (S)"
'toolbar.draw'             // "Draw (D)"
'toolbar.image'            // "Image (I)"
'toolbar.undo'             // "Undo"
'toolbar.redo'             // "Redo"
'toolbar.export'           // "Export"
'toolbar.zoom_in'          // "Zoom In"
'toolbar.zoom_out'         // "Zoom Out"
'toolbar.zoom_fit'         // "Fit to Screen"
'toolbar.zoom_reset'       // "Reset Zoom"
'toolbar.delete'           // "Delete"
'toolbar.duplicate'        // "Duplicate"
'toolbar.language'         // "Language"

// Panel titles (~10 keys)
'panel.templates'          // "Templates"
'panel.elements'           // "Elements"
'panel.uploads'            // "Uploads"
'panel.pages'              // "Pages"
'panel.layers'             // "Layers"
'panel.properties'         // "Properties"
'panel.history'            // "History"
'panel.draw'               // "Draw"
'panel.icons'              // "Icons"
'panel.text'               // "Text"

// Property labels (~25 keys)
'property.position'        // "Position"
'property.size'            // "Size"
'property.rotation'        // "Rotation"
'property.opacity'         // "Opacity"
'property.fill'            // "Fill"
'property.fill_type'       // "Fill Type"
'property.stroke'          // "Stroke"
'property.stroke_width'    // "Stroke Width"
'property.corner_radius'   // "Corner Radius"
'property.font_family'     // "Font Family"
'property.font_size'       // "Font Size"
'property.font_weight'     // "Font Weight"
'property.font_style'      // "Font Style"
'property.text_align'      // "Text Align"
'property.line_height'     // "Line Height"
'property.char_spacing'    // "Character Spacing"
'property.shadow'          // "Shadow"
'property.outline'         // "Outline"
'property.decoration'      // "Decoration"
'property.gradient_angle'  // "Gradient Angle"
'property.gradient_type'   // "Gradient Type"

// Layer panel (~10 keys)
'layer.lock'               // "Lock"
'layer.unlock'             // "Unlock"
'layer.hide'               // "Hide"
'layer.show'               // "Show"
'layer.rename'             // "Rename"
'layer.delete'             // "Delete"
'layer.bring_forward'      // "Bring Forward"
'layer.send_backward'      // "Send Backward"
'layer.bring_to_front'     // "Bring to Front"
'layer.send_to_back'       // "Send to Back"

// Export dialog (~15 keys)
'export.title'             // "Export"
'export.format'            // "Format"
'export.quality'           // "Quality"
'export.resolution'        // "Resolution"
'export.transparent'       // "Transparent Background"
'export.all_pages'         // "All Pages"
'export.current_page'      // "Current Page"
'export.download'          // "Download"
'export.cancel'            // "Cancel"

// Drawing (~8 keys)
'draw.brush_type'          // "Brush Type"
'draw.pencil'              // "Pencil"
'draw.spray'               // "Spray"
'draw.circle'              // "Circle"
'draw.brush_size'          // "Brush Size"
'draw.brush_color'         // "Brush Color"
'draw.brush_opacity'       // "Brush Opacity"

// Common actions (~15 keys)
'action.save'              // "Save"
'action.cancel'            // "Cancel"
'action.apply'             // "Apply"
'action.delete'            // "Delete"
'action.duplicate'         // "Duplicate"
'action.add_page'          // "Add Page"
'action.delete_page'       // "Delete Page"
'action.search'            // "Search"
'action.no_results'        // "No results found"
'action.loading'           // "Loading..."
'action.confirm'           // "Are you sure?"

// Version history (~8 keys)
'history.save_version'     // "Save Version"
'history.restore'          // "Restore"
'history.preview'          // "Preview"
'history.apply'            // "Apply"
'history.cancel_preview'   // "Cancel Preview"
'history.delete'           // "Delete"
'history.rename'           // "Rename"
'history.max_reached'      // "Maximum snapshots reached"

// Templates (~6 keys)
'template.apply'           // "Apply Template"
'template.save_as'         // "Save as Template"
'template.all'             // "All"
'template.search'          // "Search templates..."
'template.confirm_apply'   // "This will replace current content. Continue?"
'template.name'            // "Template Name"

UI: Language Selector

The toolbar includes a language selector dropdown (globe icon) that lets users switch between registered locales at runtime. The entire editor UI updates immediately without a page reload.

💡
Hiding the language selector
If your application manages language externally, you can hide the built-in language selector by not including it in your toolbar configuration, and call I18nManager.setLocale()programmatically instead.