mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-08-01 05:10:15 +02:00
Internationalization (#131)
Replaced hardcoded English strings with locale-based variables based on the [easy_localization](https://pub.dev/packages/easy_localization) Flutter plugin.
This commit is contained in:
@@ -6,6 +6,7 @@ import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
@@ -105,7 +106,7 @@ class AppsProvider with ChangeNotifier {
|
||||
}
|
||||
if (response.statusCode != 200) {
|
||||
tempDownloadedFile.deleteSync();
|
||||
throw response.reasonPhrase ?? 'Unknown Error';
|
||||
throw response.reasonPhrase ?? tr('unexpectedError');
|
||||
}
|
||||
tempDownloadedFile.renameSync(downloadedFile.path);
|
||||
}
|
||||
@@ -127,7 +128,8 @@ class AppsProvider with ChangeNotifier {
|
||||
notifyListeners();
|
||||
} else if ((prog == 25 || prog == 50 || prog == 75) && prevProg != prog) {
|
||||
Fluttertoast.showToast(
|
||||
msg: 'Progress: $prog%', toastLength: Toast.LENGTH_SHORT);
|
||||
msg: tr('percentProgress', args: [prog.toString()]),
|
||||
toastLength: Toast.LENGTH_SHORT);
|
||||
}
|
||||
prevProg = prog;
|
||||
});
|
||||
@@ -272,7 +274,7 @@ class AppsProvider with ChangeNotifier {
|
||||
// 2. That cannot be installed silently (IF no buildContext was given for interactive install)
|
||||
for (var id in appIds) {
|
||||
if (apps[id] == null) {
|
||||
throw ObtainiumError('App not found');
|
||||
throw ObtainiumError(tr('appNotFound'));
|
||||
}
|
||||
String? apkUrl;
|
||||
if (!apps[id]!.app.trackOnly) {
|
||||
@@ -413,6 +415,7 @@ class AppsProvider with ChangeNotifier {
|
||||
if (installedInfo != null && app.installedVersion == null) {
|
||||
if (app.latestVersion.characters
|
||||
.where((p0) => [
|
||||
// TODO: Won't work for other charsets
|
||||
'0',
|
||||
'1',
|
||||
'2',
|
||||
@@ -601,13 +604,13 @@ class AppsProvider with ChangeNotifier {
|
||||
|
||||
Future<String> exportApps() async {
|
||||
Directory? exportDir = Directory('/storage/emulated/0/Download');
|
||||
String path = 'Downloads';
|
||||
String path = 'Downloads'; // TODO: Is this true on non-english phones?
|
||||
if (!exportDir.existsSync()) {
|
||||
exportDir = await getExternalStorageDirectory();
|
||||
path = exportDir!.path;
|
||||
}
|
||||
File export = File(
|
||||
'${exportDir.path}/obtainium-export-${DateTime.now().millisecondsSinceEpoch}.json');
|
||||
'${exportDir.path}/${tr('obtainiumExportHyphenatedLowercase')}-${DateTime.now().millisecondsSinceEpoch}.json');
|
||||
export.writeAsStringSync(
|
||||
jsonEncode(apps.values.map((e) => e.app.toJson()).toList()));
|
||||
return path;
|
||||
@@ -643,7 +646,7 @@ class AppsProvider with ChangeNotifier {
|
||||
Map<String, dynamic> errorsMap = results[1];
|
||||
for (var app in pps) {
|
||||
if (apps.containsKey(app.id)) {
|
||||
errorsMap.addAll({app.id: 'App already added'});
|
||||
errorsMap.addAll({app.id: tr('appAlreadyAdded')});
|
||||
} else {
|
||||
await saveApps([app]);
|
||||
}
|
||||
@@ -673,9 +676,9 @@ class _APKPickerState extends State<APKPicker> {
|
||||
apkUrl ??= widget.initVal;
|
||||
return AlertDialog(
|
||||
scrollable: true,
|
||||
title: const Text('Pick an APK'),
|
||||
title: Text(tr('pickAnAPK')),
|
||||
content: Column(children: [
|
||||
Text('${widget.app.name} has more than one package:'),
|
||||
Text(tr('appHasMoreThanOnePackage', args: [widget.app.name])),
|
||||
const SizedBox(height: 16),
|
||||
...widget.app.apkUrls.map(
|
||||
(u) => RadioListTile<String>(
|
||||
@@ -697,7 +700,11 @@ class _APKPickerState extends State<APKPicker> {
|
||||
),
|
||||
if (widget.archs != null)
|
||||
Text(
|
||||
'Note:\nYour device supports the ${widget.archs!.length == 1 ? '\'${widget.archs![0]}\' CPU architecture.' : 'following CPU architectures: ${list2FriendlyString(widget.archs!.map((e) => '\'$e\'').toList())}.'}',
|
||||
widget.archs!.length == 1
|
||||
? tr('deviceSupportsXArch', args: [widget.archs![0]])
|
||||
: tr('deviceSupportsFollowingArchs') +
|
||||
list2FriendlyString(
|
||||
widget.archs!.map((e) => '\'$e\'').toList()),
|
||||
style: const TextStyle(fontStyle: FontStyle.italic, fontSize: 12),
|
||||
),
|
||||
]),
|
||||
@@ -706,13 +713,13 @@ class _APKPickerState extends State<APKPicker> {
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(null);
|
||||
},
|
||||
child: const Text('Cancel')),
|
||||
child: Text(tr('cancel'))),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
HapticFeedback.selectionClick();
|
||||
Navigator.of(context).pop(apkUrl);
|
||||
},
|
||||
child: const Text('Continue'))
|
||||
child: Text(tr('continue')))
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -734,21 +741,23 @@ class _APKOriginWarningDialogState extends State<APKOriginWarningDialog> {
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
scrollable: true,
|
||||
title: const Text('Warning'),
|
||||
content: Text(
|
||||
'The App source is \'${Uri.parse(widget.sourceUrl).host}\' but the release package comes from \'${Uri.parse(widget.apkUrl).host}\'. Continue?'),
|
||||
title: Text(tr('warning')),
|
||||
content: Text(tr('sourceIsXButPackageFromYPrompt', args: [
|
||||
Uri.parse(widget.sourceUrl).host,
|
||||
Uri.parse(widget.apkUrl).host
|
||||
])),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(null);
|
||||
},
|
||||
child: const Text('Cancel')),
|
||||
child: Text(tr('cancel'))),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
HapticFeedback.selectionClick();
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
child: const Text('Continue'))
|
||||
child: Text(tr('continue')))
|
||||
],
|
||||
);
|
||||
}
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
|
||||
@@ -85,7 +86,9 @@ create table if not exists $logTable (
|
||||
var res = await (await getDB())
|
||||
.delete(logTable, where: where.key, whereArgs: where.value);
|
||||
if (res > 0) {
|
||||
add('Cleared $res logs (before = $before, after = $after)');
|
||||
add(plural('clearedNLogsBeforeXAfterY', res,
|
||||
namedArgs: {'before': before.toString(), 'after': after.toString()},
|
||||
name: 'n'));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
// Exposes functions that can be used to send notifications to the user
|
||||
// Contains a set of pre-defined ObtainiumNotification objects that should be used throughout the app
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:obtainium/providers/source_provider.dart';
|
||||
|
||||
@@ -21,33 +22,30 @@ class UpdateNotification extends ObtainiumNotification {
|
||||
UpdateNotification(List<App> updates)
|
||||
: super(
|
||||
2,
|
||||
'Updates Available',
|
||||
tr('updatesAvailable'),
|
||||
'',
|
||||
'UPDATES_AVAILABLE',
|
||||
'Updates Available',
|
||||
'Notifies the user that updates are available for one or more Apps tracked by Obtainium',
|
||||
tr('updatesAvailable'),
|
||||
tr('updatesAvailableNotifDescription'),
|
||||
Importance.max) {
|
||||
message = updates.isEmpty
|
||||
? "No new updates."
|
||||
? tr('noNewUpdates')
|
||||
: updates.length == 1
|
||||
? '${updates[0].name} has an update.'
|
||||
: '${(updates.length == 2 ? '${updates[0].name} and ${updates[1].name}' : '${updates[0].name} and ${updates.length - 1} more apps')} have updates.';
|
||||
? tr('xHasAnUpdate', args: [updates[0].name])
|
||||
: plural('xAndNMoreUpdatesAvailable', updates.length - 1,
|
||||
args: [updates[0].name]);
|
||||
}
|
||||
}
|
||||
|
||||
class SilentUpdateNotification extends ObtainiumNotification {
|
||||
SilentUpdateNotification(List<App> updates)
|
||||
: super(
|
||||
3,
|
||||
'Apps Updated',
|
||||
'',
|
||||
'APPS_UPDATED',
|
||||
'Apps Updated',
|
||||
'Notifies the user that updates to one or more Apps were applied in the background',
|
||||
Importance.defaultImportance) {
|
||||
: super(3, tr('appsUpdated'), '', 'APPS_UPDATED', tr('appsUpdated'),
|
||||
tr('appsUpdatedNotifDescription'), Importance.defaultImportance) {
|
||||
message = updates.length == 1
|
||||
? '${updates[0].name} was updated to ${updates[0].latestVersion}.'
|
||||
: '${(updates.length == 2 ? '${updates[0].name} and ${updates[1].name}' : '${updates[0].name} and ${updates.length - 1} more apps')} were updated.';
|
||||
? tr('xWasUpdatedToY',
|
||||
args: [updates[0].name, updates[0].latestVersion])
|
||||
: plural('xAndNMoreUpdatesInstalled', updates.length - 1,
|
||||
args: [updates[0].name]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,27 +53,21 @@ class ErrorCheckingUpdatesNotification extends ObtainiumNotification {
|
||||
ErrorCheckingUpdatesNotification(String error)
|
||||
: super(
|
||||
5,
|
||||
'Error Checking for Updates',
|
||||
tr('errorCheckingUpdates'),
|
||||
error,
|
||||
'BG_UPDATE_CHECK_ERROR',
|
||||
'Error Checking for Updates',
|
||||
'A notification that shows when background update checking fails',
|
||||
tr('errorCheckingUpdates'),
|
||||
tr('errorCheckingUpdatesNotifDescription'),
|
||||
Importance.high);
|
||||
}
|
||||
|
||||
class AppsRemovedNotification extends ObtainiumNotification {
|
||||
AppsRemovedNotification(List<List<String>> namedReasons)
|
||||
: super(
|
||||
6,
|
||||
'Apps Removed',
|
||||
'',
|
||||
'APPS_REMOVED',
|
||||
'Apps Removed',
|
||||
'Notifies the user that one or more Apps were removed due to errors while loading them',
|
||||
Importance.max) {
|
||||
: super(6, tr('appsRemoved'), '', 'APPS_REMOVED', tr('appsRemoved'),
|
||||
tr('appsRemovedNotifDescription'), Importance.max) {
|
||||
message = '';
|
||||
for (var r in namedReasons) {
|
||||
message += '${r[0]} was removed due to this error: ${r[1]}. \n';
|
||||
message += '${tr('xWasRemovedDueToErrorY', args: [r[0], r[1]])} \n';
|
||||
}
|
||||
message = message.trim();
|
||||
}
|
||||
@@ -83,20 +75,20 @@ class AppsRemovedNotification extends ObtainiumNotification {
|
||||
|
||||
final completeInstallationNotification = ObtainiumNotification(
|
||||
1,
|
||||
'Complete App Installation',
|
||||
'Obtainium must be open to install Apps',
|
||||
tr('completeAppInstallation'),
|
||||
tr('obtainiumMustBeOpenToInstallApps'),
|
||||
'COMPLETE_INSTALL',
|
||||
'Complete App Installation',
|
||||
'Asks the user to return to Obtanium to finish installing an App',
|
||||
tr('completeAppInstallation'),
|
||||
tr('completeAppInstallationNotifDescription'),
|
||||
Importance.max);
|
||||
|
||||
final checkingUpdatesNotification = ObtainiumNotification(
|
||||
4,
|
||||
'Checking for Updates',
|
||||
tr('checkingForUpdates'),
|
||||
'',
|
||||
'BG_UPDATE_CHECK',
|
||||
'Checking for Updates',
|
||||
'Transient notification that appears when checking for updates',
|
||||
tr('checkingForUpdates'),
|
||||
tr('checkingForUpdatesNotifDescription'),
|
||||
Importance.min);
|
||||
|
||||
class NotificationsProvider {
|
||||
|
@@ -1,5 +1,6 @@
|
||||
// Exposes functions used to save/load app settings
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:obtainium/app_sources/github.dart';
|
||||
@@ -109,8 +110,7 @@ class SettingsProvider with ChangeNotifier {
|
||||
while (!(await Permission.requestInstallPackages.isGranted)) {
|
||||
// Explicit request as InstallPlugin request sometimes bugged
|
||||
Fluttertoast.showToast(
|
||||
msg: 'Please allow Obtainium to install Apps',
|
||||
toastLength: Toast.LENGTH_LONG);
|
||||
msg: tr('pleaseAllowInstallPerm'), toastLength: Toast.LENGTH_LONG);
|
||||
if ((await Permission.requestInstallPackages.request()) ==
|
||||
PermissionStatus.granted) {
|
||||
break;
|
||||
|
@@ -3,6 +3,7 @@
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:html/dom.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:obtainium/app_sources/apkmirror.dart';
|
||||
@@ -120,13 +121,6 @@ preStandardizeUrl(String url) {
|
||||
return url;
|
||||
}
|
||||
|
||||
const String couldNotFindReleases = 'Could not find a suitable release';
|
||||
const String couldNotFindLatestVersion =
|
||||
'Could not determine latest release version';
|
||||
String notValidURL(String sourceName) {
|
||||
return 'Not a valid $sourceName App URL';
|
||||
}
|
||||
|
||||
const String noAPKFound = 'No APK found';
|
||||
|
||||
List<String> getLinksFromParsedHTML(
|
||||
@@ -164,7 +158,7 @@ class AppSource {
|
||||
// Some additional data may be needed for Apps regardless of Source
|
||||
final List<GeneratedFormItem> additionalAppSpecificSourceAgnosticFormItems = [
|
||||
GeneratedFormItem(
|
||||
label: 'Track-Only',
|
||||
label: tr('trackOnly'),
|
||||
type: FormItemType.bool,
|
||||
key: 'trackOnlyFormItemKey')
|
||||
];
|
||||
@@ -192,8 +186,8 @@ class AppSource {
|
||||
}
|
||||
|
||||
ObtainiumError getObtainiumHttpError(Response res) {
|
||||
return ObtainiumError(
|
||||
res.reasonPhrase ?? 'Error ${res.statusCode.toString()}');
|
||||
return ObtainiumError(res.reasonPhrase ??
|
||||
tr('errorWithHttpStatusCode', args: [res.statusCode.toString()]));
|
||||
}
|
||||
|
||||
abstract class MassAppUrlSource {
|
||||
@@ -254,6 +248,7 @@ class SourceProvider {
|
||||
}
|
||||
for (int i = 0; i < parts.length - 1; i++) {
|
||||
if (RegExp('.*[A-Z].*').hasMatch(parts[i])) {
|
||||
// TODO: RegEx won't work for non-eng chars
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user