Search by

therealworld / langfallback-module

therealworld

Language fallback for multilingual database views in OXID7. Shows fallback language content when translations are empty.

Package info

bitbucket.org/therealworld/langfallback-module/

Homepage

Type:oxideshop-module

pkg:composer/therealworld/langfallback-module

Statistics

Installs: 38

Dependents: 0

Suggesters: 0

v1.0.8 2026-10-25 14:15 UTC

This package is auto-updated.

Last update: 2026-10-25 14:17:28 UTC


README

Deutsche Fassung: README.de.md

vendor-logo the-real-world.de

description

In a multilingual OXID shop every translatable field (article title, description, category name, CMS text ...) has to be maintained in every language. A field that has not been translated yet shows up empty in the frontend.

This module closes those gaps: wherever a translation is missing, the shop shows the text of a fallback language (usually the main language, e.g. German) instead. Content can therefore be translated step by step - everything not translated yet keeps showing the fallback text until the translation exists.

The fallback is built into the database views OXID generates per language, so it costs practically nothing at runtime and works for every place that reads through the views (frontend pages, lists, search).

  • composer package: therealworld/langfallback-module
  • OXID module id: trwlangfallback

how it works

OXID generates one view per table and language, e.g.:

  • oxv_oxarticles_de - OXTITLE comes from oxarticles.OXTITLE
  • oxv_oxarticles_en - OXTITLE comes from oxarticles.OXTITLE_1

If OXTITLE_1 is empty, the English shop shows nothing. After OXID has generated its standard views, the module recreates the views of all non-fallback languages for the configured tables and wraps every multilingual field:

-- standard OXID view
SELECT oxarticles.OXTITLE_1 AS OXTITLE FROM oxarticles

-- with the module
SELECT COALESCE(NULLIF(oxarticles.OXTITLE_1, ''), oxarticles.OXTITLE) AS OXTITLE FROM oxarticles
  • OXTITLE_1 has content: the English text is shown
  • OXTITLE_1 is empty ('') or NULL: the fallback text (OXTITLE) is shown

The empty-string comparison applies to text columns only (CHAR, VARCHAR, *TEXT). All other multilingual columns - above all flags like OXACTIVE_1 (TINYINT) - fall back on NULL only, so a value of 0 is kept:

SELECT COALESCE(oxcategories.OXACTIVE_1, oxcategories.OXACTIVE) AS OXACTIVE FROM oxcategories

A category or CMS page deactivated in only one language therefore stays deactivated there. The module reads the column type from the table description (SHOW COLUMNS) during the view update.

The views of the fallback language itself, and the "all languages" views without language suffix (e.g. oxv_oxarticles), stay untouched. Fields that are not multilingual are taken over unchanged.

requirements

  • OXID eShop CE >= 7.4 (PE/EE supported, see below)
  • PHP ^8.3
  • therealworld/tools-plugin >= 3.2 (installed automatically by composer)

installation

composer require therealworld/langfallback-module
vendor/bin/oe-console oe:module:activate trwlangfallback

With therealworld/clirun-plugin installed, vendor/bin/oe-console trw:module:setup trwlangfallback does install, activation and view update in one step.

Activating the module regenerates all database views with the fallback applied; deactivating it regenerates them again and thereby restores the standard OXID views.

After updating the module, regenerate the views once (see "configuration") so changed view definitions take effect - e.g. after updating to the version that no longer lets numeric columns fall back on 0.

Check the result:

SHOW CREATE VIEW oxv_oxarticles_en\G

The multilingual text fields have to appear as COALESCE(NULLIF(...), ...), the other multilingual fields as COALESCE(...) without NULLIF.

configuration

Admin: Extensions → Modules → the-real-world - Language Fallback → Settings, group Language Fallback Settings.

Setting (as shown in the admin)DefaultEffect
Enable language fallbackonMaster switch. Off: the next view update creates the standard OXID views again.
Fallback language ID (default: 0 = first language)0ID of the language whose text fills the gaps. 0 is the shop's first language; set 1 if the main content is maintained in the second language.
Tables with fallback (one per line)oxarticles, oxcategories, oxartextends, oxattribute, oxmanufacturers, oxcontents, oxvendorTables whose language views get the fallback.

Important: changing a setting does not rebuild the views by itself. Afterwards update the views once, e.g.:

  • admin: Service → Tools → Update DB Views now (shown when blShowUpdateViews is enabled in config.inc.php)
  • CLI: vendor/bin/oe-eshop-db_views_generate
  • CLI with therealworld/clirun-plugin: vendor/bin/oe-console trw:update:views

The default tables cover:

TableContent
oxarticlesarticle titles, short descriptions, search keys, variant names, stock texts
oxartextendslong descriptions, meta tags
oxcategoriescategory titles, descriptions, SEO data
oxattributeattribute titles
oxmanufacturersmanufacturer/publisher titles and descriptions
oxvendorvendor titles
oxcontentsCMS pages, terms, mail texts

Own multilingual tables (e.g. a news table mynews of another module) can simply be added as an extra line. A table has to be registered as multilingual in OXID (it must appear in Registry::getLang()->getMultiLangTables(), i.e. the core list or aMultiLangTables); other tables are skipped silently.

Enterprise / Professional Edition

On PE/EE (detected via ToolsConfig::isMultiShop()) OXID additionally generates shop-specific views oxv_{table}_{shopId}_{lang} with an INNER JOIN {table}2shop and a WHERE oxshopid = ... filter. The module recreates those with the same fallback and keeps the shop filter. oxobject2category, which carries oxshopid itself instead of using a 2shop mapping table, is handled separately.

EditionViews with fallback
CEoxv_{table}_{lang}
PE / EEoxv_{table}_{lang} and oxv_{table}_{shopId}_{lang}

logging

If a view cannot be created, the error goes to the OXID log (Registry::getLogger()) with the prefix LangFallback: View generation failed for <view> and the view update reports failure.

limits and known caveats

  • In text fields an empty string counts as "not translated". A text field cannot be deliberately left blank in a single language while the fallback language has content.
  • Non-text fields (numbers, flags, dates) fall back on NULL only. As OXID creates the language columns with default values, they practically never take over the fallback language's value.
  • The fallback text appears in the translated shop as-is - including SEO data of the configured tables (e.g. category meta texts).
  • Languages 8 and above store their fields in the {table}_set1, _set2 ... tables; the module adds the necessary LEFT JOINs for the target and the fallback language. Shops with up to 8 languages need no extra join.
  • Views are only rebuilt when OXID regenerates them (module activation/deactivation, admin action, CLI) - never per request.

troubleshooting

  • No COALESCE in the views after activation or a settings change: update the views (see above).
  • No fallback for one particular table: check that it is listed in "Tables with fallback", that OXID knows it as multilingual, and look for LangFallback: entries in the OXID log.
  • Still empty: the fallback language itself has no content in that field.

For developers

  • Chain extension: OxidEsales\Eshop\Application\Model\Shop → TheRealWorld\LangFallbackModule\Application\Model\Shop. It overrides generateViews(), calls the parent first, then runs CREATE OR REPLACE SQL SECURITY INVOKER VIEW for each configured table and non-fallback language. Field lists come from DbMetaDataHandler::getSinglelangFields() / getMultilangFields(), the _set table names from getLangTableName(), the column types (text or not) from ToolsDB::showTableColumn() for the base table.
  • Settings access: TheRealWorld\LangFallbackModule\Core\LangFallbackConfig (isEnabled(), getFallbackLangId(), getFallbackTables()).
  • Events: Core\LangFallbackEvents::onActivate() / onDeactivate() both call DbMetaDataHandler::updateViews().
  • No own DB tables, migrations, templates, controllers or services. Admin labels: views/admin_twig/{de,en}/module_options.php.

license

GPL-2.0-only / proprietary - Mario Lorenz, the-real-world.de