feat: Add US/GB english discrimination, fix incorrect localizations

This commit is contained in:
Anton Stubenbord
2023-10-09 00:24:13 +02:00
parent d4951e1c82
commit 23b7560dda
24 changed files with 204 additions and 85 deletions

View File

@@ -0,0 +1,2 @@
* Beheben von inkorrekter Lokalisierung in der App
* Hinzufügen der Unterscheidung zwischen US und GB Englisch

View File

@@ -0,0 +1,2 @@
* Fixed incorrect localizations of dates and other texts
* Add discrimination between US and GB English (useful for date input)

View File

@@ -146,7 +146,7 @@ class _ExtendedDateRangeDialogState extends State<ExtendedDateRangeDialog> {
)
: null,
),
format: DateFormat.yMd(),
format: DateFormat.yMd(Localizations.localeOf(context).toString()),
lastDate: _dateTimeMax(_before, DateTime.now()),
inputType: InputType.date,
onChanged: (after) {
@@ -174,7 +174,7 @@ class _ExtendedDateRangeDialogState extends State<ExtendedDateRangeDialog> {
)
: null,
),
format: DateFormat.yMd(),
format: DateFormat.yMd(Localizations.localeOf(context).toString()),
firstDate: _after,
lastDate: DateTime.now(),
onChanged: (before) {

View File

@@ -79,7 +79,7 @@ class _FormBuilderExtendedDateRangePickerState
}
String _dateRangeQueryToString(DateRangeQuery query) {
final df = DateFormat.yMd();
final df = DateFormat.yMd(Localizations.localeOf(context).toString());
if (query is UnsetDateRangeQuery) {
return '';
} else if (query is AbsoluteDateRangeQuery) {

View File

@@ -4,6 +4,7 @@ import 'package:flutter_animate/flutter_animate.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:paperless_mobile/constants.dart';
import 'package:paperless_mobile/core/database/tables/local_user_account.dart';
import 'package:paperless_mobile/core/widgets/paperless_logo.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
import 'package:paperless_mobile/features/documents/cubit/documents_cubit.dart';
@@ -22,6 +23,10 @@ class AppDrawer extends StatelessWidget {
@override
Widget build(BuildContext context) {
final currentAccount = context.watch<LocalUserAccount>();
final username = currentAccount.paperlessUser.username;
final serverUrl =
currentAccount.serverUrl.replaceAll(RegExp(r'https?://'), '');
return SafeArea(
child: Drawer(
child: Column(
@@ -29,13 +34,43 @@ class AppDrawer extends StatelessWidget {
children: [
Row(
children: [
const PaperlessLogo.green(),
const PaperlessLogo.green(
width: 32,
height: 32,
),
Text(
"Paperless Mobile",
style: Theme.of(context).textTheme.titleMedium,
),
],
).padded(),
).paddedLTRB(8, 8, 8, 16),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
S.of(context)!.loggedInAs(username),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.labelMedium?.copyWith(
color: Theme.of(context)
.colorScheme
.onBackground
.withOpacity(0.5),
),
),
Text(
serverUrl,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.labelMedium?.copyWith(
color: Theme.of(context)
.colorScheme
.onBackground
.withOpacity(0.5),
),
),
],
).paddedSymmetrically(horizontal: 16),
const Divider(),
ListTile(
dense: true,
@@ -43,6 +78,31 @@ class AppDrawer extends StatelessWidget {
leading: const Icon(Icons.info_outline),
onTap: () => _showAboutDialog(context),
),
ListTile(
dense: true,
leading: const Icon(Icons.favorite_outline),
title: Text(S.of(context)!.donate),
onTap: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
icon: const Icon(Icons.favorite),
title: Text(S.of(context)!.donate),
content: Text(
S.of(context)!.donationDialogContent,
),
actionsAlignment: MainAxisAlignment.spaceBetween,
actions: [
Text("~ Anton"),
TextButton(
onPressed: Navigator.of(context).pop,
child: Text(S.of(context)!.gotIt),
),
],
),
);
},
),
ListTile(
dense: true,
leading: const Icon(Icons.bug_report_outlined),
@@ -63,26 +123,6 @@ class AppDrawer extends StatelessWidget {
);
},
),
ListTile(
dense: true,
leading: const Icon(Icons.favorite_outline),
title: Text(S.of(context)!.donate),
onTap: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
icon: const Icon(Icons.favorite),
title: Text(S.of(context)!.donate),
content: Text(
S.of(context)!.donationDialogContent,
),
actions: const [
Text("~ Anton"),
],
),
);
},
),
ListTile(
dense: true,
leading: SvgPicture.asset(

View File

@@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:intl/intl.dart';
import 'package:open_filex/open_filex.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/bloc/connectivity_cubit.dart';
@@ -43,6 +45,11 @@ class _DocumentDetailsPageState extends State<DocumentDetailsPage> {
static const double _itemSpacing = 24;
final _pagingScrollController = ScrollController();
@override
void didChangeDependencies() {
super.didChangeDependencies();
initializeDateFormatting(Localizations.localeOf(context).toString());
}
@override
Widget build(BuildContext context) {

View File

@@ -44,12 +44,14 @@ class _DocumentMetaDataWidgetState extends State<DocumentMetaDataWidget> {
document: widget.document,
).paddedOnly(bottom: widget.itemSpacing),
DetailsItem.text(
DateFormat().format(widget.document.modified),
DateFormat.yMMMMd(Localizations.localeOf(context).toString())
.format(widget.document.modified),
context: context,
label: S.of(context)!.modifiedAt,
).paddedOnly(bottom: widget.itemSpacing),
DetailsItem.text(
DateFormat().format(widget.document.added),
DateFormat.yMMMMd(Localizations.localeOf(context).toString())
.format(widget.document.added),
context: context,
label: S.of(context)!.addedAt,
).paddedOnly(bottom: widget.itemSpacing),

View File

@@ -39,7 +39,8 @@ class DocumentOverviewWidget extends StatelessWidget {
),
).paddedOnly(bottom: itemSpacing),
DetailsItem.text(
DateFormat.yMMMMd().format(document.created),
DateFormat.yMMMMd(Localizations.localeOf(context).toString())
.format(document.created),
context: context,
label: S.of(context)!.createdAt,
).paddedOnly(bottom: itemSpacing),

View File

@@ -376,14 +376,16 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
label: Text(S.of(context)!.createdAt),
),
initialValue: initialCreatedAtDate,
format: DateFormat.yMMMMd(),
format: DateFormat.yMMMMd(Localizations.localeOf(context).toString()),
initialEntryMode: DatePickerEntryMode.calendar,
),
if (filteredSuggestions?.hasSuggestedDates ?? false)
_buildSuggestionsSkeleton<DateTime>(
suggestions: filteredSuggestions!.dates,
itemBuilder: (context, itemData) => ActionChip(
label: Text(DateFormat.yMMMd().format(itemData)),
label: Text(
DateFormat.yMMMMd(Localizations.localeOf(context).toString())
.format(itemData)),
onPressed: () => _formKey.currentState?.fields[fkCreatedDate]
?.didChange(itemData),
),

View File

@@ -10,12 +10,10 @@ import 'package:paperless_mobile/features/settings/view/widgets/user_avatar.dart
import 'package:provider/provider.dart';
class SliverSearchBar extends StatelessWidget {
final bool floating;
final bool pinned;
final String titleText;
const SliverSearchBar({
super.key,
this.floating = false,
this.pinned = false,
required this.titleText,
});

View File

@@ -68,7 +68,6 @@ class _DocumentUploadPreparationPageState
void initState() {
super.initState();
_syncTitleAndFilename = widget.filename == null && widget.title == null;
initializeDateFormatting();
}
@override
@@ -219,7 +218,8 @@ class _DocumentUploadPreparationPageState
// Created at
FormBuilderDateTimePicker(
autovalidateMode: AutovalidateMode.always,
format: DateFormat.yMMMMd(),
format: DateFormat.yMMMMd(
Localizations.localeOf(context).toString()),
inputType: InputType.date,
name: DocumentModel.createdKey,
initialValue: null,

View File

@@ -177,13 +177,9 @@ class _DocumentsPageState extends State<DocumentsPage> {
child: _showExtendedFab
? Row(
children: [
const Icon(
Icons.filter_alt_outlined,
),
const Icon(Icons.filter_alt_outlined),
const SizedBox(width: 8),
Text(
S.of(context)!.filterDocuments,
),
Text(S.of(context)!.filterDocuments),
],
)
: const Icon(Icons.filter_alt_outlined),
@@ -270,7 +266,6 @@ class _DocumentsPageState extends State<DocumentsPage> {
builder: (context, state) {
if (state.selection.isEmpty) {
return SliverSearchBar(
floating: true,
titleText: S.of(context)!.documents,
);
} else {

View File

@@ -89,7 +89,8 @@ class DocumentDetailedItem extends DocumentItem {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
DateFormat.yMMMMd().format(document.created),
DateFormat.yMMMMd(Localizations.localeOf(context).toString())
.format(document.created),
style: Theme.of(context)
.textTheme
.bodySmall

View File

@@ -121,7 +121,9 @@ class DocumentGridItem extends DocumentItem {
),
const Spacer(),
Text(
DateFormat.yMMMd().format(document.created),
DateFormat.yMMMMd(
Localizations.localeOf(context).toString())
.format(document.created),
style: Theme.of(context).textTheme.bodySmall,
),
],

View File

@@ -81,7 +81,8 @@ class DocumentListItem extends DocumentItem {
maxLines: 1,
overflow: TextOverflow.ellipsis,
text: TextSpan(
text: DateFormat.yMMMd().format(document.created),
text: DateFormat.yMMMMd(Localizations.localeOf(context).toString())
.format(document.created),
style: Theme.of(context)
.textTheme
.labelSmall

View File

@@ -331,7 +331,8 @@ class _InboxPageState extends State<InboxPage>
if (doc.added.isYesterday) {
return S.of(context)!.yesterday;
}
return DateFormat.yMMMMd().format(doc.added);
return DateFormat.yMMMMd(Localizations.localeOf(context).toString())
.format(doc.added);
},
);
}

View File

@@ -35,7 +35,6 @@ class _LandingPageState extends State<LandingPage> {
SliverOverlapAbsorber(
handle: _searchBarHandle,
sliver: SliverSearchBar(
floating: true,
titleText: S.of(context)!.documents,
),
),

View File

@@ -13,7 +13,8 @@ class LanguageSelectionSetting extends StatefulWidget {
class _LanguageSelectionSettingState extends State<LanguageSelectionSetting> {
static const _languageOptions = {
'en': LanguageOption('English', true),
'en': LanguageOption('English (US)', true),
'en_GB': LanguageOption('English (GB)', true),
'de': LanguageOption('Deutsch', true),
'es': LanguageOption("Español", true),
'fr': LanguageOption('Français', true),

View File

@@ -40,31 +40,34 @@ class _RadioSettingsDialogState<T> extends State<RadioSettingsDialog<T>> {
return AlertDialog(
actions: [
const DialogCancelButton(),
widget.confirmButton ??
DialogConfirmButton(
returnValue: _groupValue,
),
widget.confirmButton ?? DialogConfirmButton(returnValue: _groupValue),
],
title: widget.titleText != null ? Text(widget.titleText!) : null,
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (widget.descriptionText != null)
Text(widget.descriptionText!,
style: Theme.of(context).textTheme.bodySmall),
...widget.options.map(_buildOptionListTile),
if (widget.footer != null) widget.footer!,
],
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (widget.descriptionText != null)
Text(
widget.descriptionText!,
style: Theme.of(context).textTheme.bodySmall,
),
...widget.options.map(_buildOptionListTile).toList(),
if (widget.footer != null) widget.footer!,
],
),
),
);
}
Widget _buildOptionListTile(RadioOption<T> option) => RadioListTile<T>(
groupValue: _groupValue,
onChanged: (value) => setState(() => _groupValue = value!),
value: option.value,
title: Text(option.label),
);
Widget _buildOptionListTile(RadioOption<T> option) {
return RadioListTile<T>(
groupValue: _groupValue,
onChanged: (value) => setState(() => _groupValue = value!),
value: option.value,
title: Text(option.label),
);
}
}
class RadioOption<T> {

View File

@@ -15,16 +15,26 @@ class UserAvatar extends StatelessWidget {
Colors.primaries[account.id.hashCode % Colors.primaries.length];
final foregroundColor =
backgroundColor.computeLuminance() > 0.5 ? Colors.black : Colors.white;
return CircleAvatar(
child: Text(
return Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: backgroundColor.shade900.withOpacity(0.4),
width: 2,
),
),
child: CircleAvatar(
child: Text(
(account.paperlessUser.fullName ?? account.paperlessUser.username)
.split(" ")
.take(2)
.map((e) => e.substring(0, 1))
.map((e) => e.toUpperCase())
.join("")),
backgroundColor: backgroundColor,
foregroundColor: foregroundColor,
.join(""),
),
backgroundColor: backgroundColor,
foregroundColor: foregroundColor,
),
);
}
}

View File

@@ -41,19 +41,22 @@ import 'package:paperless_mobile/routes/typed/shells/authenticated_route.dart';
import 'package:paperless_mobile/routes/typed/top_level/add_account_route.dart';
import 'package:paperless_mobile/routes/typed/top_level/logging_out_route.dart';
import 'package:paperless_mobile/routes/typed/top_level/login_route.dart';
import 'package:paperless_mobile/routes/typed/top_level/settings_route.dart';
import 'package:paperless_mobile/theme.dart';
import 'package:path_provider/path_provider.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
String get defaultPreferredLocaleSubtag {
String preferredLocale = Platform.localeName.split("_").first;
if (!S.supportedLocales
.any((locale) => locale.languageCode == preferredLocale)) {
preferredLocale = 'en';
Locale get defaultPreferredLocale {
final deviceLocale = _stringToLocale(Platform.localeName);
if (S.supportedLocales.contains(deviceLocale)) {
return deviceLocale;
} else if (S.supportedLocales
.map((e) => e.languageCode)
.contains(deviceLocale.languageCode)) {
return Locale(deviceLocale.languageCode);
} else {
return const Locale('en');
}
return preferredLocale;
}
Map<String, Future<void> Function()> _migrations = {
@@ -99,7 +102,7 @@ Future<void> _initHive() async {
if (!globalSettingsBox.hasValue) {
await globalSettingsBox.setValue(
GlobalSettings(preferredLocaleSubtag: defaultPreferredLocaleSubtag),
GlobalSettings(preferredLocaleSubtag: defaultPreferredLocale.toString()),
);
}
}
@@ -323,6 +326,7 @@ class _GoRouterShellState extends State<GoRouterShell> {
Widget build(BuildContext context) {
return GlobalSettingsBuilder(
builder: (context, settings) {
final locale = _stringToLocale(settings.preferredLocaleSubtag);
return DynamicColorBuilder(
builder: (lightDynamic, darkDynamic) {
return MaterialApp.router(
@@ -340,10 +344,41 @@ class _GoRouterShellState extends State<GoRouterShell> {
preferredColorScheme: settings.preferredColorSchemeOption,
),
themeMode: settings.preferredThemeMode,
supportedLocales: S.supportedLocales,
locale: Locale.fromSubtags(
languageCode: settings.preferredLocaleSubtag,
),
supportedLocales: const [
Locale('en'),
Locale('de'),
Locale('en', 'GB'),
Locale('ca'),
Locale('cs'),
Locale('es'),
Locale('fr'),
Locale('pl'),
Locale('ru'),
Locale('tr'),
],
localeResolutionCallback: (locale, supportedLocales) {
if (locale == null) {
return supportedLocales.first;
}
final exactMatch = supportedLocales
.where((element) =>
element.languageCode == locale.languageCode &&
element.countryCode == locale.countryCode)
.toList();
if (exactMatch.isNotEmpty) {
return exactMatch.first;
}
final superLanguageMatch = supportedLocales
.where((element) =>
element.languageCode == locale.languageCode)
.toList();
if (superLanguageMatch.isNotEmpty) {
return superLanguageMatch.first;
}
return supportedLocales.first;
},
locale: locale,
localizationsDelegates: S.localizationsDelegates,
);
},
@@ -352,3 +387,10 @@ class _GoRouterShellState extends State<GoRouterShell> {
);
}
}
Locale _stringToLocale(String code) {
final codes = code.split("_");
final languageCode = codes[0];
final countryCode = codes.length > 1 ? codes[1] : null;
return Locale(languageCode, countryCode);
}

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:intl/intl.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/model/info_message_exception.dart';
import 'package:paperless_mobile/features/login/cubit/authentication_cubit.dart';
@@ -39,7 +40,7 @@ class AddAccountRoute extends GoRouteData {
clientCertificate: clientCertificate,
serverUrl: serverUrl,
enableBiometricAuthentication: false,
locale: Localizations.localeOf(context).languageCode,
locale: Intl.getCurrentLocale(),
);
final shoudSwitch = await showDialog<bool>(
context: context,

View File

@@ -0,0 +1,9 @@
import 'package:paperless_mobile/generated/l10n/app_localizations_en.dart';
class SEnGb extends SEn {
SEnGb() : super('en_GB');
}
class SEnUs extends SEn {
SEnUs() : super('en_US');
}

View File

@@ -15,7 +15,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 3.0.2+49
version: 3.0.3+50
environment:
sdk: ">=3.0.0 <4.0.0"