|
| 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';
|
@@ -30,6 +32,9 @@ class GlobalSettings extends Table {
|
30 | 32 | Column<String> get markReadOnScroll => textEnum<MarkReadOnScrollSetting>()
|
31 | 33 | .nullable()();
|
32 | 34 |
|
| 35 | + Column<String> get language => text().map(const LocaleConverter()) |
| 36 | + .nullable()(); |
| 37 | + |
33 | 38 | // If adding a new column to this table, consider whether [BoolGlobalSettings]
|
34 | 39 | // can do the job instead (by adding a value to the [BoolGlobalSetting] enum).
|
35 | 40 | // That way is more convenient, when it works, because
|
@@ -125,7 +130,7 @@ class AppDatabase extends _$AppDatabase {
|
125 | 130 | // information on using the build_runner.
|
126 | 131 | // * Write a migration in `_migrationSteps` below.
|
127 | 132 | // * Write tests.
|
128 |
| - static const int latestSchemaVersion = 8; // See note. |
| 133 | + static const int latestSchemaVersion = 9; // See note. |
129 | 134 |
|
130 | 135 | @override
|
131 | 136 | int get schemaVersion => latestSchemaVersion;
|
@@ -188,6 +193,9 @@ class AppDatabase extends _$AppDatabase {
|
188 | 193 | await m.addColumn(schema.globalSettings,
|
189 | 194 | schema.globalSettings.markReadOnScroll);
|
190 | 195 | },
|
| 196 | + from8To9: (m, schema) async { |
| 197 | + await m.addColumn(schema.globalSettings, schema.globalSettings.language); |
| 198 | + } |
191 | 199 | );
|
192 | 200 |
|
193 | 201 | Future<void> _createLatestSchema(Migrator m) async {
|
@@ -257,3 +265,55 @@ class AppDatabase extends _$AppDatabase {
|
257 | 265 | }
|
258 | 266 |
|
259 | 267 | class AccountAlreadyExistsException implements Exception {}
|
| 268 | + |
| 269 | +class LocaleConverter extends TypeConverter<Locale, String> { |
| 270 | + const LocaleConverter(); |
| 271 | + |
| 272 | + /// Parse a Unicode BCP 47 Language Identifier into [Locale]. |
| 273 | + /// |
| 274 | + /// Throw when it fails to convert [languageTag] into a [Locale]. |
| 275 | + /// |
| 276 | + /// This supports parsing a Unicode Language Identifier returned from |
| 277 | + /// [Locale.toLanguageTag]. |
| 278 | + /// |
| 279 | + /// This implementation refers to a part of |
| 280 | + /// [this EBNF grammar](https://www.unicode.org/reports/tr35/#Unicode_language_identifier), |
| 281 | + /// assuming the identifier is valid without |
| 282 | + /// [unicode_variant_subtag](https://www.unicode.org/reports/tr35/#unicode_variant_subtag). |
| 283 | + /// |
| 284 | + /// This doesn't check if the [languageTag] is a valid identifier, (i.e., when |
| 285 | + /// this returns without errors, the identifier is not necessarily |
| 286 | + /// syntactically well-formed or valid). |
| 287 | + // TODO(upstream): send this as a factory Locale.fromLanguageTag |
| 288 | + // https://github.com/flutter/flutter/issues/143491 |
| 289 | + Locale _fromLanguageTag(String languageTag) { |
| 290 | + final subtags = languageTag.replaceAll('_', '-').split('-'); |
| 291 | + |
| 292 | + return switch (subtags) { |
| 293 | + [final language, final script, final region] => |
| 294 | + Locale.fromSubtags( |
| 295 | + languageCode: language, scriptCode: script, countryCode: region), |
| 296 | + |
| 297 | + [final language, final script] when script.length == 4 => |
| 298 | + Locale.fromSubtags(languageCode: language, scriptCode: script), |
| 299 | + |
| 300 | + [final language, final region] => |
| 301 | + Locale(language, region), |
| 302 | + |
| 303 | + [final language] => |
| 304 | + Locale(language), |
| 305 | + |
| 306 | + _ => throw ArgumentError.value(languageTag, 'languageTag'), |
| 307 | + }; |
| 308 | + } |
| 309 | + |
| 310 | + @override |
| 311 | + Locale fromSql(String fromDb) { |
| 312 | + return _fromLanguageTag(fromDb); |
| 313 | + } |
| 314 | + |
| 315 | + @override |
| 316 | + String toSql(Locale value) { |
| 317 | + return value.toLanguageTag(); |
| 318 | + } |
| 319 | +} |
0 commit comments