Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/l10n/app_de.arb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
"settings_page_dark_theme" : "Dunkel",
"settings_page_light_theme" : "Hell",
"settings_page_system_theme" : "System",
"settings_page_language" : "Sprache:",
"settings_page_system_language" : "System",
"settings_page_advanced" : "Fortgeschritten:",
"settings_page_serversettings" : "Server Einstellungen",
"settings_page_rendezvousserver" : "Rendezvous Server:",
Expand Down
2 changes: 2 additions & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
"settings_page_dark_theme" : "Dark",
"settings_page_light_theme" : "Light",
"settings_page_system_theme" : "System",
"settings_page_language" : "Language:",
"settings_page_system_language" : "System",
"settings_page_advanced" : "Advanced:",
"settings_page_serversettings" : "Server Settings",
"settings_page_rendezvousserver" : "Rendezvous Server:",
Expand Down
6 changes: 5 additions & 1 deletion lib/l10n/app_et.arb
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,9 @@
"transfer_finished_label": "Fail on vastuvõetud!",
"@transfer_finished_label": {},
"transfer_finished_open": "Ava fail",
"@transfer_finished_open": {}
"@transfer_finished_open": {},
"settings_page_language": "Keel:",
"@settings_page_language": {},
"settings_page_system_language": "Süsteem",
"@settings_page_system_language": {}
}
2 changes: 2 additions & 0 deletions lib/l10n/app_pt.arb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
"settings_page_dark_theme": "Escuro",
"settings_page_light_theme": "Claro",
"settings_page_system_theme": "Sistema",
"settings_page_language": "Idioma:",
"settings_page_system_language": "Sistema",
"settings_page_advanced": "Avançado:",
"settings_page_serversettings": "Configurações do Servidor",
"settings_page_rendezvousserver": "Servidor Rendezvous:",
Expand Down
2 changes: 2 additions & 0 deletions lib/l10n/app_sv.arb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
"settings_page_dark_theme" : "Mörkt",
"settings_page_light_theme" : "Ljus",
"settings_page_system_theme" : "System",
"settings_page_language" : "Språk:",
"settings_page_system_language" : "System",
"settings_page_advanced" : "Avancerad:",
"settings_page_serversettings" : "Serverinställningar",
"settings_page_rendezvousserver" : "Rendezvous Server:",
Expand Down
2 changes: 2 additions & 0 deletions lib/l10n/app_uk.arb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
"settings_page_dark_theme" : "Темна",
"settings_page_light_theme" : "Світла",
"settings_page_system_theme" : "Системна",
"settings_page_language" : "Мова:",
"settings_page_system_language" : "Системна",
"settings_page_advanced" : "Додатково:",
"settings_page_serversettings" : "Налаштування сервера",
"settings_page_rendezvousserver" : "Сервер рандеву:",
Expand Down
45 changes: 45 additions & 0 deletions lib/locale/locale_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import 'package:flutter/cupertino.dart';

import '../l10n/app_localizations.dart';
import '../settings/settings.dart';

enum LanguageType { system, de, en, et, pt, sv, uk }

class LocaleProvider with ChangeNotifier {
// Native language names - displayed in their native form universally, and thus not localized
static const Map<LanguageType, String> nativeLanguageNames = {
LanguageType.de: 'Deutsch',
LanguageType.en: 'English',
LanguageType.et: 'Eesti',
LanguageType.pt: 'Português',
LanguageType.sv: 'Svenska',
LanguageType.uk: 'Українська',
};

static String getLanguageDisplayName(
LanguageType language, BuildContext context) {
if (language == LanguageType.system) {
return AppLocalizations.of(context)!.settings_page_system_language;
}
final name = nativeLanguageNames[language];
assert(name != null, 'Missing native language name for $language');
return name ?? 'Unknown language';
}

LanguageType _language = LanguageType.system;

LanguageType get language => _language;

set language(LanguageType language) {
_language = language;
Settings.setLanguage(language);
notifyListeners();
}

Locale? getLocale() {
if (_language == LanguageType.system) {
return null; // Let the system decide
}
return Locale(_language.name);
}
}
65 changes: 43 additions & 22 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:provider/provider.dart';
import 'l10n/app_localizations.dart';
import 'src/rust/api/wormhole.dart';
import 'src/rust/frb_generated.dart';
import 'locale/locale_provider.dart';
import 'navigation/navigation.dart';
import 'settings/settings.dart';
import 'theme/dark_theme.dart';
Expand Down Expand Up @@ -76,11 +77,13 @@ class MyApp extends StatefulWidget {

class _MyAppState extends State<MyApp> {
ThemeProvider themeChangeProvider = ThemeProvider();
LocaleProvider localeChangeProvider = LocaleProvider();

@override
void initState() {
super.initState();
getCurrentAppTheme();
getCurrentAppLocale();
initBackend();
}

Expand All @@ -93,35 +96,53 @@ class _MyAppState extends State<MyApp> {
themeChangeProvider.theme = await Settings.getTheme();
}

void getCurrentAppLocale() async {
localeChangeProvider.language = await Settings.getLanguage();
}

@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) {
return themeChangeProvider;
},
child: Consumer<ThemeProvider>(
builder: (context, value, child) {
return MaterialApp(
debugShowCheckedModeBanner: false,
localeResolutionCallback: (deviceLocale, supportedLocales) {
if (supportedLocales
.map((e) => e.languageCode)
.contains(deviceLocale?.languageCode)) {
return deviceLocale;
}
AppLogger.info('Fallback to default locale');
return const Locale('en');
},
theme: lightTheme,
darkTheme: darkTheme,
themeMode: themeChangeProvider.isDarkThemeActive()
? ThemeMode.dark
: ThemeMode.light,
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: const Navigation(),
);
child: ChangeNotifierProvider(
create: (_) {
return localeChangeProvider;
},
child: Consumer2<ThemeProvider, LocaleProvider>(
builder: (context, themeProvider, localeProvider, child) {
return MaterialApp(
debugShowCheckedModeBanner: false,
locale: localeProvider.getLocale(),
localeResolutionCallback: (deviceLocale, supportedLocales) {
// If user selected a specific language, use it
final userLocale = localeProvider.getLocale();
if (userLocale != null) {
return userLocale;
}

// Otherwise, use device locale if supported
if (supportedLocales
.map((e) => e.languageCode)
.contains(deviceLocale?.languageCode)) {
return deviceLocale;
}

AppLogger.info('Fallback to default locale');
return const Locale('en');
},
theme: lightTheme,
darkTheme: darkTheme,
themeMode: themeProvider.isDarkThemeActive()
? ThemeMode.dark
: ThemeMode.light,
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: const Navigation(),
);
},
),
),
);
}
Expand Down
48 changes: 48 additions & 0 deletions lib/pages/settings/settings_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:provider/provider.dart';
import 'package:toggle_switch/toggle_switch.dart';

import '../../l10n/app_localizations.dart';
import '../../locale/locale_provider.dart';
import '../../src/rust/api/wormhole.dart';
import '../../settings/settings.dart';
import '../../theme/theme_provider.dart';
Expand Down Expand Up @@ -114,9 +115,52 @@ class _SettingsPageState extends State<SettingsPage> {
);
}

Widget _buildLanguageDropdown(
BuildContext context,
LanguageType currentLanguage,
LocaleProvider localeprov,
ThemeData theme,
) {
final items = LanguageType.values;

final labels = {
for (var lang in LanguageType.values)
lang: LocaleProvider.getLanguageDisplayName(lang, context)
};

return SizedBox(
width: 180.0,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
decoration: BoxDecoration(
color: theme.colorScheme.secondary,
borderRadius: BorderRadius.circular(15.0),
),
child: DropdownButton<LanguageType>(
value: currentLanguage,
items: items
.map((language) => DropdownMenuItem(
value: language,
child: Text(labels[language] ?? ''),
))
.toList(),
onChanged: (LanguageType? value) {
if (value != null) {
localeprov.language = value;
}
},
underline: const SizedBox(),
isExpanded: true,
style: theme.textTheme.bodyMedium,
),
),
);
}

List<Widget> _buildSettingsContent() {
final theme = Theme.of(context);
final themeprov = Provider.of<ThemeProvider>(context);
final localeprov = Provider.of<LocaleProvider>(context);

return [
SettingsRow(
Expand Down Expand Up @@ -214,6 +258,10 @@ class _SettingsPageState extends State<SettingsPage> {
themeprov.theme = ThemeType.values[index];
},
)),
SettingsRow(
name: AppLocalizations.of(context)!.settings_page_language,
child: _buildLanguageDropdown(
context, localeprov.language, localeprov, theme)),
SettingsRow(
name: AppLocalizations.of(context)!.settings_page_advanced,
child: SettingsSectionButton(
Expand Down
17 changes: 17 additions & 0 deletions lib/settings/settings.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:shared_preferences/shared_preferences.dart';

import '../locale/locale_provider.dart' show LanguageType;
import '../theme/theme_provider.dart';

enum CodeType { qrCode, aztecCode }
Expand All @@ -17,6 +18,7 @@ class Settings {
static const _codeType = 'CODETYPE';
static const _codeAlwaysVisible = 'CODEALVISIBLE';
static const themeStatus = 'THEMESTATUS';
static const _language = 'LANGUAGE';
static const _rendezvousUrl = 'RENDEZVOUSSERVER';
static const _transitUrl = 'TRANSITURL';

Expand Down Expand Up @@ -44,6 +46,10 @@ class Settings {
await _setField(theme.index, themeStatus);
}

static Future<void> setLanguage(LanguageType language) async {
await _setField(language.index, _language);
}

static Future<String?> getRendezvousUrl() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString(_rendezvousUrl);
Expand Down Expand Up @@ -86,6 +92,17 @@ class Settings {
}
}

/// get current language
static Future<LanguageType> getLanguage() async {
final prefs = await SharedPreferences.getInstance();
final idx = prefs.getInt(_language);
if (idx != null) {
return LanguageType.values[idx];
} else {
return LanguageType.system;
}
}

static Future<void> _setField<T>(T? value, String field) async {
final prefs = await SharedPreferences.getInstance();
if (value == null) {
Expand Down
Loading