Вопрос: Как локализовать проект при помощи mo файлов?

Лучший ответ: Проводить локализацию проектов PHP, и тем более WordPress, силами gettext не приходилось, но на днях делал это с некоторыми bash-скриптами. Возможно мой опыт будет полезен.
1. Необходимо определить домен и область поиска файлов сообщений.
Домен задается функцией textdomain, а путь к ресурсам - bindtextdomain.  bindtextdomain('example', './local');
textdomain('example');
2. В коде, весь локализуемый текст следует выводить через функцию gettext. Например: echo gettext("Hello world!");
echo gettext("Test123");
Можно использовать фразы, либо ключи.
Если использовать фразы, то не нужно будет создавать ресурсы для языка по умолчанию. Из минусов такого подхода, то что некоторые фразы могут иметь разное значение в зависимости от контекста использования, но эту проблему в принципе можно решить перефразированием.
Если использовать ключи (например, MSG_ABOUT_TEXT1), то придется создавать ресурсы для языка по умолчанию. При отсутствии ресурсов, или если что-то сломается, пользователю будут выводиться ключи, что не очень хорошо. 
Какой подход выбрать, зависит от сложности проекта и предпочтений. Фразы проще и проблем с ними в небольших проектах точно не будет. 
3. Необходимо извлечь строки из кода и создать файлы po. Делается это при помощи набора инструментов gettext. Если ранее этим заниматься не приходилось, то скорее всего придется выполнить установку необходимых компонентов.
Под Debian/Ubuntu установить пакет gettext можно следующей командой:sudo apt-get install gettext
Под остальными разновидностями Linux систем в общем-то примерно также, только диспетчер пакетов будет другим.
Под Windows мне не доводилось этим заниматься. Думаю, можно что-нибудь найти. Например, sourceforge.net/projects/gettext
Я опишу только порядок действий под Linux (конкретно Debian).
Для создания po-файла необходимо выполнить следующую команду:xgettext --extract-all --default-domain=example --from-code=utf-8 /путь к проекту/*.php
Параметры:--default-domain - имя домена, должно соответствовать домену, который используется в коде PHP (textdomain). Не путать с доменом сайта, хотя обычно используется название проекта;--from-code=имя кодировки текста. Например: utf-8;--extract-all - указывает на необходимость извлечения всех строк.
Дополнительно можно указать параметры:--output=имяФайла.po - по умолчанию строки будут извлечены в файл messages.po, этот параметр позволяет указать любое имя файла;--no-wrap - запрет разбития длинных строк;--join-existing - если есть предыдущие извлеченные строки, то эта опция указывает на необходимость склейки новых найденных строк с существующими (использовать, только если po-файл существует);--copyright-holder="Вася Пупкаидзе" - кому принадлежат авторские права.
Параметр --help позволяет посмотреть список всех параметров :-)
Программа создаст po-файл примерно следующего содержания:# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-01-15 14:55+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"

msgid "Hello world!"
msgstr ""

msgid "Test123"
msgstr ""msgid - это идентификатор сообщения.msgstr - перевод.
После указания перевода и сохранения изменений в файле po:msgid "Hello world!"
msgstr "Привет, мир!"

msgid "Test123"
msgstr "Тест123"
Его необходимо компилировать в файл mo.
Делается это при помощи команды msgfmt:msgfmt --output-file="/путь к файлу.mo" "/путь к файлу.po"
Для себя я написал следующий bash-скрипт, который компилирует все файлы po в каталоге (включая все подкаталоги), в котором был запущен скрипт:#!/bin/bash

lang="$1"
path="$(cd "$(dirname "$0")" && pwd)"

if [[ -n "$lang" ]]; then
  path="$path/$lang"
  if [[ ! -d "$path" ]]; then
    mkdir "$path"
  fi
fi

cd "$path"

find "$path" -name "*.po" | while read -r f; do
  po_dir="$(dirname $f)"
  po_file="$(basename $f)"
  po_name="$(echo $po_file | cut -d'.' -f1)"
  msgfmt --output-file="$po_dir/$po_name.mo" "$f" && \
  printf "Created: $po_dir/$po_name.mo\n" || \
  printf "ERROR: Could not create mo-file from: $f\n"
done
Если все пройдет без эксцессов, файлы mo следует разместить по подпапкам LC_MESSAGES нужных культур, которые должны располагаться в папке указанной в коде PHP функцией bindtextdomain.  Запутано вышло, проще показать :-)
Если:bindtextdomain('example', './local');
textdomain('example');
, то структуру размещения файлов mo:/local/ru/LC_MESSAGES/messages.mo
/local/en/LC_MESSAGES/messages.mo
/local/en_US/LC_MESSAGES/messages.mo
/local/ru_GB/LC_MESSAGES/messages.mo
/local/[Код культуры]/LC_MESSAGES/messages.mo/local - часть пути, указанная в коде PHP: bindtextdomain('example', './local');Код культуры - это стандартный (ISO 639) двузначный код языка, или код языка с указанием кода региона (ISO 639 + ISO 3166): ru (русский язык), ru_RU (русский язык, Россия), en (английский язык), en_US (американский английский) и т.п.
Честно говоря, я не вижу пока никаких особых преимуществ от использования gettext. Слишком сложный процесс генерирования всех этих po и mo. Проще и удобней текстовой файл с нужными ресурсами создать, или использовать базу. Хотя работу с gettext можно автоматизировать, но придется потратить на это время.

Худший ответ: не вижу смысла в этих .po\.mo, тем более для php. 
хранить в простом ассоциативном массиве - и проще (файл отредактировать в любом редакторе можно, без необходимости компиляции) и меньше ресурсов тратит.
вот например вордпресс MO::import_from_reader и не выкинуть, и процессорного времени жрёт.