fix(i18n): stop warning on missing tool.name.* keys for proper-name tools (T-493)

Tool display names are open-ended (Bash, Grep, Glob, ScheduleWakeup, MCP tools,
…) — they intentionally have no catalog key and fall back to the raw name, so a
miss isn't a gap to fix. The conversation pane was logging an i18n "missing key"
warning for each, cluttering the output dock.

Adds a `warnIfMissing` flag to I18nService.string (default true, so fixed UI
strings still warn on a real gap) threaded through the ClideSettings facade;
_toolNameLabel passes false. Display is unchanged — the placeholder already
rendered the raw name.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-28 14:58:12 +02:00
co-authored by Claude Opus 4.8
parent e5976ad6b1
commit 066197c5e1
7 changed files with 55 additions and 8 deletions
+30
View File
@@ -217,4 +217,34 @@ void main() {
expect(FallbackChain.filenameSuffix(const Locale('en')), 'en');
});
});
// T-493: open-ended lookups (tool display names like `tool.name.Bash`,
// `tool.name.ScheduleWakeup`, MCP tools) intentionally miss and fall back to
// the raw name, so they must not spam the warn log.
group('warnIfMissing (T-493)', () {
I18n withCapture(List<Object> warns) => I18n(
loader: InMemoryCatalogLoader(const {}),
log: Logger(minLevel: LogLevel.warn, sinks: [warns.add]),
defaultLocale: const Locale('en', 'US'),
);
test('a real gap warns by default, but warnIfMissing:false stays silent', () async {
final warns = <Object>[];
final i = withCapture(warns);
await i.ensureNamespaceLoaded('builtin.x');
expect(i.string('missing', namespace: 'builtin.x', placeholder: 'fb'), 'fb');
expect(warns, isNotEmpty, reason: 'a fixed UI string gap should still warn');
warns.clear();
expect(i.string('tool.name.Bash', namespace: 'builtin.x', placeholder: 'Bash', warnIfMissing: false), 'Bash');
expect(warns, isEmpty, reason: 'open-ended tool-name lookups stay quiet');
});
test('warnIfMissing:false also silences an unregistered namespace', () {
final warns = <Object>[];
expect(withCapture(warns).string('k', namespace: 'unregistered', placeholder: 'p', warnIfMissing: false), 'p');
expect(warns, isEmpty);
});
});
}