Every module needs a language string named module__<app>_<module>. Leave it out and Invision prints the name of the missing string in place of the module's name — with no error, nothing in the logs, and nothing on the application's own screens to hint at it.
The symptom
Members > Staff > Administrators > Edit Restrictions lists your application's sections as raw keys:
module__myapp_backup module__myapp_prompt
The restrictions themselves work correctly. Ticking and saving behaves exactly as it should — the only thing wrong is the label. On a site with a dozen applications installed, an administrator cannot even tell which application an unnamed row belongs to.
The cause
A module is a node, and its title comes from Module::get__title() in system/Application/Module.php:
protected function get__title(): string
{
$key = "module__{$this->application}_{$this->key}";
return Member::loggedIn()->language()->addToStack( $key );
}
The key is built from the application directory and the module directory — not from anything you declare. A module at applications/myapp/modules/admin/backup/ needs module__myapp_backup, whether or not you ever refer to that string yourself.
Why it is silent
This is the part worth remembering, because it generalises. addToStack() and get() fail in opposite ways:
$lang->addToStack( 'missing_key' ); // returns the string "missing_key" $lang->get( 'missing_key' ); // throws UnderflowException
addToStack() is the safe one, so a missing key degrades to printing itself rather than breaking the page. That is usually what you want — but it also means nothing anywhere reports the fault. There is no exception, no log entry and no failed request. The only way to find it is to look at the screen.
The reverse case is worth knowing too: a core/Notifications extension missing its string is read with get(), and takes the entire ACP Notification Settings screen down for every application on the site. Same class of mistake, opposite volume.
Finding them
Every module in data/modules.json needs a matching key in dev/lang.php. Both areas count — admin and front:
'module__myapp_backup' => "Backup & Restore", 'module__myapp_prompt' => "Prompts",
To check a built application without installing it:
tar -xOf myapp-1.0.1.tar data/lang.xml | grep module__
Compare that against the module names in data/modules.json. Anything present there and absent from the language file will appear as a raw key.
If you are fixing it in a released application
Adding the string is the easy half. Getting it to people who already installed the application is the half that catches you out: correcting the file and re-uploading it under the same version number reaches nobody. Invision compares versions on upload and silently ignores one that is not newer, so the fix serves new downloads while every existing installation stays exactly as it was.
The fix has to ship as a version bump. Language strings need no upgrade step of their own — on upgrade Invision calls installLanguages(), which imports data/lang.xml in full, so a bumped version carries new keys automatically. The per-version setup/upg_<version>/lang.json is consulted only for strings you have removed.
Recommended Comments