l10n: typography fixes for arabic/persian
This commit is contained in:
parent
ea53420c17
commit
dd6258d8ac
18 changed files with 108 additions and 53 deletions
3
devtools_options.yaml
Normal file
3
devtools_options.yaml
Normal file
|
@ -0,0 +1,3 @@
|
|||
description: This file stores settings for Dart & Flutter DevTools.
|
||||
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
|
||||
extensions:
|
|
@ -11,6 +11,7 @@ import 'package:aves/services/common/services.dart';
|
|||
import 'package:aves/theme/text.dart';
|
||||
import 'package:aves/utils/android_file_utils.dart';
|
||||
import 'package:aves/utils/time_utils.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
extension ExtraAvesEntryProps on AvesEntry {
|
||||
bool get isValid => !isMissingAtPath && sizeBytes != 0 && width > 0 && height > 0;
|
||||
|
@ -51,9 +52,10 @@ extension ExtraAvesEntryProps on AvesEntry {
|
|||
|
||||
// text
|
||||
|
||||
String get resolutionText {
|
||||
final ws = width;
|
||||
final hs = height;
|
||||
String getResolutionText(String locale) {
|
||||
final numberFormat = NumberFormat('0', locale);
|
||||
final ws = numberFormat.format(width);
|
||||
final hs = numberFormat.format(height);
|
||||
return isRotated ? '$hs${AText.resolutionSeparator}$ws' : '$ws${AText.resolutionSeparator}$hs';
|
||||
}
|
||||
|
||||
|
|
|
@ -47,10 +47,11 @@ extension ExtraCoordinateFormat on CoordinateFormat {
|
|||
final min = minDecimal.toInt();
|
||||
final sec = (minDecimal - min) * 60;
|
||||
|
||||
var minText = NumberFormat('0' * (minuteSecondPadding ? 2 : 1), locale).format(min);
|
||||
var secText = NumberFormat('${'0' * (minuteSecondPadding ? 2 : 1)}${secondDecimals > 0 ? '.${'0' * secondDecimals}' : ''}', locale).format(sec);
|
||||
final degText = NumberFormat('0', locale).format(deg);
|
||||
final minText = NumberFormat('0' * (minuteSecondPadding ? 2 : 1), locale).format(min);
|
||||
final secText = NumberFormat('${'0' * (minuteSecondPadding ? 2 : 1)}${secondDecimals > 0 ? '.${'0' * secondDecimals}' : ''}', locale).format(sec);
|
||||
|
||||
return '$deg° $minText′ $secText″';
|
||||
return '$degText° $minText′ $secText″';
|
||||
}
|
||||
|
||||
static List<String> _toDecimal(AppLocalizations l10n, LatLng latLng) {
|
||||
|
|
|
@ -45,3 +45,13 @@ bool shouldUseNativeDigits(Locale? countrifiedLocale) {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool canHaveLetterSpacing(String locale) {
|
||||
switch (locale) {
|
||||
case 'ar':
|
||||
case 'fa':
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
import 'package:aves/model/device.dart';
|
||||
import 'package:aves/ref/locales.dart';
|
||||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/widgets/about/policy_page.dart';
|
||||
import 'package:aves/widgets/common/basic/link_chip.dart';
|
||||
|
@ -10,13 +10,6 @@ import 'package:flutter/material.dart';
|
|||
class AppReference extends StatelessWidget {
|
||||
static const avesGithub = 'https://github.com/deckerst/aves';
|
||||
|
||||
static const _appTitleStyle = TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.normal,
|
||||
letterSpacing: 1.0,
|
||||
fontFeatures: [FontFeature.enable('smcp')],
|
||||
);
|
||||
|
||||
const AppReference({super.key});
|
||||
|
||||
@override
|
||||
|
@ -38,27 +31,35 @@ class AppReference extends StatelessWidget {
|
|||
}
|
||||
|
||||
Widget _buildAvesLine(BuildContext context) {
|
||||
final locale = context.l10n.localeName;
|
||||
final textScaler = MediaQuery.textScalerOf(context);
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
AvesLogo(
|
||||
size: textScaler.scale(_appTitleStyle.fontSize!) * 1.3,
|
||||
size: textScaler.scale(_getAppTitleStyle(locale).fontSize!) * 1.3,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
context.l10n.appName,
|
||||
style: _appTitleStyle,
|
||||
style: _getAppTitleStyle(locale),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
device.packageVersion,
|
||||
style: _appTitleStyle,
|
||||
style: _getAppTitleStyle(locale),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
TextStyle _getAppTitleStyle(String locale) => TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.normal,
|
||||
letterSpacing: canHaveLetterSpacing(locale) ? 1 : 0,
|
||||
fontFeatures: const [FontFeature.enable('smcp')],
|
||||
);
|
||||
|
||||
static List<Widget> buildLinks(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
return [
|
||||
|
|
|
@ -122,6 +122,9 @@ class _BugReportState extends State<BugReport> with FeedbackMixin {
|
|||
}
|
||||
|
||||
Widget _buildStep(int step, String text, String buttonText, VoidCallback onPressed) {
|
||||
final locale = context.l10n.localeName;
|
||||
final numberFormat = NumberFormat.decimalPattern(locale);
|
||||
|
||||
final isMonochrome = settings.themeColorMode == AvesThemeColorMode.monochrome;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
|
@ -136,7 +139,7 @@ class _BugReportState extends State<BugReport> with FeedbackMixin {
|
|||
)),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Text('$step'),
|
||||
child: Text(numberFormat.format(step)),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(child: Text(text)),
|
||||
|
|
|
@ -218,6 +218,9 @@ class _ReportOverlayState<T> extends State<ReportOverlay<T>> with SingleTickerPr
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final locale = context.l10n.localeName;
|
||||
final percentFormat = NumberFormat.percentPattern(locale);
|
||||
|
||||
final theme = Theme.of(context);
|
||||
final colorScheme = theme.colorScheme;
|
||||
final progressColor = colorScheme.primary;
|
||||
|
@ -230,7 +233,6 @@ class _ReportOverlayState<T> extends State<ReportOverlay<T>> with SingleTickerPr
|
|||
final processedCount = processed.length.toDouble();
|
||||
final total = widget.itemCount;
|
||||
final percent = total == null || total == 0 ? 0.0 : min(1.0, processedCount / total);
|
||||
final percentFormat = NumberFormat.percentPattern();
|
||||
return FadeTransition(
|
||||
opacity: _animation,
|
||||
child: Stack(
|
||||
|
@ -351,6 +353,9 @@ class _FeedbackMessageState extends State<_FeedbackMessage> with SingleTickerPro
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final locale = context.l10n.localeName;
|
||||
final numberFormat = NumberFormat('0', locale);
|
||||
|
||||
final textScaler = MediaQuery.textScalerOf(context);
|
||||
final theme = Theme.of(context);
|
||||
final colorScheme = theme.colorScheme;
|
||||
|
@ -388,7 +393,7 @@ class _FeedbackMessageState extends State<_FeedbackMessage> with SingleTickerPro
|
|||
// because we cannot use the app context theme here
|
||||
foreground: widget.progressColor,
|
||||
center: ChangeHighlightText(
|
||||
'${(remainingDurationMillis / 1000).ceil()}',
|
||||
numberFormat.format((remainingDurationMillis / 1000).ceil()),
|
||||
style: contentTextStyle.copyWith(
|
||||
shadows: [
|
||||
Shadow(
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
|
||||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/ref/locales.dart';
|
||||
import 'package:aves/theme/colors.dart';
|
||||
import 'package:aves/theme/themes.dart';
|
||||
import 'package:aves/widgets/common/basic/text/outlined.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:aves/widgets/common/extensions/theme.dart';
|
||||
import 'package:aves/widgets/common/fx/highlight_decoration.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
|
@ -40,7 +41,7 @@ class HighlightTitle extends StatelessWidget {
|
|||
final style = TextStyle(
|
||||
shadows: shadows(context),
|
||||
fontSize: fontSize,
|
||||
letterSpacing: 1.0,
|
||||
letterSpacing: canHaveLetterSpacing(context.l10n.localeName) ? 1 : 0,
|
||||
fontFeatures: const [FontFeature.enable('smcp')],
|
||||
);
|
||||
|
||||
|
|
|
@ -146,9 +146,11 @@ class _GeoMapState extends State<GeoMap> {
|
|||
selector: (context, s) => s.mapStyle,
|
||||
builder: (context, mapStyle, child) {
|
||||
final isHeavy = ExtraEntryMapStyle.isHeavy(mapStyle);
|
||||
final locale = context.l10n.localeName;
|
||||
Widget _buildMarkerWidget(MarkerKey<AvesEntry> key) => ImageMarker(
|
||||
key: key,
|
||||
count: key.count,
|
||||
locale: locale,
|
||||
buildThumbnailImage: (extent) => ThumbnailImage(
|
||||
entry: key.entry,
|
||||
extent: extent,
|
||||
|
@ -482,9 +484,11 @@ class _GeoMapState extends State<GeoMap> {
|
|||
} else {
|
||||
markerEntry = geoEntry.entry!;
|
||||
}
|
||||
final locale = context.l10n.localeName;
|
||||
final markerLocation = LatLng(geoEntry.latitude!, geoEntry.longitude!);
|
||||
Widget markerBuilder(BuildContext context) => ImageMarker(
|
||||
count: geoEntry.pointsSize,
|
||||
locale: locale,
|
||||
drawArrow: false,
|
||||
buildThumbnailImage: (extent) => ThumbnailImage(
|
||||
entry: markerEntry,
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import 'package:aves/model/filters/album.dart';
|
||||
import 'package:aves/model/filters/trash.dart';
|
||||
import 'package:aves/model/settings/settings.dart';
|
||||
|
@ -8,6 +7,7 @@ import 'package:aves/model/source/collection_source.dart';
|
|||
import 'package:aves/model/source/location/country.dart';
|
||||
import 'package:aves/model/source/location/place.dart';
|
||||
import 'package:aves/model/source/tag.dart';
|
||||
import 'package:aves/ref/locales.dart';
|
||||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/utils/android_file_utils.dart';
|
||||
|
@ -111,6 +111,9 @@ class _AppDrawerState extends State<AppDrawer> {
|
|||
}
|
||||
|
||||
Widget _buildHeader(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
final locale = l10n.localeName;
|
||||
|
||||
Future<void> goTo(String routeName, WidgetBuilder pageBuilder) async {
|
||||
Navigator.maybeOf(context)?.pop();
|
||||
await Future.delayed(ADurations.drawerTransitionAnimation);
|
||||
|
@ -145,13 +148,13 @@ class _AppDrawerState extends State<AppDrawer> {
|
|||
OutlinedText(
|
||||
textSpans: [
|
||||
TextSpan(
|
||||
text: context.l10n.appName,
|
||||
style: const TextStyle(
|
||||
text: l10n.appName,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 38,
|
||||
fontWeight: FontWeight.w300,
|
||||
letterSpacing: 1.0,
|
||||
fontFeatures: [FontFeature.enable('smcp')],
|
||||
letterSpacing: canHaveLetterSpacing(locale) ? 1 : 0,
|
||||
fontFeatures: const [FontFeature.enable('smcp')],
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -177,7 +180,7 @@ class _AppDrawerState extends State<AppDrawer> {
|
|||
onPressed: () => goTo(AboutPage.routeName, (_) => const AboutPage()),
|
||||
style: drawerButtonStyle,
|
||||
icon: const Icon(AIcons.info),
|
||||
label: Text(context.l10n.drawerAboutButton),
|
||||
label: Text(l10n.drawerAboutButton),
|
||||
),
|
||||
OutlinedButton.icon(
|
||||
// key is expected by test driver
|
||||
|
@ -185,7 +188,7 @@ class _AppDrawerState extends State<AppDrawer> {
|
|||
onPressed: () => goTo(SettingsPage.routeName, (_) => const SettingsPage()),
|
||||
style: drawerButtonStyle,
|
||||
icon: const Icon(AIcons.settings),
|
||||
label: Text(context.l10n.drawerSettingsButton),
|
||||
label: Text(l10n.drawerSettingsButton),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:aves/model/settings/enums/home_page.dart';
|
|||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/model/source/collection_lens.dart';
|
||||
import 'package:aves/model/source/collection_source.dart';
|
||||
import 'package:aves/ref/locales.dart';
|
||||
import 'package:aves/widgets/about/about_page.dart';
|
||||
import 'package:aves/widgets/collection/collection_page.dart';
|
||||
import 'package:aves/widgets/common/basic/insets.dart';
|
||||
|
@ -90,6 +91,9 @@ class _TvRailState extends State<TvRail> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
final locale = l10n.localeName;
|
||||
|
||||
final navEntries = _getNavEntries(context);
|
||||
return DirectionalSafeArea(
|
||||
end: false,
|
||||
|
@ -103,13 +107,13 @@ class _TvRailState extends State<TvRail> {
|
|||
logo,
|
||||
const SizedBox(width: 16),
|
||||
Text(
|
||||
context.l10n.appName,
|
||||
style: const TextStyle(
|
||||
l10n.appName,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.w300,
|
||||
letterSpacing: 1.0,
|
||||
fontFeatures: [FontFeature.enable('smcp')],
|
||||
letterSpacing: canHaveLetterSpacing(locale) ? 1 : 0,
|
||||
fontFeatures: const [FontFeature.enable('smcp')],
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
@ -1,21 +1,24 @@
|
|||
import 'package:aves/theme/styles.dart';
|
||||
import 'package:aves/theme/themes.dart';
|
||||
import 'package:aves/widgets/common/basic/text/outlined.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:aves/widgets/common/extensions/theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class LinearPercentIndicatorText extends StatelessWidget {
|
||||
final double percent;
|
||||
final percentFormat = NumberFormat.percentPattern();
|
||||
|
||||
LinearPercentIndicatorText({
|
||||
const LinearPercentIndicatorText({
|
||||
super.key,
|
||||
required this.percent,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final locale = context.l10n.localeName;
|
||||
final percentFormat = NumberFormat.percentPattern(locale);
|
||||
|
||||
return OutlinedText(
|
||||
textSpans: [
|
||||
TextSpan(
|
||||
|
|
|
@ -32,6 +32,7 @@ import 'package:aves_model/aves_model.dart';
|
|||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class BasicSection extends StatefulWidget {
|
||||
|
@ -277,15 +278,6 @@ class _BasicInfoState extends State<_BasicInfo> {
|
|||
|
||||
AvesEntry get entry => widget.entry;
|
||||
|
||||
int get megaPixels => (entry.width * entry.height / 1000000).round();
|
||||
|
||||
// guess whether this is a photo, according to file type
|
||||
bool get isPhoto => [MimeTypes.heic, MimeTypes.heif, MimeTypes.jpeg, MimeTypes.tiff].contains(entry.mimeType) || entry.isRaw;
|
||||
|
||||
bool get showMegaPixels => isPhoto && megaPixels > 0;
|
||||
|
||||
String get rasterResolutionText => '${entry.resolutionText}${showMegaPixels ? ' • $megaPixels MP' : ''}';
|
||||
|
||||
static const ownerPackageNamePropKey = 'owner_package_name';
|
||||
static const iconSize = 20.0;
|
||||
|
||||
|
@ -331,7 +323,7 @@ class _BasicInfoState extends State<_BasicInfo> {
|
|||
l10n.viewerInfoLabelTitle: title,
|
||||
l10n.viewerInfoLabelDate: dateText,
|
||||
if (entry.isVideo) ..._buildVideoRows(context),
|
||||
if (showResolution) l10n.viewerInfoLabelResolution: context.applyDirectionality(rasterResolutionText),
|
||||
if (showResolution) l10n.viewerInfoLabelResolution: context.applyDirectionality(getRasterResolutionText(locale)),
|
||||
l10n.viewerInfoLabelSize: context.applyDirectionality(sizeText),
|
||||
if (!entry.trashed) l10n.viewerInfoLabelUri: entry.uri,
|
||||
if (path != null) l10n.viewerInfoLabelPath: path,
|
||||
|
@ -384,4 +376,20 @@ class _BasicInfoState extends State<_BasicInfo> {
|
|||
),
|
||||
];
|
||||
}
|
||||
|
||||
String getRasterResolutionText(String locale) {
|
||||
var s = entry.getResolutionText(locale);
|
||||
|
||||
// guess whether this is a photo, according to file type
|
||||
final isPhoto = [MimeTypes.heic, MimeTypes.heif, MimeTypes.jpeg, MimeTypes.tiff].contains(entry.mimeType) || entry.isRaw;
|
||||
if (isPhoto) {
|
||||
final numberFormat = NumberFormat('0', locale);
|
||||
final megaPixels = (entry.width * entry.height / 1000000).round();
|
||||
if (megaPixels > 0) {
|
||||
s += ' • ${numberFormat.format(megaPixels)} MP';
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,10 +69,13 @@ class _ColorSectionSliverState extends State<ColorSectionSliver> {
|
|||
children: [
|
||||
ColorIndicator(value: v.color),
|
||||
const SizedBox(width: 8),
|
||||
SelectableText(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: SelectableText(
|
||||
'#${v.color.hex}',
|
||||
style: const TextStyle(fontFamily: 'monospace'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -29,7 +29,7 @@ class OverlayDateRow extends StatelessWidget {
|
|||
final resolutionText = entry.isSvg
|
||||
? entry.aspectRatioText
|
||||
: entry.isSized
|
||||
? entry.resolutionText
|
||||
? entry.getResolutionText(locale)
|
||||
: '';
|
||||
|
||||
return Row(
|
||||
|
|
|
@ -4,10 +4,12 @@ import 'package:collection/collection.dart';
|
|||
import 'package:custom_rounded_rectangle_border/custom_rounded_rectangle_border.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:intl/intl.dart' as intl;
|
||||
import 'package:latlong2/latlong.dart';
|
||||
|
||||
class ImageMarker extends StatelessWidget {
|
||||
final int? count;
|
||||
final intl.NumberFormat numberFormat;
|
||||
final bool drawArrow;
|
||||
final Widget Function(double extent) buildThumbnailImage;
|
||||
|
||||
|
@ -20,12 +22,13 @@ class ImageMarker extends StatelessWidget {
|
|||
static const innerRadius = Radius.circular(outerBorderRadiusDim - outerBorderWidth);
|
||||
static const innerBorderRadius = BorderRadius.all(innerRadius);
|
||||
|
||||
const ImageMarker({
|
||||
ImageMarker({
|
||||
super.key,
|
||||
required this.count,
|
||||
required String locale,
|
||||
this.drawArrow = true,
|
||||
required this.buildThumbnailImage,
|
||||
});
|
||||
}) : numberFormat = intl.NumberFormat.decimalPattern(locale);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -107,7 +110,7 @@ class ImageMarker extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
child: Text(
|
||||
'$count',
|
||||
numberFormat.format(count),
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: theme.colorScheme.onPrimary,
|
||||
|
|
|
@ -102,7 +102,7 @@ packages:
|
|||
source: hosted
|
||||
version: "4.0.2"
|
||||
intl:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: intl
|
||||
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
|
||||
|
|
|
@ -15,6 +15,7 @@ dependencies:
|
|||
equatable:
|
||||
fluster:
|
||||
flutter_map:
|
||||
intl:
|
||||
latlong2:
|
||||
provider:
|
||||
|
||||
|
|
Loading…
Reference in a new issue