# General Translation Knowledge Base: General Translation vs. Babel and Flask-Babel: which should you use?
URL: https://generaltranslation.com/en-US/kb/general-translation-vs-flask-babel.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: Babel formats dates, numbers, and currencies for any locale and manages gettext catalogs, and Flask-Babel connects it to Flask, but neither translates your strings. General Translation's CLI fills in Babel's PO files for every language, plural forms included, so you can keep Flask-Babel and stop translating catalogs by hand.
Category: Internationalization
Last updated: 2026-10-16
Authors: Kevin Wu, Jackie Chen
Published: 2026-10-16

Babel formats dates, numbers, and currencies using Unicode CLDR data, and its `pybabel` command extracts, updates, and compiles gettext message catalogs. Flask-Babel connects those tools to Flask's request handling and Jinja templates. General Translation fills the catalogs between extraction and compilation, so your app gains automated translation while keeping its existing Flask-Babel setup.

## What Babel and Flask-Babel do well

- **Locale formatting from CLDR.** [Babel 2.18.0 bundles CLDR 47](https://babel.pocoo.org/en/latest/changelog.html#version-2-18-0) data for more than a thousand locale identifiers. `format_currency(1234.5, "EUR")` gives "1.234,50 €" in Spanish, and `format_date` gives "16 października 2026" in Polish.
- **Catalog tools.** [`pybabel extract`](https://babel.pocoo.org/en/latest/cmdline.html) reads Python files and Jinja templates through a `babel.cfg` mapping, `pybabel init` starts a PO file with the right plural rule and slots (three for Polish), `pybabel update` merges new strings and marks changed ones fuzzy, and `pybabel compile` builds the `.mo` files.
- **Flask integration.** A `locale_selector` and `timezone_selector` [decide each request's locale and time zone](https://python-babel.github.io/flask-babel/#configuration), `lazy_gettext` covers strings defined at import time, Jinja templates get `_()`, `{% trans %}`, and formatting filters, and `force_locale` switches locale temporarily, for example to send an email in another language.
- **Stable and free.** Both are BSD-licensed (see [Babel's license](https://babel.pocoo.org/en/latest/license.html)). Babel works in any Python program; Flask-Babel's [current release is 4.0.0](https://github.com/python-babel/flask-babel/releases/tag/v4.0.0).

## What Babel and Flask-Babel leave to you

Here's a small Flask-Babel app:

```python title="app.py"
from datetime import date
from flask import Flask, request
from flask_babel import Babel, gettext, ngettext, format_currency, format_date


def select_locale():
    return request.accept_languages.best_match(["en", "es", "pl"])


app = Flask(__name__)
babel = Babel(app, locale_selector=select_locale)


@app.route("/inbox/<int:count>")
def inbox(count):
    return {
        "title": gettext("Inbox"),
        "unread": ngettext("You have %(num)d unread message.", "You have %(num)d unread messages.", count),
        "price": format_currency(1234.5, "EUR"),
        "date": format_date(date(2026, 10, 16), format="long"),
    }
```

`pybabel extract`, `pybabel init -l pl`, and `pybabel compile` produce a Polish catalog with every `msgstr` empty. With Flask-Babel 4.0.0, a request with `Accept-Language: pl` then returned:

```json title="Output"
{
  "date": "16 października 2026",
  "price": "1 234,50 €",
  "title": "Inbox",
  "unread": "You have 5 unread messages."
}
```

Babel formats the date and price for Polish, and every string stays in English, with no warning. Each string waits for someone to fill in its PO entry, including all three Polish plural forms, then for `pybabel compile` and a redeploy. Every new or edited string repeats that in each language.

## Translating Babel's catalogs with General Translation

If Flask-Babel already works for you, keep it and let General Translation's CLI write the catalogs. Create an English catalog once and update it after each extraction, so the source sits in the same tree as the translations:

```bash
pybabel extract -F babel.cfg -k lazy_gettext -o messages.pot .
pybabel init -i messages.pot -d translations -l en     # once
pybabel update -i messages.pot -d translations -l en   # after each extract
```

Then point `gt.config.json` at it. This config was checked against `gt` 2.26.5:

```json title="gt.config.json"
{
  "defaultLocale": "en",
  "locales": ["es", "pl", "pt_BR"],
  "customMapping": { "pt_BR": { "code": "pt-BR" } },
  "files": {
    "pot": {
      "include": ["translations/[locale]/LC_MESSAGES/messages.po"],
      "transformationFormat": "PO"
    }
  }
}
```

The `pot` key reads PO and POT files, and `"transformationFormat": "PO"` writes translated `.po` files (see the [PO / POT format reference](/docs/cli/reference/formats/po-pot-files)). [`customMapping`](/docs/cli/reference/config#custom-mapping) connects Babel's `pt_BR` directory to the standard locale code `pt-BR`. Set up the [CLI](/docs/cli/quickstart). If you already have translations, import them with [`npx gt upload`](/docs/cli/reference/commands/upload) before generating new ones. Then run [`npx gt translate`](/docs/cli/reference/commands/translate), followed by `pybabel compile -d translations`.

General Translation sends each entry with its `msgctxt`, translator comments, source references, and flags as context. For plural entries, it writes the target language's `Plural-Forms` header and fills every `msgstr[n]` slot, so Polish gets all three, and it checks that placeholders such as `%(num)d` come through intact. Unchanged entries reuse earlier translations on the next run. A Glossary and Custom Prompts in a [Context Group](/docs/platform/dashboard/guides/defining-context-for-translations) keep terms consistent across your catalogs and the rest of your product, and reviewers can [edit each entry](/docs/platform/dashboard/guides/reviewing-translations) in the Dashboard. Run the CLI in CI so each deploy ships with current catalogs. Save later local edits with [`npx gt save-local`](/docs/cli/reference/commands/save-local) before generating again.

## Babel and Flask-Babel vs. General Translation at a glance

| Feature | Babel and Flask-Babel | General Translation |
| --- | --- | --- |
| How you write strings | `gettext` calls and Jinja `{% trans %}`, extracted to PO files | Keep those calls and templates; translate the extracted catalogs |
| Translates your content | No | Yes, with [`npx gt translate`](/docs/cli/reference/commands/translate) |
| Plural forms | `pybabel init` writes the slots; you fill each one | Generates every PO slot for the target language |
| Date, number, and currency formatting | Yes, from CLDR | Keeps Babel's formatting |
| Jinja templates | Yes | Translates the messages extracted from your existing templates |
| Frameworks | Babel anywhere; Flask-Babel for Flask | CLI for PO catalogs from Flask or other gettext applications |
| License | BSD | MIT-licensed CLI |
| Cost | Free | [Usage-based translation](https://generaltranslation.com/pricing) |

## When to use which

Keep your current translation process when translators or a TMS already deliver the catalogs you need. Add General Translation when you want new and changed messages translated during development, with the terminology and review tools your team shares across projects. Flask-Babel continues to handle Jinja, request locales, dates, and currencies; your application code stays the same.

## Key takeaways

- Babel formats dates, numbers, and currencies for any locale and manages gettext catalogs, and Flask-Babel wires it into Flask, but neither translates your strings.
- General Translation's CLI translates Babel's PO files with the `pot` key, `"transformationFormat": "PO"`, and `customMapping` for directories such as `pt_BR`, filling every plural slot for each language.
- Keep Flask-Babel's rendering and formatting, import your existing translations, and add General Translation between extraction and compilation.

## Related questions

- [General Translation vs. GNU gettext: do you need both?](https://generaltranslation.com/en-US/kb/general-translation-vs-gettext.mdx): Keep GNU gettext for extraction and rendering, and add General Translation to generate and maintain the PO files. General Translation carries translator notes and message context into generation, applies shared terminology, and gives reviewers a Dashboard editor. Your existing gettext calls and compiled catalogs stay in place.
- [General Translation vs. Django's translation framework: do you need both?](https://generaltranslation.com/en-US/kb/general-translation-vs-django-i18n.mdx): Use Django's translation framework and General Translation together. Django handles message extraction, language selection, and rendering. General Translation fills its PO files, including plural forms, with your product terminology and Dashboard review. Your templates and URL patterns stay the same while translations update with your code.
- [General Translation vs. i18next, react-intl, and next-intl: which i18n library should you use?](https://generaltranslation.com/en-US/kb/general-translation-vs-i18n-libraries.mdx): Open-source libraries such as i18next, react-intl, and next-intl don't translate anything. They display translations you supply in a catalog file for each language, written by you, your translators, or a separate service. General Translation's libraries are open source too and do the same job, and General Translation also does the translating, with your code as context.
- [Why use General Translation instead of a TMS (Smartling, Lokalise, Phrase, Crowdin, or Transifex)?](https://generaltranslation.com/en-US/kb/general-translation-vs-tms.mdx): A translation management system (TMS) such as Smartling, Lokalise, Phrase, Crowdin, or Transifex coordinates translators, but engineers still extract strings, send them out, wait for translations, and wire them back into the product. General Translation translates content in your code with its context, ships every language with each release, and runs on open-source libraries with no lock-in.
- [Can I do i18n without translation files?](https://generaltranslation.com/en-US/kb/i18n-without-translation-files.mdx): Yes. With gt-react or gt-next, content stays inline in your components inside a `<T>` component, and translations are generated at build time as output, so there are no keys or per-locale JSON files to write and keep in sync.

## Previous and next questions

- Previous question: [Can I do i18n without translation files?](https://generaltranslation.com/en-US/kb/i18n-without-translation-files.mdx)
- Next question: [General Translation vs. Crowdin: which should you use?](https://generaltranslation.com/en-US/kb/general-translation-vs-crowdin.mdx)

## Sitemap

See the [Knowledge Base sitemap](https://generaltranslation.com/kb/sitemap.md) for every article.
