Skip to main content

Internationalization (i18n)


We’re currently making rapid changes to the product so our docs may be out of date. If you need help, please email

Forem's internationalization is currently in its early stages. You are encouraged to follow patterns laid out here, but it is not yet required for every pull request until further development and stabilization of the feature.

This development is a matter of translation as well as decoupling of copy from code. As this functionality is new, best practices in this area are still a work in progress.

i18n features and direction

Admins can choose a default locale among those currently supported. This feature is not complete and marked as experimental. This functionality will translate the display of the site (buttons, labels, etc.) and not user-generated content. It does not use the browser's locale as a hint. It only allows admins to set a default locale.

The site is also translatable via the dedicated path of /locale/:locale. So if a user types in /locale/fr they will get the french translation. This is also experimental and not technically "in use", but it does lay the framework for future development where we detect the language of the user-generated content on the page and serve the locale URL as the canonical page (not the one visible to the end user, they will still see the locale determined by the admin).

Future development should include members overriding the default locale as a setting so that any Forem user can use any Forem in their native language. This could involve additional caching complexity and is not yet in the works.

We are currently not localizing the admin backend at all, as that area has more nuanced communication and is generally less mature than the member-facing site.


The most helpful contributions are anything that gets the project a step closer to full localization. A PR that tries to do it all at once will not be as helpful. Any opportunity to remove copy entirely that is not necessary is also a step in the right direction and can be considered at pull-request time as an option, though that has a higher chance of being rejected due to the change to product functionality vs an objective improvement to localization.

We use standard Rails localization practices. We have files related to each locale such as config/locales/fr.yml.

The yml file will look like this...

add_comment: Ajouter un commentaire
comment: Commentaire
copy_link: Copier le lien
create_account: Créer un profil

And in HTML, these can be used as helpers, such as...

<div><%= t("core.add_comment") %></div>

t in this view helper context is short for I18n.translate. Check out the linked Rails guide for full information about how to use all standard translation and localization functionality.


We use the i18n.js gem to pass values to JavaScript. Our frontend code is currently divided into old asset pipeline code in app/assets/javascripts and modern Rails JS practices at app/javascript. In the asset pipeline you should directly use I18n.t and in app/javascript, there is a wrapper at app/javascript/utilities/locale.js you can include as you go. This is all subject to change in the name of code quality improvements.

This should generally be abstracted away for you if you follow above patterns, but for your information, we store translations in the view like this:

  <div id="i18n-translations" data-translations="<%= { I18n.locale.to_sym => { core: I18n::JS.translations[I18n.locale.to_sym][:core] } }.to_json %>"></div>

And originally access them in JavaScript like this:

const translationsDiv = document.getElementById('i18n-translations')

This approach helps ensure we don't need to ship all translations in JS files, but is somewhat naive and could be improved over time.


We use the i18n-tasks gem to manage updating the localization files as changes are made. It is not yet fully integrated into the test suite, but will be as a matter of ensuring consistency of internationalization. This gem provides several helpers for automatically translating missing values, some of which can plug into cloud translation services such as Google Translate, which will require you to add a key to the appropriate service. This is optional local functionality and does not constitute a hard dependency on development.