{"record":{"id":"83b303bdf2ead527","repo":"Leantime/leantime","slug":"plugin-s-is-not-installed","errorCode":null,"errorMessage":"Plugin %s is not installed","messagePattern":"Plugin (.+?) is not installed","errorType":"console","errorClass":"RuntimeException","httpStatus":null,"severity":"info","filePath":"app/Command/DisablePluginCommand.php","lineNumber":38,"sourceCode":"{\n    /**\n     * {@inheritdoc}\n     */\n    protected function configure(): void\n    {\n        $this->addArgument('plugin', InputArgument::REQUIRED, 'The plugin name');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    protected function executeCommand(): int\n    {\n        $name = $this->input->getArgument('plugin');\n        $plugin = $this->getPlugin($name);\n\n        if (! isset($plugin->id)) {\n            throw new RuntimeException(sprintf('Plugin %s is not installed', $plugin->name));\n        }\n\n        if (! $plugin->enabled) {\n            throw new RuntimeException(sprintf('Plugin %s is not enabled', $plugin->name));\n        }\n\n        if (! $this->confirm(sprintf('Disable plugin %s', $plugin->name))) {\n            return Command::SUCCESS;\n        }\n\n        return $this->plugins->disablePlugin($plugin->id) ? Command::SUCCESS : Command::FAILURE;\n    }\n}\n","sourceCodeStart":20,"sourceCodeEnd":52,"githubUrl":"https://github.com/Leantime/leantime/blob/9a9f49f1008f4782b30f6723c54228f4f992e636/app/Command/DisablePluginCommand.php#L20-L52","documentation":"This warning is emitted by the artisan command `translations:check-unused` (app/Command/CheckTranslations.php:134). The command parses app/Language/en-US.ini into a key list, then greps every .php/.blade.php/.tpl.php/.html/.js file under app/ (excluding vendor/node_modules/.git/storage/cache) for literal usages of each key. Any key whose literal string never appears is reported as unused and counted via sprintf('Found %d unused translations:', ...). It is a hygiene report, not a runtime failure — but it is prone to false positives because it only matches literal key text.","triggerScenarios":"Running `php bin/leantime translations:check-unused` when: (1) a translation key is referenced dynamically (key built by string concatenation or `__($type . '.title')`) so the literal never appears in source; (2) the key is only used inside app/Plugins (scanned separately), tests, or excluded directories; (3) a feature was removed and its language keys were never pruned from en-US.ini; (4) the key is used in a file type the Finder does not include (it chains multiple ->name() calls, which the Symfony Finder treats as an OR of patterns per-name, so some patterns may not all apply as expected).","commonSituations":"Post-refactor cleanup after the .tpl.php → Blade migration (old template keys orphaned); after canvas-variant consolidation into the Blueprints domain left legacy keys behind; after deleting legacy REST API controllers that had their own labels; before a release when trimming the ~thousand-key INI files; export via `--export=unused.json` for bulk review.","solutions":["Re-run with export to review the full list offline: `php bin/leantime translations:check-unused --export=storage/unused-translations.json`.","Manually verify each reported key is truly dynamic before deleting: `rg -n \"<key>\" app/ public/ --glob '!vendor'` including JS assets built into public/dist.","For dynamically-composed keys (e.g. `status_' . $status`), add the base prefix to an allowlist in your review process instead of deleting, or refactor the call sites to use a constant key so the scanner can see it.","Delete genuinely dead keys from app/Language/en-US.ini (and the mirrored keys in other language INI files) in a dedicated commit.","If keys are used only by a plugin, move them into the plugin's own language file rather than core en-US.ini."],"exampleFix":"// before — dynamic key, scanner reports 'todo.status_3' as unused\n$label = __('todo.status_' . $ticket['status']);\n\n// after — explicit key the scanner (and readers) can see\n$statusKeyMap = [1 => 'todo.status_open', 2 => 'todo.status_progress', 3 => 'todo.status_done'];\n$label = __($statusKeyMap[$ticket['status']] ?? 'todo.status_unknown');","handlingStrategy":"validation","validationCode":"// Before deleting a reported key, validate it is not used dynamically or in built assets\n$key = 'todo.status_3';\n$usedInCode = \\Illuminate\\Support\\Facades\\Process::run(\n    ['grep', '-rq', $key, app_path(), public_path('dist')]\n)->successful();\n$usedDynamically = \\Illuminate\\Support\\Facades\\Process::run(\n    ['grep', '-rq', \"'todo.status_'\", app_path()]\n)->successful(); // dynamic base prefix found -> keep the family of keys\nif (! $usedInCode && ! $usedDynamically) { /* safe to remove from en-US.ini */ }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pass --export and review the JSON report instead of parsing console output.","Grep public/dist too: keys referenced from compiled JS survive the scanner's app/-only scan.","Avoid building translation keys by concatenation; use explicit constant keys so static scans stay accurate.","Keep plugin-specific strings in the plugin's own language files so core 'unused' reports do not mislead."],"tags":["i18n","translations","console-command","static-analysis","dead-code"],"backgroundTag":"unused-translation-keys","analyzedSha":"9a9f49f1008f4782b30f6723c54228f4f992e636","analyzedAt":"2026-08-21T02:37:38.966Z","schemaVersion":2},"datasetVersion":"2026-08-21T03:17:12.404Z"}