therealworld / langfallback-module
Language fallback for multilingual database views in OXID7. Shows fallback language content when translations are empty.
Package info
bitbucket.org/therealworld/langfallback-module/
Type:oxideshop-module
pkg:composer/therealworld/langfallback-module
Requires
- php: ^8.3
- oxid-esales/oxideshop-ce: >=v7.4
- therealworld/tools-plugin: >=v3.2
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Deutsche Fassung: README.de.md

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-OXTITLEcomes fromoxarticles.OXTITLEoxv_oxarticles_en-OXTITLEcomes fromoxarticles.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_1has content: the English text is shownOXTITLE_1is empty ('') orNULL: 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) | Default | Effect |
|---|---|---|
| Enable language fallback | on | Master switch. Off: the next view update creates the standard OXID views again. |
| Fallback language ID (default: 0 = first language) | 0 | ID 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, oxvendor | Tables 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
blShowUpdateViewsis enabled inconfig.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:
| Table | Content |
|---|---|
oxarticles | article titles, short descriptions, search keys, variant names, stock texts |
oxartextends | long descriptions, meta tags |
oxcategories | category titles, descriptions, SEO data |
oxattribute | attribute titles |
oxmanufacturers | manufacturer/publisher titles and descriptions |
oxvendor | vendor titles |
oxcontents | CMS 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.
| Edition | Views with fallback |
|---|---|
| CE | oxv_{table}_{lang} |
| PE / EE | oxv_{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
NULLonly. 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 necessaryLEFT 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
COALESCEin 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 overridesgenerateViews(), calls the parent first, then runsCREATE OR REPLACE SQL SECURITY INVOKER VIEWfor each configured table and non-fallback language. Field lists come fromDbMetaDataHandler::getSinglelangFields()/getMultilangFields(), the_settable names fromgetLangTableName(), the column types (text or not) fromToolsDB::showTableColumn()for the base table. - Settings access:
TheRealWorld\LangFallbackModule\Core\LangFallbackConfig(isEnabled(),getFallbackLangId(),getFallbackTables()). - Events:
Core\LangFallbackEvents::onActivate()/onDeactivate()both callDbMetaDataHandler::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