1
+ import 'dart:ui' ;
2
+
1
3
import 'package:drift/drift.dart' ;
2
4
import 'package:drift/internal/versioned_schema.dart' ;
3
5
import 'package:drift/remote.dart' ;
@@ -24,6 +26,8 @@ class GlobalSettings extends Table {
24
26
Column <String > get browserPreference => textEnum <BrowserPreference >()
25
27
.nullable ()();
26
28
29
+ Column <String > get language => text ().map (const LocaleConverter ()).nullable ()();
30
+
27
31
// If adding a new column to this table, consider whether [BoolGlobalSettings]
28
32
// can do the job instead (by adding a value to the [BoolGlobalSetting] enum).
29
33
// That way is more convenient, when it works, because
@@ -119,7 +123,7 @@ class AppDatabase extends _$AppDatabase {
119
123
// information on using the build_runner.
120
124
// * Write a migration in `_migrationSteps` below.
121
125
// * Write tests.
122
- static const int latestSchemaVersion = 6 ; // See note.
126
+ static const int latestSchemaVersion = 7 ; // See note.
123
127
124
128
@override
125
129
int get schemaVersion => latestSchemaVersion;
@@ -174,6 +178,9 @@ class AppDatabase extends _$AppDatabase {
174
178
from5To6: (m, schema) async {
175
179
await m.createTable (schema.boolGlobalSettings);
176
180
},
181
+ from6To7: (m, schema) async {
182
+ await m.addColumn (schema.globalSettings, schema.globalSettings.language);
183
+ }
177
184
);
178
185
179
186
Future <void > _createLatestSchema (Migrator m) async {
@@ -243,3 +250,55 @@ class AppDatabase extends _$AppDatabase {
243
250
}
244
251
245
252
class AccountAlreadyExistsException implements Exception {}
253
+
254
+ class LocaleConverter extends TypeConverter <Locale , String > {
255
+ const LocaleConverter ();
256
+
257
+ /// Parse a Unicode BCP 47 Language Identifier into [Locale] .
258
+ ///
259
+ /// Throw when it fails to convert [languageTag] into a [Locale] .
260
+ ///
261
+ /// This supports parsing a Unicode Language Identifier returned from
262
+ /// [Locale.toLanguageTag] .
263
+ ///
264
+ /// This implementation refers to a part of
265
+ /// [this EBNF grammar] (https://www.unicode.org/reports/tr35/#Unicode_language_identifier),
266
+ /// assuming the identifier is valid without
267
+ /// [unicode_variant_subtag] (https://www.unicode.org/reports/tr35/#unicode_variant_subtag).
268
+ ///
269
+ /// This doesn't check if the [languageTag] is a valid identifier, (i.e., when
270
+ /// this returns without errors, the identifier is not necessarily
271
+ /// syntactically well-formed or valid).
272
+ // TODO(upstream): send this as a factory Locale.fromLanguageTag
273
+ // https://github.com/flutter/flutter/issues/143491
274
+ Locale _fromLanguageTag (String languageTag) {
275
+ final subtags = languageTag.replaceAll ('_' , '-' ).split ('-' );
276
+
277
+ return switch (subtags) {
278
+ [final language, final script, final region] =>
279
+ Locale .fromSubtags (
280
+ languageCode: language, scriptCode: script, countryCode: region),
281
+
282
+ [final language, final script] when script.length == 4 =>
283
+ Locale .fromSubtags (languageCode: language, scriptCode: script),
284
+
285
+ [final language, final region] =>
286
+ Locale (language, region),
287
+
288
+ [final language] =>
289
+ Locale (language),
290
+
291
+ _ => throw ArgumentError .value (languageTag, 'languageTag' ),
292
+ };
293
+ }
294
+
295
+ @override
296
+ Locale fromSql (String fromDb) {
297
+ return _fromLanguageTag (fromDb);
298
+ }
299
+
300
+ @override
301
+ String toSql (Locale value) {
302
+ return value.toLanguageTag ();
303
+ }
304
+ }
0 commit comments