Перейти к содержимому
DedicatedPHP Контакт

Управление многоязычным контентом в PHP: модель и редакционный процесс

Спроектируйте многоязычный контент в PHP: свяжите переводы, задайте редакционные статусы для каждого языка и определите правила для URL, поиска и неполного контента.

Диаграмма PHP-приложения, связывающего редакционный материал с переводами и статусами публикации для каждого языка

Многоязычное приложение — это не просто переведённые подписи в интерфейсе. Если в нём есть статьи, карточки товаров или другие редакционные материалы, необходимо представлять, на каком языке доступен каждый материал, как связаны его версии и что означает готовность перевода к публикации. Неточная модель приводит к тому, что пользователи видят устаревшие тексты, ссылки без целевой страницы или черновики, которые им видеть не следует.

Планируя управление многоязычным контентом в PHP, полезно разделить решения, относящиеся к интерфейсу, данным и редакционному процессу. Для интерфейса можно использовать файлы переводов, а для изменяемого контента — базу данных. Однако такое разделение должно соответствовать предметной области и потребностям редакторов.

Разделите язык интерфейса, исходный язык и перевод

Разделите язык интерфейса, исходный язык и перевод — guía visual de DedicatedPHP

Язык интерфейса определяет подписи, сообщения валидации, форматы дат и другие тексты самого продукта. В PHP-приложении для этого можно использовать каталоги переводов, например файлы, сгруппированные по locale, а затем выбирать их на основе пользовательских предпочтений или настроек сессии. Язык интерфейса не следует путать с языком просматриваемого контента.

Исходный язык показывает, на каком языке создан редакционный материал. Каждый перевод — это связанная с ним версия на собственном языке. Пользователь может просматривать интерфейс на русском и открыть страницу, исходный контент которой написан на английском; приложение должно явно определять, допустима ли такая ситуация и как о ней сообщить.

Храните нормализованные коды языков и при необходимости придерживайтесь единой политики для региональных локалей. Не считайте язык и страну взаимозаменяемыми понятиями: региональный вариант может влиять на текст, а также на форматы, доступность или коммерческие требования. Определите, какие локали поддерживает продукт и какую из них использовать, если пользователь указал предпочтение не полностью.

Выберите модель данных, отражающую предметную область

Есть три распространённых подхода с разными компромиссами:

  • Поля для каждого языка: такие столбцы, как title_es и title_en, удобны, если языков мало, набор полей стабилен, а запросы просты. При добавлении языков гибкость снижается, а каждое изменение схемы начинает зависеть от списка языков.
  • Независимые записи контента: каждая версия хранится как самостоятельный материал. Такой подход может подойти, если версии действительно имеют независимые жизненные циклы или структуру, но для их группировки потребуется другой надёжный механизм. Не используйте похожие заголовки или URL в качестве неявной связи.
  • Связанная таблица переводов: общий материал связывается со строкой для каждого языка, например через content_id и locale. Так проще добавлять языки и запрашивать доступные версии. При этом нужны ограничения, предотвращающие появление нескольких переводов одного материала на одном языке.

Связанная таблица обычно подходит, когда версии имеют общую идентичность и структуру, но это не универсальное правило. Если некоторые поля не переводятся — например, внутренняя ссылка на сущность, — их можно оставить в общей сущности, а локализованные редакционные поля хранить в переводе. Также определите, может ли у перевода быть собственный URL, поисковые метаданные или дата публикации.

В базе данных задайте внешние ключи, уникальность по материалу и языку, а также поведение при удалении данных. PHP-приложение должно дополнительно проверять, что язык поддерживается, а связанный материал существует. Ограничения базы данных защищают от конкурентной записи и ошибок, которые невозможно предотвратить одной лишь предварительной проверкой.

Связывайте версии, не требуя наличия всех переводов

Не каждый материал будет доступен на всех языках, и переводы не обязательно будут завершаться одновременно. Отразите это в модели: отсутствие строки не следует считать нарушением целостности, если перевод необязателен. Если же строка есть, но материал ещё нельзя публиковать, это должен показывать редакционный статус.

Не дублируйте в каждом переводе информацию, относящуюся к общему материалу, если предметная область не требует вариантов. Если переводы можно отвязывать, переносить или сохранять в архиве, определите соответствующие операции и права доступа. Для определения группы версий используйте устойчивую связь, а не совпадения текста, slug или даты.

Если команде нужно выявлять потенциально устаревший контент, при изменении перевода записывайте, на какой версии оригинала он основан. Этот признак сам по себе не доказывает, что перевод неправильный, но позволяет расставить приоритеты при проверке. Для одних продуктов достаточно пометки о необходимости проверки, другим потребуются история версий и аудит.

Задайте редакционные статусы для каждого языка

Статус публикации должен относиться к каждому переводу, если для каждого языка можно независимо подготовить, проверить и опубликовать материал. Материал может быть опубликован на одном языке и оставаться черновиком на другом. Один булевый флаг публикации на уровне материала не отражает эту разницу.

Спроектируйте небольшой и понятный процесс, например: черновик, на проверке и опубликован. Определите, кто может менять каждый статус, какие поля обязательны и требуется ли одобрение по итогам проверки. Для работы процесса могут понадобиться дата публикации, ответственное лицо и история изменений; не добавляйте статусы, за которыми не стоят реальные действия команды.

Публичные запросы должны фильтровать данные по статусу и языку, а не полагаться на то, что редакционный интерфейс скрывает неопубликованные строки. В PHP сосредоточьте эти правила в слое доступа к предметной области или в повторно используемых запросах и проверьте, что их соблюдают контроллеры, API и фоновые задачи. При редактировании и публикации также необходимо проверять права на конкретные язык и материал.

Согласованно обрабатывайте отсутствующие переводы, URL и поиск

Если перевод отсутствует, возможны разные варианты поведения. Приложение может скрыть материал на этом языке, сообщить, что он доступен только на другом языке, или показать fallback. Выберите подходящий вариант с учётом типа контента и влияния на пользовательский опыт: fallback нельзя выдавать за перевод. Если показывается контент на другом языке, явно укажите это и оставьте возможность перейти к запрошенной версии.

Для интерфейса и контента установите разные правила. Наличие fallback для подписей навигации из другого каталога не означает, что статьи нужно автоматически заменять оригинальной версией. Редакционный fallback должен быть ограничен разрешёнными языками и содержанием, которое можно публиковать; он не должен возвращать черновики.

URL должны указывать на существующую версию или следовать документированному правилу перенаправления. При переключении языка ищите перевод того же материала; если его нет, предложите понятный выход, а не создавайте путь, который выглядит действительным. Для SEO избегайте публикации пустых или дублирующихся страниц из-за незаметного fallback. В поиск следует индексировать только подходящие версии и использовать соответствующий язык для фильтрации и показа результатов.

Контрольный список перед публикацией

Контрольный список перед публикацией — guía visual de DedicatedPHP
  • Разделены ли в модели понятия языка интерфейса, исходного языка и языка каждого перевода?
  • Запрещено ли сохранять два перевода одного материала на одном языке?
  • Может ли перевод отсутствовать, не создавая противоречивого состояния?
  • Может ли команда различать статусы «черновик», «на проверке» и «опубликован» для каждого языка?
  • Проверяют ли публичные маршруты и ссылки переключения языка наличие видимой версии?
  • Определён ли fallback для каждого сценария, сообщается ли о нём пользователю и исключена ли возможность показа черновиков?
  • Фильтрует ли поиск результаты по языку и статусу и прекращает ли индексировать снятую с публикации версию?
  • Проверены ли права доступа, одновременное редактирование, удаление, неполный контент и переключение языка?

Также проверьте снятие с публикации перевода, на который уже ведут ссылки, обновление оригинала после публикации версии и прямой запрос недоступного URL. В каждом случае проверьте ответ, навигацию и видимость в поиске. Цель не в том, чтобы все языки развивались в одном темпе, а в том, чтобы у каждой версии были собственная идентичность, проверяемый статус и предсказуемое поведение для редакторов и пользователей.

Хотите применить эти идеи в своем проекте?Давайте обсудим вашу PHP-платформу.
Посмотреть связанные услуги