Contributing Translations
This guide provides step-by-step instructions for contributing a new language translation to Scrumlr. The application uses i18next for internationalization and currently supports English, German and French.
Prerequisites
Section titled “Prerequisites”Before starting your translation contribution:
- Check existing languages: Review
src/i18n/to see currently supported languages - Language code: Identify the appropriate ISO 639-1 language code for your language (e.g.,
frfor French,esfor Spanish) - Development setup: Ensure you have the development environment set up (see Setup and the Contributing Guide)
Translation Files Structure
Section titled “Translation Files Structure”Scrumlr uses two main translation files per language:
src/i18n/{language_code}/translation.json- Main UI translationssrc/i18n/{language_code}/templates.json- Board template translations
Adding a language touches more than these two files. There are four separate hardcoded language lists plus two directories of static assets.
Step-by-Step Process
Section titled “Step-by-Step Process”Throughout this guide, replace es with your own language code.
1. Create Translation Files
Section titled “1. Create Translation Files”Create the translation directory and files for your language:
mkdir src/i18n/escp src/i18n/en/translation.json src/i18n/es/translation.jsoncp src/i18n/en/templates.json src/i18n/es/templates.json2. Translate Content
Section titled “2. Translate Content”Open src/i18n/es/translation.json and src/i18n/es/templates.json and translate all JSON values while keeping the keys unchanged.
3. Update the i18n Configuration
Section titled “3. Update the i18n Configuration”Register the language in src/i18n/index.ts. This is the first of four language lists:
// Import your translation filesimport translationEs from "./es/translation.json";import templatesEs from "./es/templates.json";
export const resources = { en: { translation: translationEn, templates: templatesEn, }, // ... // Add your language here es: { translation: translationEs, templates: templatesEs, },};4. Add Flag Icon
Section titled “4. Add Flag Icon”Flag icons live in src/assets/flags/ and are named after the ISO 3166-1 alpha-2 country code, in uppercase —
DE.svg, FR.svg, US.svg. Add yours the same way, e.g. src/assets/flags/ES.svg.
The existing flags are circular SVGs taken from HatScripts/circle-flags
(see src/assets/flags/readme.md). Take yours from the same source so it matches the others visually.
There is no top-level assets/ directory — assets/* is a path alias for src/assets/*, which is why the import strings
below look like they have no path.
5. Add the Language to the Settings Dropdown
Section titled “5. Add the Language to the Settings Dropdown”Update src/components/SettingsDialog/Components/LanguageSettingsDropdown.tsx — the second language list:
// Import the flag icon. Note the `?react` suffix: it is what turns the SVG into a component.import Spanish from "assets/flags/ES.svg?react";
// Add to the languages arrayconst languages = [ {icon: English, text: t("Language.english"), callback: () => changeLanguage("en"), code: "en"}, {icon: German, text: t("Language.german"), callback: () => changeLanguage("de"), code: "de"}, {icon: French, text: t("Language.french"), callback: () => changeLanguage("fr"), code: "fr"}, // Add your language {icon: Spanish, text: t("Language.spanish"), callback: () => changeLanguage("es"), code: "es"},];6. Add the Language to the Homepage Picker
Section titled “6. Add the Language to the Homepage Picker”The landing page has its own flag list, separate from the settings dialog. Update
src/routes/Homepage/Homepage.tsx:
import Spanish from "assets/flags/ES.svg?react";<li> <LegacyButton leftIcon={<Spanish />} className="homepage__language" hideLabel onClick={changeLanguage("es")}> Español </LegacyButton></li>Note that the label here is the language’s own name, written out literally rather than translated.
7. Add the Language to the Test i18n Instance
Section titled “7. Add the Language to the Test i18n Instance”src/i18nTest.ts is a second, test-only i18next instance with a third hardcoded list. It uses the translation
namespace only:
import translationEs from "./i18n/es/translation.json";
export const resources = { en: {translation: translationEn}, de: {translation: translationDe}, fr: {translation: translationFr}, es: {translation: translationEs},};Skip this and tests that assert on translated text will render raw keys, with no obvious explanation.
8. Add the Language Label Translation
Section titled “8. Add the Language Label Translation”Add the language name to the Language key of every translation file, including the new one:
e.g. src/i18n/en/translation.json:
{ "Language": { "english": "English", "german": "German", "french": "French", "spanish": "Spanish" }}9. Add Emoji Picker Data
Section titled “9. Add Emoji Picker Data”The emoji picker loads its dataset per language from public/emoji-data/<language_code>.json. EmojiPicker.tsx builds
the URL from i18n.resolvedLanguage, so a language without a matching file gets a 404 and a broken picker.
Generate the file with:
node scripts/generateEmojiData.mjsThe script writes to scripts/out/ (gitignored); copy the result into public/emoji-data/.
10. Testing Your Translation
Section titled “10. Testing Your Translation”-
Run the key parity test:
Terminal window yarn test --run src/__tests__/locales.test.tsThis compares your
translation.jsonagainst the English one. Be aware of its limits: it only checks the top two levels of keys, and it does not look attemplates.jsonat all. A green run means you have not missed a whole section and not that the file is complete. -
Start the development server:
Terminal window yarn start -
Test language switching:
- Switch from the homepage flag buttons, and from Settings → Appearance
- Verify all text displays correctly in both places
-
Test completeness:
- Navigate through all application features
- Check that no English text remains
- Open the emoji picker and a board template to check the
templatesnamespace and the emoji data - Verify special characters display correctly
Adding a Single Translation Key
Section titled “Adding a Single Translation Key”Far more common than adding a language: you are building a feature and need a new string.
-
Add the key to
src/i18n/en/translation.json, under an object named after your component ("Note": {"showMore": "Show more"}). -
Add the same key to
de/translation.jsonandfr/translation.json. A machine translation is acceptable — say so in the pull request and a native speaker can correct it later. Leaving the key out is not, becausesrc/__tests__/locales.test.tswill fail. -
Use it in the component:
const {t} = useTranslation();// ...{t("Note.showMore")}With interpolation:
{t("Note.userImageAlt", {user: participant.user.name})}
Two special cases:
- Board templates live in the
templatesnamespace:useTranslation(["translation", "templates"]). retryableerror keys must go under theErrorobject insrc/i18n/en/translation.json. The key is typed, so a thunk using a missing one will not compile — see State & Realtime.
Translation Guidelines
Section titled “Translation Guidelines”Best Practices
Section titled “Best Practices”- Maintain context: Understand the UI context where text appears
- Consistent terminology: Use consistent terms throughout your translation
- Length considerations: Consider text length differences that might affect UI layout
Handling Pluralization
Section titled “Handling Pluralization”Some strings use i18next pluralization syntax:
{ "column_one": "{{count}} Column", "column_other": "{{count}} Columns"}Follow your language’s pluralization rules and i18next documentation for proper implementation.
Special Characters and Encoding
Section titled “Special Characters and Encoding”- Use UTF-8 encoding for all translation files
- Test special characters (accents, umlauts, etc.) thoroughly
- Ensure proper display across different browsers and devices
Submitting Your Translation
Section titled “Submitting Your Translation”-
Create a pull request with the title format:
feat: add {Language Name} translation support -
Include in your PR description:
- Language code used
- Any specific notes about your translation choices
- Confirmation that you’ve tested the complete UI
-
Be prepared for review:
- Maintainers may ask native speakers to review samples
- Be open to feedback and suggestions
Maintaining Translations
Section titled “Maintaining Translations”Once your translation is merged:
- Monitor updates: Watch for new strings added to the English version
- Update accordingly: Contribute updates when new features are added
- Report issues: If you notice translation problems, please report them
Getting Help
Section titled “Getting Help”If you need assistance during the translation process:
- GitHub Discussions: Ask questions in the Q&A section
- Issues: Report technical problems with the translation system
- Community: Connect with other translators and maintainers
🌍 Our Amazing Translation Contributors 🎉
Section titled “🌍 Our Amazing Translation Contributors 🎉”We want to give a huge shoutout to all the incredible people who have made Scrumlr accessible to users around the world! 🙌
🇩🇪 German (Deutsch)
Section titled “🇩🇪 German (Deutsch)”- Core team contributors 💪
🇫🇷 French (Français)
Section titled “🇫🇷 French (Français)”- Loule95450 - Thank you for adding French support! 🥐✨
🚀 Want to see your name here?
Section titled “🚀 Want to see your name here?”Join our community and help make Scrumlr available in your language! Every contribution, no matter how small, makes a difference. 💪
💝 Thank You
Section titled “💝 Thank You”Thank you for contributing to make Scrumlr accessible to more users worldwide! Your efforts help break down language barriers and bring the power of collaborative retrospectives to teams everywhere.
Together, we’re building something amazing! 🚀✨