Skip to content
8 changes: 8 additions & 0 deletions assets/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -883,5 +883,13 @@
"zulipAppTitle": "Zulip",
"@zulipAppTitle": {
"description": "The name of Zulip. This should be either 'Zulip' or a transliteration."
},
"languageSettingsTitle": "Language",
"@languageSettingsTitle": {
"description": "Title for the language settings section in the app settings."
},
"systemDefaultLanguage": "System default",
"@systemDefaultLanguage": {
"description": "Option to use the system default language setting."
}
}
12 changes: 12 additions & 0 deletions lib/generated/l10n/zulip_localizations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1303,6 +1303,18 @@ abstract class ZulipLocalizations {
/// In en, this message translates to:
/// **'Zulip'**
String get zulipAppTitle;

/// Title for the language settings section in the app settings.
///
/// In en, this message translates to:
/// **'Language'**
String get languageSettingsTitle;

/// Option to use the system default language setting.
///
/// In en, this message translates to:
/// **'System default'**
String get systemDefaultLanguage;
}

class _ZulipLocalizationsDelegate
Expand Down
6 changes: 6 additions & 0 deletions lib/generated/l10n/zulip_localizations_ar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -717,4 +717,10 @@ class ZulipLocalizationsAr extends ZulipLocalizations {

@override
String get zulipAppTitle => 'Zulip';

@override
String get languageSettingsTitle => 'Language';

@override
String get systemDefaultLanguage => 'System default';
}
6 changes: 6 additions & 0 deletions lib/generated/l10n/zulip_localizations_en.dart
Original file line number Diff line number Diff line change
Expand Up @@ -717,4 +717,10 @@ class ZulipLocalizationsEn extends ZulipLocalizations {

@override
String get zulipAppTitle => 'Zulip';

@override
String get languageSettingsTitle => 'Language';

@override
String get systemDefaultLanguage => 'System default';
}
6 changes: 6 additions & 0 deletions lib/generated/l10n/zulip_localizations_ja.dart
Original file line number Diff line number Diff line change
Expand Up @@ -717,4 +717,10 @@ class ZulipLocalizationsJa extends ZulipLocalizations {

@override
String get zulipAppTitle => 'Zulip';

@override
String get languageSettingsTitle => 'Language';

@override
String get systemDefaultLanguage => 'System default';
}
6 changes: 6 additions & 0 deletions lib/generated/l10n/zulip_localizations_nb.dart
Original file line number Diff line number Diff line change
Expand Up @@ -717,4 +717,10 @@ class ZulipLocalizationsNb extends ZulipLocalizations {

@override
String get zulipAppTitle => 'Zulip';

@override
String get languageSettingsTitle => 'Language';

@override
String get systemDefaultLanguage => 'System default';
}
6 changes: 6 additions & 0 deletions lib/generated/l10n/zulip_localizations_pl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -728,4 +728,10 @@ class ZulipLocalizationsPl extends ZulipLocalizations {

@override
String get zulipAppTitle => 'Zulip';

@override
String get languageSettingsTitle => 'Language';

@override
String get systemDefaultLanguage => 'System default';
}
6 changes: 6 additions & 0 deletions lib/generated/l10n/zulip_localizations_ru.dart
Original file line number Diff line number Diff line change
Expand Up @@ -731,4 +731,10 @@ class ZulipLocalizationsRu extends ZulipLocalizations {

@override
String get zulipAppTitle => 'Zulip';

@override
String get languageSettingsTitle => 'Language';

@override
String get systemDefaultLanguage => 'System default';
}
6 changes: 6 additions & 0 deletions lib/generated/l10n/zulip_localizations_sk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -719,4 +719,10 @@ class ZulipLocalizationsSk extends ZulipLocalizations {

@override
String get zulipAppTitle => 'Zulip';

@override
String get languageSettingsTitle => 'Language';

@override
String get systemDefaultLanguage => 'System default';
}
6 changes: 6 additions & 0 deletions lib/generated/l10n/zulip_localizations_uk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -731,4 +731,10 @@ class ZulipLocalizationsUk extends ZulipLocalizations {

@override
String get zulipAppTitle => 'Zulip';

@override
String get languageSettingsTitle => 'Language';

@override
String get systemDefaultLanguage => 'System default';
}
73 changes: 73 additions & 0 deletions lib/widgets/app.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:path_provider/path_provider.dart';

import '../generated/l10n/zulip_localizations.dart';
import '../log.dart';
Expand Down Expand Up @@ -47,6 +50,63 @@ class ZulipApp extends StatefulWidget {
return completer.future;
}

// static Locale? currentLocale;
// static final ValueNotifier<Locale?> _localeNotifier = ValueNotifier(currentLocale);
//
// static void setLocale(Locale? locale) {
// currentLocale = locale;
// _localeNotifier.value = locale;
// }

static Locale? currentLocale;
static final ValueNotifier<Locale?> _localeNotifier = ValueNotifier(currentLocale);
static File? _prefsFile;

static Future<File> _getPrefsFile() async {
if (_prefsFile == null) {
final dir = await getApplicationDocumentsDirectory();
_prefsFile = File('${dir.path}/language_prefs.json');
if (!await _prefsFile!.exists()) {
await _prefsFile!.create();
}
}
return _prefsFile!;
}

static Future<void> setLocale(Locale? locale) async {
currentLocale = locale;
_localeNotifier.value = locale;

try {
final file = await _getPrefsFile();
await file.writeAsString(
jsonEncode({'locale': locale?.toString()}),
flush: true,
);
} catch (e) {
debugPrint('Error saving locale: $e');
}
}

static Future<void> loadSavedLocale() async {
try {
final file = await _getPrefsFile();
final content = await file.readAsString();
if (content.isNotEmpty) {
final data = jsonDecode(content) as Map<String, dynamic>;
final localeString = data['locale'] as String?;
if (localeString != null && localeString.isNotEmpty) {
final parts = localeString.split('_');
currentLocale = parts.length == 2
? Locale(parts[0], parts[1])
: Locale(parts[0]);
_localeNotifier.value = currentLocale;
}
}
} catch (e) {
debugPrint('Error loading locale: $e');
}
}
/// A key for the navigator for the whole app.
///
/// For code that exists entirely outside the widget tree and has no natural
Expand Down Expand Up @@ -160,14 +220,26 @@ class _ZulipAppState extends State<ZulipApp> with WidgetsBindingObserver {
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
ZulipApp._localeNotifier.addListener(_handleLocaleChange);
_initializeApp();
}

Future<void> _initializeApp() async {
await ZulipApp.loadSavedLocale(); // Load saved locale first
ZulipApp._localeNotifier.addListener(_handleLocaleChange);
}

@override
void dispose() {
ZulipApp._localeNotifier.removeListener(_handleLocaleChange);
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}

void _handleLocaleChange() {
if (mounted) setState(() {});
}

List<Route<dynamic>> _handleGenerateInitialRoutes(String initialRoute) {
// The `_ZulipAppState.context` lacks the required ancestors. Instead
// we use the Navigator which should be available when this callback is
Expand Down Expand Up @@ -220,6 +292,7 @@ class _ZulipAppState extends State<ZulipApp> with WidgetsBindingObserver {
return GlobalStoreWidget(
child: Builder(builder: (context) {
return MaterialApp(
locale: ZulipApp.currentLocale,
onGenerateTitle: (BuildContext context) {
return ZulipLocalizations.of(context).zulipAppTitle;
},
Expand Down
Loading