Add Hungarian English language switcher

This commit is contained in:
zsolt
2026-08-13 13:43:57 +02:00
parent 1b0734cf7b
commit aea92b0eed
8 changed files with 262 additions and 35 deletions
+138 -16
View File
@@ -18,6 +18,7 @@ class _OtthonAppState extends State<OtthonApp> {
String googleServerClientId = googleCalendarServerClientId;
WeatherSnapshot? weather;
String themeId = 'sage';
String languageCode = defaultLanguageCode;
bool loaded = false;
AppThemeStyle get activeTheme => appThemes.firstWhere(
@@ -43,6 +44,7 @@ class _OtthonAppState extends State<OtthonApp> {
jarvisSettings = settings.jarvisSettings;
googleServerClientId = settings.googleServerClientId;
themeId = settings.themeId;
languageCode = settings.languageCode;
loaded = true;
});
_loadRemoteAppConfig();
@@ -53,6 +55,7 @@ class _OtthonAppState extends State<OtthonApp> {
AppSettings(
people: people,
themeId: themeId,
languageCode: languageCode,
googleConnections: googleConnections,
syncedEvents: syncedEvents,
todoSuggestions: todoSuggestions,
@@ -100,6 +103,11 @@ class _OtthonAppState extends State<OtthonApp> {
_saveSettings();
}
void _updateLanguage(String nextLanguageCode) {
setState(() => languageCode = nextLanguageCode == 'en' ? 'en' : 'hu');
_saveSettings();
}
void _updateGoogleConnections(
List<GoogleCalendarConnection> nextConnections,
) {
@@ -132,9 +140,17 @@ class _OtthonAppState extends State<OtthonApp> {
@override
Widget build(BuildContext context) {
final theme = activeTheme;
final text = AppText.of(languageCode);
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'K\u00f6z\u00f6sT\u00e9r',
title: text.appTitle,
locale: text.locale,
supportedLocales: const [Locale('hu'), Locale('en')],
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
@@ -168,9 +184,11 @@ class _OtthonAppState extends State<OtthonApp> {
googleServerClientId: googleServerClientId,
weather: weather,
themeId: themeId,
languageCode: languageCode,
loaded: loaded,
onPeopleChanged: _updatePeople,
onThemeChanged: _updateTheme,
onLanguageChanged: _updateLanguage,
onGoogleConnectionsChanged: _updateGoogleConnections,
onSyncedEventsChanged: _updateSyncedEvents,
onTodoSuggestionsChanged: _updateTodoSuggestions,
@@ -191,9 +209,11 @@ class HomeShell extends StatefulWidget {
required this.googleServerClientId,
required this.weather,
required this.themeId,
required this.languageCode,
required this.loaded,
required this.onPeopleChanged,
required this.onThemeChanged,
required this.onLanguageChanged,
required this.onGoogleConnectionsChanged,
required this.onSyncedEventsChanged,
required this.onTodoSuggestionsChanged,
@@ -208,9 +228,11 @@ class HomeShell extends StatefulWidget {
final String googleServerClientId;
final WeatherSnapshot? weather;
final String themeId;
final String languageCode;
final bool loaded;
final ValueChanged<List<Person>> onPeopleChanged;
final ValueChanged<String> onThemeChanged;
final ValueChanged<String> onLanguageChanged;
final ValueChanged<List<GoogleCalendarConnection>> onGoogleConnectionsChanged;
final ValueChanged<List<CalendarEvent>> onSyncedEventsChanged;
final ValueChanged<List<String>> onTodoSuggestionsChanged;
@@ -328,15 +350,6 @@ class _HomeShellState extends State<HomeShell> {
List<CalendarEvent> get allEvents => [...events, ...widget.syncedEvents];
final labels = const [
'Napt\u00e1r',
'Bev\u00e1s\u00e1rl\u00e1s',
'K\u00e9szlet',
'Csal\u00e1dtagok',
'St\u00edlus',
'Fi\u00f3kok',
];
final icons = const [
Icons.calendar_month_rounded,
Icons.shopping_basket_outlined,
@@ -349,6 +362,15 @@ class _HomeShellState extends State<HomeShell> {
@override
Widget build(BuildContext context) {
final wide = MediaQuery.sizeOf(context).width >= 850;
final text = AppText.of(widget.languageCode);
final labels = [
text.calendar,
text.shopping,
text.stock,
text.familyMembers,
text.style,
text.accounts,
];
final body = IndexedStack(
index: selected,
children: [
@@ -362,11 +384,13 @@ class _HomeShellState extends State<HomeShell> {
),
ShoppingPage(
items: shopping,
languageCode: widget.languageCode,
onChanged: () => setState(() {}),
onAdd: _addShopping,
),
StockPage(
items: stock,
languageCode: widget.languageCode,
onChanged: () => setState(() {}),
onAdd: _addStock,
onEdit: _editStock,
@@ -375,12 +399,14 @@ class _HomeShellState extends State<HomeShell> {
PersonsPage(
people: widget.people,
events: allEvents,
languageCode: widget.languageCode,
onAdd: _addPerson,
onEdit: _editPerson,
onDelete: _deletePerson,
),
StylePage(
themeId: widget.themeId,
languageCode: widget.languageCode,
onThemeChanged: widget.onThemeChanged,
),
AccountsPage(
@@ -389,6 +415,7 @@ class _HomeShellState extends State<HomeShell> {
people: widget.people,
jarvisSettings: widget.jarvisSettings,
serverClientId: widget.googleServerClientId,
languageCode: widget.languageCode,
onChanged: widget.onGoogleConnectionsChanged,
onSyncedEventsChanged: widget.onSyncedEventsChanged,
),
@@ -451,11 +478,11 @@ class _HomeShellState extends State<HomeShell> {
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'K\u00f6z\u00f6sT\u00e9r',
Text(
text.appTitle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w900,
color: ink,
@@ -464,8 +491,8 @@ class _HomeShellState extends State<HomeShell> {
),
Text(
widget.loaded
? 'Tervez\u0151'
: 'JSON bet\u00f6lt\u00e9se\u2026',
? text.appSubtitle
: text.loadingJson,
style: const TextStyle(
fontSize: 11,
color: Colors.black45,
@@ -473,6 +500,12 @@ class _HomeShellState extends State<HomeShell> {
),
),
const SizedBox(height: 2),
LanguageSwitcher(
languageCode: widget.languageCode,
onChanged: widget.onLanguageChanged,
labels: text,
),
const SizedBox(height: 2),
const Text(
appVersionLabel,
style: TextStyle(
@@ -485,7 +518,7 @@ class _HomeShellState extends State<HomeShell> {
),
),
IconButton(
tooltip: 'JARVIS be\u00e1ll\u00edt\u00e1sok',
tooltip: text.jarvisSettings,
onPressed: _openJarvisSettings,
icon: const Icon(
Icons.settings_outlined,
@@ -1215,3 +1248,92 @@ class _HomeShellState extends State<HomeShell> {
}
}
}
class LanguageSwitcher extends StatelessWidget {
const LanguageSwitcher({
super.key,
required this.languageCode,
required this.onChanged,
required this.labels,
});
final String languageCode;
final ValueChanged<String> onChanged;
final AppText labels;
@override
Widget build(BuildContext context) {
return Container(
height: 24,
padding: const EdgeInsets.all(2),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(999),
border: Border.all(color: sage.withValues(alpha: .16)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
_LanguagePill(
selected: languageCode != 'en',
flag: '🇭🇺',
label: labels.hunShort,
onTap: () => onChanged('hu'),
),
_LanguagePill(
selected: languageCode == 'en',
flag: '🇬🇧',
label: labels.engShort,
onTap: () => onChanged('en'),
),
],
),
);
}
}
class _LanguagePill extends StatelessWidget {
const _LanguagePill({
required this.selected,
required this.flag,
required this.label,
required this.onTap,
});
final bool selected;
final String flag;
final String label;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return InkWell(
borderRadius: BorderRadius.circular(999),
onTap: selected ? null : onTap,
child: AnimatedContainer(
duration: const Duration(milliseconds: 180),
padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 2),
decoration: BoxDecoration(
color: selected ? sage : Colors.transparent,
borderRadius: BorderRadius.circular(999),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(flag, style: const TextStyle(fontSize: 11)),
const SizedBox(width: 3),
Text(
label,
style: TextStyle(
fontSize: 9,
height: 1,
fontWeight: FontWeight.w900,
color: selected ? Colors.white : Colors.black45,
),
),
],
),
),
);
}
}