Короткая заметка о том, как я полчаса разбирался с ошибкой, которая не имела
никакого отношения к правам на файлы, и куда вообще копать, если в Loco
Translate вдруг не сохраняются переводы.
Симптом
Loco Translate в админке WP перестал сохранять .po/.mo файлы плагина.
Сообщение:
File modification disallowed by the plugin settings
При этом chmod показывает 0664, владелец совпадает с пользователем
PHP-FPM, остальные плагины правятся нормально. То есть на уровне FS всё
выглядит рабочим.
Куда смотреть в первую очередь
Ошибка не про DISALLOW_FILE_MODS и не про WP wp_is_file_mod_allowed(). Это
сообщение внутренней проверки Loco, которая срабатывает в
FileWriter::authorize() (loco-translate/src/fs/FileWriter.php):
$opts = Loco_data_Settings::get();
// Запрет на запись в «опасные» системные папки
if (1 < $opts->fs_protect && $this->file->getUpdateType()) {
throw new Loco_error_WriteException( ... );
}
// Главное: путь должен попадать в список «разрешённых» директорий
$roots = Loco_fs_Locations::getBaseDirs();
if ($roots->count() && ! $roots->check($this->file->getPath())) {
throw new Loco_error_WriteException(
__('File modification disallowed by the plugin settings', 'loco-translate')
);
}
Список разрешённых директорий — это опция fs_basedir в wp_options.loco_settings.
В UI она называется Restricted writeable directories (раздел
File system access в настройках Loco). По дефолту там wp-content —
значение относительно ABSPATH, каждая директория с новой строки.
Что не так с дефолтом
После нормализации значение wp-content превращается в абсолютный путь
через Loco_fs_File::normalize(ABSPATH). В текущей версии (Loco 2.8.x) путь
для относительного сегмента wp-content резолвится так, что префикс не
совпадает с реальным расположением плагина — check() возвращает false,
хотя по логике должен бы быть true.
Грубо: это баг/особенность нормализации относительных путей в Loco 2.8.4.
Другие плагины (themes, wp-content/languages/plugins) у меня работали,
а wp-content/plugins/woo2iiko/languages/ — нет.
Решение
Самое простое: оставить поле «Restricted writeable directories» пустым.
Код выше использует короткое замыкание &&:
if ($roots->count() && ! $roots->check(...)) { ... }
Если fs_basedir пустой, getBaseDirs() вернёт пустую коллекцию,
count() === 0, условие целиком ложно, и проверка отключается. Loco в
таком случае полагается на fs_protect (защита «installed files») — этого
достаточно для большинства случаев.
Шаги:
- WP-админка → Loco Translate → Settings.
- Секция File system access → поле Restricted writeable directories.
- Очистить поле, сохранить.
После этого правки .po/.mo снова сохраняются. Остальные настройки
(fs_protect, pot_protect, fs_persist) остаются по дефолту.
Если нужен более строгий режим
По официальной документации Loco рекомендует хранить пользовательские
переводы вне wp-content/plugins/..., потому что WP-обновления могут
затереть файлы. Канонический путь — wp-content/languages/loco/<domain>/.
Тогда в Restricted writeable directories указывается:
wp-content/languages/loco
И при сохранении перевода в Loco нужно выбирать именно эту папку. Это
безопаснее, но требует переноса существующих .po файлов.
Итог
- Сообщение «File modification disallowed by the plugin settings» — это не
про FS-права, а про настройку Restricted writeable directories в Loco. - Если
wp-content(дефолт) не пускает вwp-content/plugins/<plugin>/languages/,
обнулите поле — проверка отключится по short-circuit. - Для продакшна лучше перенести переводы в
wp-content/languages/loco/.