Shop Project - Documentation

Label System & Localization (i18n)

Central infrastructure for all language-dependent user interface (UI) texts. The system strictly separates interface text from view code and loads them performantly at runtime via the app core.

🗺️ Stage 1 | 📚 Draft | 📟 2026 06 15 | 📍 Core System

Architecture & State Retention

The system differs from traditional, purely static helpers: It is data-driven, managed via the tables tb_language, tb_label_key, and tb_label. Loaded texts are centrally held for the duration of the request within the app core (app/core/app.php) inside the cApp instance.

Strict Separation: Interface vs. Trading Catalog

Content data such as category names, product names, or article texts do not belong in this label system. While this system is exclusively responsible for the UI structure (buttons, error messages, menus), catalog data resides in separate language tables (e.g., tb_category_lang).

Active System Components

  • label.model.php: Contains the central loading functions getLabel() and getLabels() for database access.
  • vw_label_export: The high-performance database view serving as the primary export source for bulk and multi-queries.
  • Central Storage: The cApp class stores page labels and navigation labels isolated from each other.
  • Template Access: Depending on the context, views either use the core call cApp::getInstance()->getLabel() or access a locally passed $labels array directly.

Loading Mechanism in App Core

public function setLabels(): void {
    $this->labels = getLabels(cSession::getLangID(), cSession::getPageID(), cSession::getCatKey(), true);
}

public function setNavLabels(): void {
    $this->navLabels = getLabels(cSession::getLangID(), null, 'nav');
}

Runtime Filter Logic & Query Core

The getLabels() function dynamically combines the selected language with optional filters for page IDs, functional groups, and global status, utilizing highly flexible SQL queries:

  • Page Assignment: Matching via comma-separated IDs utilizing FIND_IN_SET(:pagID, pag_IDs).
  • Group Tags: Filtering subsets via group identifiers using lky_group LIKE '% -tag-%'.
  • Global Status: Direct selection via the lky_is_global flag (e.g., for standardized buttons and error messages).

SQL Query Core (Principle)

SELECT DISTINCT lky_key, label_value
FROM vw_label_export
WHERE lan_ID = :lanID
  AND (...)
ORDER BY lky_key ASC

Prefix Conventions & Data Structure

lky_keyBeispiel-ZuweisungZweck / Typ
btn_saveSpeichern / SaveAktions-Buttons (Global)
nav_tradingHandel / TradingNavigationselemente (Nav-Gruppe)
msg_login_failLogin fehlgeschlagenSystem- / Validierungsmeldungen
lbl_usernameBenutzername / UsernameFormular-Beschriftungen (Global)

The strict use of descriptive prefixes prevents key collisions across different page contexts.

Error Handling & Scaling Strategy

Strict Error Display instead of Silent-Fallback

The methods getLabel() and getNavLabel() access entries strictly. If a key is completely missing in the system or chosen language, an error is logged. In the frontend, the error is intentionally made visible via the placeholder Label error: <key> to catch missing translations immediately during development.

Scaling via Filesystem Caching (Outlook)

For high-traffic scenarios, the architecture is prepared for two-stage caching. In addition to the active request cache within the app instance, automatic generation of native PHP cache files (e.g., /cache/labels_nav_de.php) can be prepended. This completely unburdens the SQL database as the service includes data via lightning-fast include statements until an administrative update triggers an automatic cache invalidation.