Back to Canvas Lms

Internationalization (i18n) in Canvas LMS

doc/i18n.md

2026-05-20.1435.5 KB
Original Source

Internationalization (i18n) in Canvas LMS

Canvas LMS uses a internationalization (i18n) system that handles translations for both backend (Ruby) and frontend (JavaScript) code.

Overview

The i18n system in Canvas LMS is built on:

  • Ruby i18nliner for backend translations
  • @instructure/i18nliner-canvas for frontend translations
  • format-message for specific packages like canvas-rce

The system supports translations in:

  • Ruby files
  • JavaScript files
  • TypeScript files
  • Handlebars templates

Translation Files

Main translation files are located in several places:

Source and Generated Files

  • config/locales/generated/en.yml: Contains all extracted strings from source code
  • config/locales/generated/en-js.json: Frontend-specific translations
  • config/locales/generated/en-js-index.json: Used to generate JS modules for runtime
  • config/locales/locales.yml: Configuration file defining available locales

Runtime Files

  • public/javascripts/translations/: Contains compiled JavaScript translation bundles used at runtime
  • config/locales/*.yml: Language-specific translation files (e.g., hy.yml for Armenian)
    • These files are typically updated by automated translation processes

Package Translations

Some packages (e.g. canvas-media, canvas-rce) are published separately on npm. These packages handle translations differently:

  1. @instructure/translations Package

    • Central package containing translations for all Canvas packages
    • Located in packages/translations/
    • Contains language files for each supported locale
    • Packages can access their translations through this package
  2. Individual Package Setup Example using canvas-media:

    javascript
    // scripts/installTranslations.js
    const {getTranslationList, readTranslationFile} = require('@instructure/translations')
    
    • Uses installTranslations.js to:
      • Fetch translations from @instructure/translations
      • Generate locale-specific files
      • Set up code-splitting for translations
      • Create a getTranslations() function for runtime loading
  3. Automated Updates

    • Packages use scripts like commitTranslations.sh to:
      • Create a new git branch
      • Commit translation updates
      • Push changes to Gerrit
      • Notify teams via Slack when updates are needed

Using Translations

In React Components

javascript
import {useScope as createI18nScope} from '@canvas/i18n'
const I18n = createI18nScope('ComponentName')

// Usage
I18n.t('string to translate')

In Ruby Code

ruby
t('string to translate')

Special Cases

Canvas Rich Content Editor (RCE)

The RCE package (packages/canvas-rce) uses format-message instead of i18nliner for translations:

javascript
import formatMessage from 'format-message'

formatMessage('string to translate')

Development Workflow

1. Extract Translations

To extract translatable strings from the codebase:

bash
bundle exec rake canvas:compile_assets i18n:extract

This command:

  • Scans Ruby, JavaScript, and TypeScript files for translatable strings
  • Generates/updates translation files in config/locales/generated/

2. Available Tasks

  • i18n:check: Validates translation calls in Ruby and JS code
  • i18n:extract: Extracts strings from source code into YAML
  • i18n:generate: Creates runtime translation files
  • i18n:export: Prepares files for translators
  • i18n:import: Imports new translations
  • i18n:generate_js: Generates JavaScript translation files
  • i18n:generate_lolz: Generates LOLZ pseudo-translations (useful for testing)
  • i18n:lock: Locks specific translation keys

3. LOLZ Translations

Canvas includes a special "LOLZ" locale for testing and development:

  • Generated using rake i18n:generate_lolz
  • Creates pseudo-translations
  • Stored in config/locales/lolz.yml

Translation Management

Available Locales

Locales are configured in config/locales/locales.yml:

  • Defines which languages are available
  • Specifies if a locale is crowdsourced

Best Practices

  1. Use Scopes

    • Always use translation scopes to organize strings by component/feature
    • Choose meaningful scope names that reflect the component's purpose
  2. Avoid String Concatenation

    • Use interpolation instead of concatenating strings
    • Example: I18n.t('Welcome %{name}', { name: userName })
  3. Keep Translations Updated

    • Run i18n:extract when adding new strings
    • Verify translations with i18n:check

Testing

When writing tests that involve translations:

  1. Don't Mock Unless Necessary

    • Avoid jest.mock('@canvas/i18n')
    • Use real translation calls when possible
  2. Test User Experience

    • Focus on testing how translated content appears to users
    • Verify that translated strings are properly displayed
    • Consider using LOLZ translations for visual testing

Common Issues and Solutions

  1. Missing Translations

    • Run i18n:extract to ensure all strings are captured
    • Check that the scope is correctly defined
  2. Runtime Errors

    • Verify that translation files are properly generated
    • Check for missing interpolation variables

Future Considerations

Moving to i18next

For new standalone packages (similar to Canvas RCE or Canvas Meteor), consider using i18next instead of format-message.

Additional Resources