Skip to content

Adding a language

This guide explains how to add support for a new language to Cobalt.

Backend

  1. Make sure you start from the project root.

  2. Create a new locale by initializing it with pybabel:

bash
make locales-init locale=de

TIP

Short alias is also available: make l:i locale=de.

  1. Run the following commands to extract and update the translation strings:
bash
make locales-generate
make locales-update

TIP

Short aliases are also available: make l:g, make l:u.

  1. Add translations to the generated file at cobalt/backend/infrastructure/locales/de/LC_MESSAGES/messages.po.

  2. Compile the translations:

bash
make locales-compile

TIP

Short alias is also available: make l:c.

  1. Add the new language to the LanguageEnum in the cobalt/backend/domain/enums.py file:
python
class LanguageEnum(StrEnum):
    """
    User language enum.
    """
    EN = "en"
    UK = "uk"
    RU = "ru"
    DE = "de"
  1. Generate a new migration:
bash
make alembic-revision name="Add de locale"

TIP

Short alias is also available: make a:r name="Add de locale".

  1. Since alembic does not track changes to enum values, you need to manually update the new migration file in the cobalt/backend/infrastructure/databases/postgres/migrations/versions folder:
python
def upgrade() -> None:
    """Upgrade schema."""
    # ### commands auto generated by Alembic - please adjust! ###
    op.execute("ALTER TYPE languageenum ADD VALUE IF NOT EXISTS 'DE'") 
    # ### end Alembic commands ###
  1. Restart the backend container:
bash
docker restart cobalt-backend

Frontend

  1. Make sure you start from the project root.

  2. Create a new locale file in cobalt/frontend/src/locales, e.g. de.json, using one of the existing locale files (en.json, uk.json, ru.json) as a template.

  3. Import the new locale file in the cobalt/frontend/src/bootstrap.ts file:

typescript
import de from "@/locales/de.json"
  1. Add the new locale to setupI18n:
typescript
function setupI18n() {
  return createI18n<[MessageSchema], LanguageEnum>({
    legacy: false,
    locale: LanguageEnum.EN,
    fallbackLocale: LanguageEnum.EN,
    pluralRules: {
      // ...
    },
    messages: { en, uk, ru, de }
  })
}
  1. Configure custom plural rules for the new language in setupI18n, if its pluralization logic differs from the default:
typescript
pluralRules: {
  [LanguageEnum.UK]: (n: number): number => {
    if (n === 0) return 0
    if (n % 10 === 1 && n % 100 !== 11) return 1
    if (n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20)) return 2
    return 3
  },
  [LanguageEnum.DE]: (n: number): number => { 
    return n === 1 ? 0 : 1
  } 
}

TIP

If the language follows standard singular/plural rules (like English or German), you can skip this step entirely - vue-i18n's default pluralization will work out of the box.

  1. Add the new language to the LanguageEnum type in the cobalt/frontend/src/types/enums/language.ts file:
typescript
export enum LanguageEnum {
  EN = "en",
  RU = "ru",
  UK = "uk",
  DE = "de", 
}

WARNING

Don't forget to add the settings.general.language.options.de translation key to all locale files, including the new one.