A humble PHP framework & CMS
Humblee includes a content personalization system that lets you serve different versions of any content block to different visitors. Internationalization — showing different content based on a URL language prefix — is built on top of that same system.
By default, every content block on a page has a single live version that every visitor sees. Personalization breaks that assumption. When p13n is enabled, any content block can have multiple versions — a default and any number of personalized variants. Humblee evaluates the current visitor against a set of rules you define and serves the matching variant automatically.
If no variant matches, the visitor sees the default content. No code changes are required in your view files — Draw::content() handles the version selection internally before output.
In your environment configuration file (humblee/configuration/env_*.php), set:
'use_p13n' => true,
With this set to false (the default), Humblee skips all personalization logic and always loads the default content version.
Each p13n variant in the CMS has a criteria definition stored as JSON. The rule structure uses two levels:
The currently supported condition types are:
| Type | Operators | What it tests |
|---|---|---|
required_role |
=, != |
Whether the current user has (or does not have) a given role |
i18n |
=, != |
Whether the current URL's language prefix matches a given value |
time_of_day |
<, > |
Whether the current server time is before or after a given time |
Variants are prioritized. When multiple variants match the current visitor, the one with the highest priority wins. Only active variants are evaluated.
Personalization works at the content block level, not the page level. A single page can have a mix of blocks — some with personalized variants, some without. The p13n_id field on a content row ties it to a specific variant; 0 means it is the default version.
When a page contains at least one personalized content block, Humblee automatically adds a Cache-Control: private header to the response, preventing shared (CDN or proxy) caches from serving a personalized page to the wrong visitor.
i18n is a special case of personalization that triggers on a URL prefix rather than on user attributes. You define a list of language or locale identifiers (country codes, locale strings, or any string you choose) in the config. When a visitor hits a URL that starts with one of those identifiers, the router strips the prefix from the slug lookup and instead uses the i18n identifier to select the matching personalized content variant for that locale.
In other words: /fr/about and /about resolve to the same page in the CMS — but if a French-language content variant exists and the i18n criteria matches fr, the visitor sees the French version.
First, use_p13n must be true. Then set i18n_segments to an array of the URL prefixes you want to support:
'use_p13n' => true,
'i18n_segments' => ['fr', 'es', 'de'],
With this configuration, all three of these URLs resolve to the same about page record:
/about → default content
/fr/about → French variant (if defined)
/es/about → Spanish variant (if defined)
/de/about → German variant (if defined)
If no variant is defined for the matched prefix, the visitor sees the default content.
Set i18n_segments to false (the default) to disable i18n URL handling entirely.
Core::getURIparts() is the method that parses the URI into segments for page lookup. When i18n_segments is configured, it checks whether the first URI segment appears in the allowed list. If it does, that segment is stripped from the array before the slug is matched against the database.
When the personalization engine evaluates criteria for the current request, the i18n condition type reads the first URI segment before stripping (via Core::getURIparts(true)) and compares it against the value in the rule. This is how the content system knows which locale variant to serve.
In the admin interface, create a new personalization entry. Define its criteria with a condition of type i18n, operator =, and the locale string as the value — for example, fr. Then, when editing content on any page, select that personalization variant when saving the French version of each block. Mark it live the same way you would default content.
| Feature | Config key | Default |
|---|---|---|
| Enable personalization | use_p13n |
false |
| Enable i18n URL routing | i18n_segments |
false |
The two features are independent in configuration but share the same underlying mechanism. i18n requires use_p13n to be true because locale-based content variants are just p13n variants with an i18n criterion.