refactor(i18n): route planning-pane labels through the catalog (T-467)

Migrate the user-facing strings in builtin.tickets, builtin.pql,
builtin.decisions, and builtin.problems (list/detail views, section headers,
filters, empty/loading/error states, type chips) to ClideSettings.i18n; extend
each extension's catalog. The detail/links tabs that had a bare title now wire
titleKey + i18nNamespace so the tab title resolves too. Context threaded into
the static label helpers. No en_US behaviour change (D-21).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-18 22:14:01 +02:00
co-authored by Claude Opus 4.8
parent f33cd603be
commit 17d22b9571
16 changed files with 337 additions and 64 deletions
+50 -9
View File
@@ -42,10 +42,22 @@ class _BacklinksViewState extends State<BacklinksView> {
builder: (context, _) {
final tokens = ClideSettings.theme.of(context).surface;
if (c.activePath == null) {
return const Padding(padding: EdgeInsets.all(12), child: ClideText('Open a file to see its links.', muted: true));
return Padding(
padding: const EdgeInsets.all(12),
child: ClideText(
ClideSettings.i18n.string(context, 'backlinks.empty', namespace: 'builtin.pql', placeholder: 'Open a file to see its links.'),
muted: true,
),
);
}
return Semantics(
label: 'backlinks for ${c.activePath}',
label: ClideSettings.i18n.interpolated(
context,
'backlinks.semantics',
namespace: 'builtin.pql',
placeholder: 'backlinks for {path}',
replacers: [I18nReplacer(from: '{path}', replace: c.activePath!)],
),
container: true,
explicitChildNodes: true,
child: SingleChildScrollView(
@@ -63,9 +75,21 @@ class _BacklinksViewState extends State<BacklinksView> {
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
child: ClideText(c.error!, color: tokens.statusError, fontSize: clideFontCaption),
),
if (c.loading) const Padding(padding: EdgeInsets.all(12), child: ClideText('Loading…', muted: true)),
_LinkGroup(label: 'Backlinks', links: c.backlinks, pathKey: 'source'),
_LinkGroup(label: 'Outlinks', links: c.outlinks, pathKey: 'target'),
if (c.loading)
Padding(
padding: const EdgeInsets.all(12),
child: ClideText(ClideSettings.i18n.string(context, 'backlinks.loading', namespace: 'builtin.pql', placeholder: 'Loading…'), muted: true),
),
_LinkGroup(
label: ClideSettings.i18n.string(context, 'group.backlinks', namespace: 'builtin.pql', placeholder: 'Backlinks'),
links: c.backlinks,
pathKey: 'source',
),
_LinkGroup(
label: ClideSettings.i18n.string(context, 'group.outlinks', namespace: 'builtin.pql', placeholder: 'Outlinks'),
links: c.outlinks,
pathKey: 'target',
),
],
),
),
@@ -90,12 +114,29 @@ class _LinkGroup extends StatelessWidget {
children: [
Padding(
padding: const EdgeInsets.only(left: 12, right: 8, top: 8, bottom: 2),
child: ClideText('$label (${links.length})', fontSize: clideFontCaption, muted: true),
child: ClideText(
ClideSettings.i18n.interpolated(
context,
'group.label',
namespace: 'builtin.pql',
placeholder: '{label} ({count})',
replacers: [
I18nReplacer(from: '{label}', replace: label),
I18nReplacer(from: '{count}', replace: '${links.length}'),
],
),
fontSize: clideFontCaption,
muted: true,
),
),
if (links.isEmpty)
const Padding(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 2),
child: ClideText('None', fontSize: clideFontCaption, muted: true),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 2),
child: ClideText(
ClideSettings.i18n.string(context, 'group.none', namespace: 'builtin.pql', placeholder: 'None'),
fontSize: clideFontCaption,
muted: true,
),
),
for (final link in links) _LinkRow(link: link, pathKey: pathKey),
],
+2
View File
@@ -21,6 +21,8 @@ class PqlExtension extends ClideExtension {
id: 'pql.backlinks',
slot: Slots.contextPanel,
title: 'Links',
titleKey: 'tab.links.title',
i18nNamespace: id,
icon: PhosphorIcons.byName('link'),
priority: -80,
build: (_) => const BacklinksView(),
+16 -4
View File
@@ -104,7 +104,7 @@ class _PqlSearchBodyState extends State<PqlSearchBody> {
if (widget.mode == PqlPaneMode.markdown)
ClideFilterBox(
address: 'search.pql.markdown',
hint: 'Filter markdown…',
hint: ClideSettings.i18n.string(context, 'filter.markdown.hint', namespace: 'builtin.pql', placeholder: 'Filter markdown…'),
onChanged: (v) => unawaited(c.loadMarkdownFiles(glob: v.isEmpty ? null : '**/*$v*.md')),
)
else
@@ -118,9 +118,19 @@ class _PqlSearchBodyState extends State<PqlSearchBody> {
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
child: ClideText(c.error!, color: tokens.statusError, fontSize: clideFontCaption, maxLines: 3),
),
if (c.loading && c.results.isEmpty) const Padding(padding: EdgeInsets.all(12), child: ClideText('Loading…', muted: true)),
if (c.loading && c.results.isEmpty)
Padding(
padding: const EdgeInsets.all(12),
child: ClideText(ClideSettings.i18n.string(context, 'loading', namespace: 'builtin.pql', placeholder: 'Loading…'), muted: true),
),
if (!c.loading && c.results.isEmpty && c.error == null && widget.mode == PqlPaneMode.markdown)
const Padding(padding: EdgeInsets.all(12), child: ClideText('No markdown files found.', muted: true)),
Padding(
padding: const EdgeInsets.all(12),
child: ClideText(
ClideSettings.i18n.string(context, 'empty.markdown', namespace: 'builtin.pql', placeholder: 'No markdown files found.'),
muted: true,
),
),
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(vertical: 4),
@@ -160,7 +170,9 @@ class _PqlSearchInput extends StatelessWidget {
Widget build(BuildContext context) {
return ClideFilterBox(
address: address,
hint: dsl ? 'PQL query…' : 'Search vault…',
hint: dsl
? ClideSettings.i18n.string(context, 'search.query.hint', namespace: 'builtin.pql', placeholder: 'PQL query…')
: ClideSettings.i18n.string(context, 'search.vault.hint', namespace: 'builtin.pql', placeholder: 'Search vault…'),
debounce: dsl ? Duration.zero : const Duration(milliseconds: 300),
onChanged: dsl ? (_) {} : (v) => unawaited(controller.search(v)),
onSubmitted: dsl ? (v) => unawaited(controller.runQuery(v)) : (v) => unawaited(controller.search(v)),