Initial support for localization

This commit is contained in:
Yannick Mauray 2021-11-11 02:03:56 +01:00
parent 72103e1b48
commit 6f6bc29a72
No known key found for this signature in database
GPG Key ID: 67C4AAC5E99CB909
13 changed files with 171 additions and 63 deletions

20
assets/i18n/en.po Normal file
View File

@ -0,0 +1,20 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2021-09-29 09:59+0200\n"
"PO-Revision-Date: 2021-09-29 10:58+0200\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.0\n"
"X-Poedit-Basepath: .\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
msgid "Main menu"
msgstr "Main menu"
msgid "Use dark mode"
msgstr "Use dark mode"

20
assets/i18n/fr.po Normal file
View File

@ -0,0 +1,20 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2021-09-29 09:59+0200\n"
"PO-Revision-Date: 2021-09-29 10:58+0200\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.0\n"
"X-Poedit-Basepath: .\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
msgid "Main menu"
msgstr "Menu principal"
msgid "Use dark mode"
msgstr "Utiliser le mode sombre"

20
assets/i18n/quickgui.pot Normal file
View File

@ -0,0 +1,20 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2021-09-29 09:59+0200\n"
"PO-Revision-Date: 2021-09-29 10:00+0200\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.0\n"
"X-Poedit-Basepath: .\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Language: en\n"
msgid "Main menu"
msgstr ""
msgid "Use dark mode"
msgstr ""

View File

@ -52,10 +52,12 @@ void main() async {
setWindowMaxSize(const Size(692, 580));
gOperatingSystems = await loadOperatingSystems(false);
AppVersion.packageInfo = await PackageInfo.fromPlatform();
runApp(MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => AppTheme()),
],
builder: (context, _) => const App(),
));
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => AppTheme()),
],
builder: (context, _) => const App(),
),
);
}

View File

@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:provider/provider.dart';
import 'package:quickgui/src/globals.dart';
import 'package:quickgui/src/i18n/quickgui_localizations_delegate.dart';
import 'package:quickgui/src/mixins/preferences_mixin.dart';
import 'package:quickgui/src/model/app_theme.dart';
import 'package:quickgui/src/pages/main_page.dart';
@ -20,7 +22,7 @@ class _AppState extends State<App> with PreferencesMixin {
builder: (context, AsyncSnapshot<bool?> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data != null) {
context.read<AppTheme>().useDarkMode = snapshot.data!;
context.read<AppTheme>().useDarkModeSilently = snapshot.data!;
}
return Consumer<AppTheme>(
builder: (context, appTheme, _) => MaterialApp(
@ -28,6 +30,33 @@ class _AppState extends State<App> with PreferencesMixin {
darkTheme: ThemeData.dark(),
themeMode: appTheme.themeMode,
home: const MainPage(title: 'Quickgui - A Flutter frontend for Quickget and Quickemu'),
supportedLocales: const [
/// List of locales we have translations for.
Locale('en', ''),
Locale('fr', ''),
Locale('fr', 'CH'),
],
localizationsDelegates: [
QuickguiLocalizationsDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
localeListResolutionCallback: (locales, supportedLocales) {
if (locales != null) {
for (var locale in locales) {
var supportedLocale =
supportedLocales.where((element) => element.languageCode == locale.languageCode && element.countryCode == locale.countryCode);
if (supportedLocale.isNotEmpty) {
return supportedLocale.first;
}
supportedLocale = supportedLocales.where((element) => element.languageCode == locale.languageCode);
if (supportedLocale.isNotEmpty) {
return supportedLocale.first;
}
}
}
return null;
},
),
);
} else {

View File

@ -0,0 +1,6 @@
import 'package:flutter/material.dart';
import 'package:quickgui/src/i18n/quickgui_localizations.dart';
extension I18nExt on BuildContext {
t(String key) => QuickguiLocalizations.of(this).t(key);
}

View File

@ -0,0 +1,28 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:gettext/gettext.dart';
import 'package:gettext_parser/gettext_parser.dart' as gettext_parser;
class QuickguiLocalizations {
final _gt = Gettext(
onWarning: ((message) {
if (kDebugMode) {
// ignore: avoid_print
print('$message\n');
final r = RegExp(r'^No translation was found for msgid "(.*)" in msgctxt "(.*)" and domain "(.*)"$');
final matches = r.firstMatch(message);
var msgid = matches!.group(1);
// ignore: avoid_print
print('\nmsgid "$msgid"\nmsgstr ""\n \n');
}
}),
);
QuickguiLocalizations.fromPO(String poContent) {
_gt.addLocale(gettext_parser.po.parse(poContent));
}
static QuickguiLocalizations of(BuildContext context) => Localizations.of<QuickguiLocalizations>(context, QuickguiLocalizations)!;
String t(String key) => _gt.gettext(key);
}

View File

@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:quickgui/src/i18n/quickgui_localizations.dart';
class QuickguiLocalizationsDelegate extends LocalizationsDelegate<QuickguiLocalizations> {
@override
bool isSupported(Locale locale) => ['fr', 'en'].contains(locale.languageCode);
@override
Future<QuickguiLocalizations> load(Locale locale) async {
var poContent = '';
try {
poContent = await rootBundle.loadString('assets/i18n/${locale.languageCode}_${locale.countryCode}.po');
} catch (e) {
poContent = await rootBundle.loadString('assets/i18n/${locale.languageCode}.po');
}
return QuickguiLocalizations.fromPO(poContent);
}
@override
bool shouldReload(covariant LocalizationsDelegate<QuickguiLocalizations> old) => true;
}

View File

@ -4,8 +4,13 @@ class AppTheme extends ChangeNotifier {
ThemeMode? _themeMode;
ThemeMode get themeMode => _themeMode ?? ThemeMode.system;
set useDarkMode(bool useDarkMode) {
set useDarkModeSilently(bool useDarkMode) {
_themeMode = useDarkMode ? ThemeMode.dark : ThemeMode.light;
}
set useDarkMode(bool useDarkMode) {
useDarkModeSilently = useDarkMode;
notifyListeners();
}
}

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:quickgui/src/widgets/home_page/logo.dart';
import 'package:quickgui/src/widgets/home_page/main_menu.dart';
import 'package:quickgui/src/widgets/left_menu.dart';
import 'package:quickgui/src/i18n/i18n_ext.dart';
class MainPage extends StatefulWidget {
const MainPage({Key? key, required this.title}) : super(key: key);
@ -17,7 +18,7 @@ class _MainPageState extends State<MainPage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Main menu'),
title: Text(context.t('Main menu')),
),
drawer: const LeftMenu(),
body: Column(

View File

@ -5,6 +5,7 @@ import 'package:quickgui/src/globals.dart';
import 'package:quickgui/src/mixins/app_version.dart';
import 'package:quickgui/src/mixins/preferences_mixin.dart';
import 'package:quickgui/src/model/app_theme.dart';
import 'package:quickgui/src/i18n/i18n_ext.dart';
class LeftMenu extends StatelessWidget with PreferencesMixin {
const LeftMenu({Key? key}) : super(key: key);
@ -24,7 +25,7 @@ class LeftMenu extends StatelessWidget with PreferencesMixin {
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
const Text('Use dark mode'),
Text(context.t('Use dark mode')),
Switch(
value: Theme.of(context).brightness == Brightness.dark,
onChanged: (value) {

View File

@ -45,6 +45,11 @@ dependencies:
shared_preferences: ^2.0.8
package_info_plus: ^1.3.0
provider: ^6.0.1
intl: ^0.17.0
gettext: ^1.2.0
gettext_parser: ^0.2.0
flutter_localizations:
sdk: flutter
dev_dependencies:
flutter_test:
@ -74,6 +79,7 @@ flutter:
# - images/a_dot_ham.jpeg
assets:
- assets/images/
- assets/i18n/
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.

View File

@ -1,53 +0,0 @@
name: quickgui
adopt-info: quickgui
summary: a Flutter frontend for (quickget)[https://github.com/wimpysworld/quickget]
description: |
The app is a frontend to quickget.
grade: stable
confinement: classic
base: core20
architectures:
- build-on: amd64
apps:
quickgui:
command: bin/quickgui
environment:
PATH: $SNAP/usr/bin:$SNAP/bin:$PATH
LIVE_RUN: 1
LOG_LEVEL: debug
parts:
quickgui-deps:
plugin: nil
stage-packages:
- libatk1.0-0
- libcairo-gobject2
- libcairo2
- libepoxy0
- libgtk-3-0
- libpango-1.0-0
- libpangocairo-1.0-0
quickgui:
source: .
source-type: git
plugin: nil
override-pull: |
snapcraftctl pull
snapcraftctl set-version "$(cat pubspec.yaml | grep '^version: ' | cut -c 10- | sed 's/+/-/')"
override-build: |
set -eux
mkdir -p $SNAPCRAFT_PART_INSTALL/bin/lib
flutter channel stable
flutter upgrade
flutter config --enable-linux-desktop
flutter doctor
flutter pub get
flutter build linux --release -v
cp -r build/linux/x64/release/bundle/* $SNAPCRAFT_PART_INSTALL/bin/
build-snaps:
- flutter/latest/stable
after:
- quickgui-deps