feat(studio): Store + Cmd+K palette i18n (v0.27.0)
- Localize Store page: search hint, category and status filter
chips (All / Published / Alpha / Planned / Installed),
pluralized result count, hub-unreachable / no-matches empty
states, featured strip header, store-card pills (installed /
update / version), Install / Update / Details / Read docs /
Uninstall / Not installable buttons, install-progress dialog,
uninstall confirm dialog, install / uninstall toasts,
open-browser failure toast.
- Localize detail-sheet sections: Tags, Required capabilities,
Required host services, Screenshots, Documentation, Source.
- Localize docs panel: Load documentation button, fetching
spinner copy, Open repository in browser button, friendly
error mapping (not_found / no_docs / auth / network / parse).
- Localize Cmd+K palette: search hint, group labels (Pages /
Modules / Store / Flows), keyboard hint footer, indexing /
no-matches states, dynamic-hit copy ("installed module ·
v{version}", "saved flow", "uncategorized"). Static page
hits now flow through the localized "Pages" group; Settings
entry uses the localized label and hint.
Status pill values ("published", "alpha", "planned") stay as
data-derived English strings since they map directly to the
store-index `status:` enum.
Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
parent
d25b4c87ae
commit
e2b2639a86
9 changed files with 990 additions and 109 deletions
|
|
@ -11,6 +11,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/services.dart';
|
||||
|
||||
import '../data/hub.dart';
|
||||
import '../l10n/app_localizations.dart';
|
||||
import 'fai_module_sheet.dart';
|
||||
|
||||
/// One row in the palette. Carries everything needed to render
|
||||
|
|
@ -90,6 +91,7 @@ class _FaiSearchPaletteState extends State<FaiSearchPalette> {
|
|||
/// first character.
|
||||
Future<List<FaiSearchHit>> _loadDynamic() async {
|
||||
final svc = HubService.instance;
|
||||
final l = AppLocalizations.of(context)!;
|
||||
final results = await Future.wait([
|
||||
svc.listModules().catchError((_) => <ModuleSummary>[]),
|
||||
svc.searchStore(limit: 200).catchError((_) => <StoreItem>[]),
|
||||
|
|
@ -103,23 +105,26 @@ class _FaiSearchPaletteState extends State<FaiSearchPalette> {
|
|||
for (final m in modules) {
|
||||
hits.add(FaiSearchHit(
|
||||
label: m.name,
|
||||
hint: 'installed module · v${m.version}',
|
||||
hint: l.searchInstalledModuleHint(m.version),
|
||||
icon: Icons.extension,
|
||||
group: 'Modules',
|
||||
group: l.searchGroupModules,
|
||||
onSelect: () {
|
||||
Navigator.pop(context);
|
||||
FaiModuleSheet.show(context, m.name);
|
||||
},
|
||||
));
|
||||
}
|
||||
final storeGroup = l.searchGroupStore;
|
||||
final pagesGroup = l.searchGroupPages;
|
||||
for (final s in store) {
|
||||
hits.add(FaiSearchHit(
|
||||
label: s.name,
|
||||
hint: s.taglineEn.isEmpty
|
||||
? 'store · ${s.category.isEmpty ? "uncategorized" : s.category}'
|
||||
: 'store · ${s.taglineEn}',
|
||||
? l.searchStoreHintWithCategory(
|
||||
s.category.isEmpty ? l.searchStoreUncategorized : s.category)
|
||||
: l.searchStoreHintWithTagline(s.taglineEn),
|
||||
icon: Icons.storefront_outlined,
|
||||
group: 'Store',
|
||||
group: storeGroup,
|
||||
// Selecting a store hit just closes the palette and
|
||||
// focuses the Store tab; deep-linking to the detail
|
||||
// sheet would require thread-through plumbing we don't
|
||||
|
|
@ -127,7 +132,7 @@ class _FaiSearchPaletteState extends State<FaiSearchPalette> {
|
|||
onSelect: () {
|
||||
Navigator.pop(context);
|
||||
for (final h in widget.staticHits) {
|
||||
if (h.label == 'Store' && h.group == 'Pages') {
|
||||
if (h.label == storeGroup && h.group == pagesGroup) {
|
||||
h.onSelect();
|
||||
return;
|
||||
}
|
||||
|
|
@ -135,16 +140,17 @@ class _FaiSearchPaletteState extends State<FaiSearchPalette> {
|
|||
},
|
||||
));
|
||||
}
|
||||
final flowsGroup = l.searchGroupFlows;
|
||||
for (final f in flows) {
|
||||
hits.add(FaiSearchHit(
|
||||
label: f.name,
|
||||
hint: 'saved flow',
|
||||
hint: l.searchSavedFlowHint,
|
||||
icon: Icons.account_tree_outlined,
|
||||
group: 'Flows',
|
||||
group: flowsGroup,
|
||||
onSelect: () {
|
||||
Navigator.pop(context);
|
||||
for (final h in widget.staticHits) {
|
||||
if (h.label == 'Flows' && h.group == 'Pages') {
|
||||
if (h.label == flowsGroup && h.group == pagesGroup) {
|
||||
h.onSelect();
|
||||
return;
|
||||
}
|
||||
|
|
@ -201,6 +207,7 @@ class _FaiSearchPaletteState extends State<FaiSearchPalette> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final l = AppLocalizations.of(context)!;
|
||||
return Dialog(
|
||||
alignment: Alignment.topCenter,
|
||||
insetPadding: const EdgeInsets.only(top: 80, left: 24, right: 24),
|
||||
|
|
@ -235,8 +242,7 @@ class _FaiSearchPaletteState extends State<FaiSearchPalette> {
|
|||
decoration: InputDecoration(
|
||||
prefixIcon:
|
||||
const Icon(Icons.search, size: 20),
|
||||
hintText:
|
||||
'Jump anywhere — modules, store, flows, pages…',
|
||||
hintText: l.searchHint,
|
||||
border: InputBorder.none,
|
||||
suffixIcon: snap.connectionState ==
|
||||
ConnectionState.waiting
|
||||
|
|
@ -264,8 +270,8 @@ class _FaiSearchPaletteState extends State<FaiSearchPalette> {
|
|||
padding: const EdgeInsets.all(24),
|
||||
child: Text(
|
||||
snap.connectionState == ConnectionState.waiting
|
||||
? 'Indexing…'
|
||||
: 'No matches.',
|
||||
? l.searchIndexing
|
||||
: l.searchNoMatches,
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
|
|
@ -380,14 +386,14 @@ class _FaiSearchPaletteState extends State<FaiSearchPalette> {
|
|||
),
|
||||
child: Row(
|
||||
children: [
|
||||
_Hint(text: '↑↓ navigate'),
|
||||
_Hint(text: l.searchHintNavigate),
|
||||
const SizedBox(width: 12),
|
||||
_Hint(text: 'enter open'),
|
||||
_Hint(text: l.searchHintOpen),
|
||||
const SizedBox(width: 12),
|
||||
_Hint(text: 'esc close'),
|
||||
_Hint(text: l.searchHintClose),
|
||||
const Spacer(),
|
||||
Text(
|
||||
'${hits.length} result${hits.length == 1 ? "" : "s"}',
|
||||
l.storeNResults(hits.length),
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue