---
title: "How do I convert a .mo file back to a .po file?"
description: "Run `msgunfmt messages.mo -o messages.po` from GNU gettext. A `.mo` stores only the header and the source/translation pairs, so the recovered `.po` has no translator comments, source references, fuzzy or obsolete entries. In Python, `polib.mofile()` plus `save_as_pofile()` does the same."
category: localization-files
date: 2026-10-09
canonical: https://localizely.com/i18n-questions/localization-files/how-do-i-convert-a-mo-file-back-to-a-po-file/
---

# How do I convert a .mo file back to a .po file?

Use `msgunfmt`, the counterpart of `msgfmt` in GNU gettext:

```sh
msgunfmt messages.mo -o messages.po
```

Without `-o` the PO text goes to standard output. The tool ships with the `gettext` package of Linux distributions and Homebrew. The [manual page](https://www.gnu.org/software/gettext/manual/html_node/msgunfmt-Invocation.html) also lists `--java`, `--csharp` and `--tcl` modes for the other catalog formats `msgfmt` can produce.

The result is a valid, editable `.po` file, but not the file the translation was made from. A `.mo` contains a header (the entry with the empty `msgid`) and two parallel tables, one of original strings and one of translations, plus an optional hash table. Nothing else is stored, so these parts of the original file are gone:

- Translator comments (`# ...`), extracted comments (`#.`) and source references (`#:`), which is why the recovered file has no file names or line numbers.
- Flags such as `#, fuzzy` and `#, c-format`. Entries marked fuzzy are not compiled by default, so they are missing entirely rather than recovered as fuzzy.
- Untranslated entries, which have an empty `msgstr` and nothing to compile, and obsolete `#~` entries.

What does survive: contexts and plurals. The MO format encodes `msgctxt` in front of the original string and stores plural forms as NUL-separated variants, so `msgunfmt` writes proper `msgctxt`, `msgid_plural` and `msgstr[n]` entries again. Since gettext 0.22 `msgfmt` writes the strings as UTF-8 unless `--no-convert` is given, and `msgunfmt` keeps the charset declared in the header.

If you still have the program sources, get the references back by merging the recovered file with a fresh template; `msgmerge` takes the `#:` lines from the template and marks changed strings fuzzy:

```sh
xgettext --from-code=UTF-8 -o messages.pot src/*.py
msgmerge recovered.po messages.pot -o de.po
```

The same conversion from Python uses [polib](https://polib.readthedocs.io/en/latest/quickstart.html):

```python
import polib

mo = polib.mofile("messages.mo")
mo.save_as_pofile("messages.po")
```

Treat the decompiled file as a recovery, not as the source of truth. If the original `.po` exists in version control or in a translation tool, use that one; the `.mo` is only the compiled output. The PO entry types mentioned above are described at [PO file](/po-file/).

Source: https://localizely.com/i18n-questions/localization-files/how-do-i-convert-a-mo-file-back-to-a-po-file/
