chore: adopt Dart 3.9 toolchain — honest floor + tall-style reformat (T-353)
Raise the declared minimums in pubspec.yaml to what our deps already require: Flutter >=3.35.0 / Dart >=3.9.0 (was 3.19.0 / 3.5.0). alchemist 0.12 needs Flutter 3.32; Dart 3.9 first ships in Flutter 3.35, so 3.35 is the binding floor. Pin the exact build toolchain in .fvmrc (Flutter 3.44.1). Moving to the Dart 3.9 language level switches `dart format` to the new "tall" style and enables two new lints. This commit is the resulting mechanical churn, isolated from any behaviour change: - whole-tree `dart format` reformat (tall style) - `dart fix` for unnecessary_underscores + use_null_aware_elements No runtime behaviour change; `make test` green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -29,9 +29,7 @@ class _MapAssetBundle extends CachingAssetBundle {
|
||||
void main() {
|
||||
group('AssetCatalogLoader', () {
|
||||
test('returns parsed JSON for a present asset', () async {
|
||||
final bundle = _MapAssetBundle({
|
||||
'lib/kernel/src/i18n/catalog/welcome_en_us.json': '{"title":{"translation":"Hi"}}',
|
||||
});
|
||||
final bundle = _MapAssetBundle({'lib/kernel/src/i18n/catalog/welcome_en_us.json': '{"title":{"translation":"Hi"}}'});
|
||||
final loader = AssetCatalogLoader(bundle: bundle);
|
||||
final r = await loader.load('welcome', const Locale('en', 'US'));
|
||||
expect(r['title'], isA<Map>());
|
||||
@@ -44,25 +42,19 @@ void main() {
|
||||
});
|
||||
|
||||
test('returns an empty map on malformed JSON (FormatException catch)', () async {
|
||||
final bundle = _MapAssetBundle({
|
||||
'lib/kernel/src/i18n/catalog/welcome_en_us.json': 'not json at all',
|
||||
});
|
||||
final bundle = _MapAssetBundle({'lib/kernel/src/i18n/catalog/welcome_en_us.json': 'not json at all'});
|
||||
final loader = AssetCatalogLoader(bundle: bundle);
|
||||
expect(await loader.load('welcome', const Locale('en', 'US')), isEmpty);
|
||||
});
|
||||
|
||||
test('returns an empty map when the asset is blank', () async {
|
||||
final bundle = _MapAssetBundle({
|
||||
'lib/kernel/src/i18n/catalog/welcome_en_us.json': ' \n',
|
||||
});
|
||||
final bundle = _MapAssetBundle({'lib/kernel/src/i18n/catalog/welcome_en_us.json': ' \n'});
|
||||
final loader = AssetCatalogLoader(bundle: bundle);
|
||||
expect(await loader.load('welcome', const Locale('en', 'US')), isEmpty);
|
||||
});
|
||||
|
||||
test('returns an empty map when JSON parses to a non-object', () async {
|
||||
final bundle = _MapAssetBundle({
|
||||
'lib/kernel/src/i18n/catalog/welcome_en_us.json': '[1, 2, 3]',
|
||||
});
|
||||
final bundle = _MapAssetBundle({'lib/kernel/src/i18n/catalog/welcome_en_us.json': '[1, 2, 3]'});
|
||||
final loader = AssetCatalogLoader(bundle: bundle);
|
||||
expect(await loader.load('welcome', const Locale('en', 'US')), isEmpty);
|
||||
});
|
||||
|
||||
@@ -30,11 +30,7 @@ void main() {
|
||||
}
|
||||
|
||||
test('locale getters reflect constructor arguments', () {
|
||||
final i = build(
|
||||
catalogs: const {},
|
||||
initial: const Locale('nl', 'NL'),
|
||||
defaultLocale: const Locale('en', 'US'),
|
||||
);
|
||||
final i = build(catalogs: const {}, initial: const Locale('nl', 'NL'), defaultLocale: const Locale('en', 'US'));
|
||||
expect(i.currentLocale, const Locale('nl', 'NL'));
|
||||
expect(i.defaultLocale, const Locale('en', 'US'));
|
||||
expect(i.availableLocales, [const Locale('en', 'US'), const Locale('nl', 'NL')]);
|
||||
@@ -43,10 +39,7 @@ void main() {
|
||||
test('missing key with placeholder returns placeholder', () async {
|
||||
final i = build(catalogs: const {});
|
||||
await i.ensureNamespaceLoaded('builtin.x');
|
||||
expect(
|
||||
i.string('missing', namespace: 'builtin.x', placeholder: 'fallback'),
|
||||
'fallback',
|
||||
);
|
||||
expect(i.string('missing', namespace: 'builtin.x', placeholder: 'fallback'), 'fallback');
|
||||
});
|
||||
|
||||
test('missing key with null placeholder returns the key', () async {
|
||||
@@ -57,68 +50,66 @@ void main() {
|
||||
|
||||
test('unknown namespace still returns placeholder (no crash)', () {
|
||||
final i = build(catalogs: const {});
|
||||
expect(
|
||||
i.string('k', namespace: 'not.registered', placeholder: 'fb'),
|
||||
'fb',
|
||||
);
|
||||
expect(i.string('k', namespace: 'not.registered', placeholder: 'fb'), 'fb');
|
||||
});
|
||||
|
||||
test('exact locale hit beats fallback', () async {
|
||||
final i = build(catalogs: {
|
||||
'builtin.x': {
|
||||
const Locale('en', 'US'): {
|
||||
'greet': {'translation': 'Hello'},
|
||||
},
|
||||
const Locale('en'): {
|
||||
'greet': {'translation': 'Hi'},
|
||||
final i = build(
|
||||
catalogs: {
|
||||
'builtin.x': {
|
||||
const Locale('en', 'US'): {
|
||||
'greet': {'translation': 'Hello'},
|
||||
},
|
||||
const Locale('en'): {
|
||||
'greet': {'translation': 'Hi'},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
await i.ensureNamespaceLoaded('builtin.x');
|
||||
expect(
|
||||
i.string('greet', namespace: 'builtin.x', placeholder: 'fb'),
|
||||
'Hello',
|
||||
);
|
||||
await i.ensureNamespaceLoaded('builtin.x');
|
||||
expect(i.string('greet', namespace: 'builtin.x', placeholder: 'fb'), 'Hello');
|
||||
});
|
||||
|
||||
test('language-only locale hit falls through from exact', () async {
|
||||
final i = build(catalogs: {
|
||||
'builtin.x': {
|
||||
const Locale('nl'): {
|
||||
'greet': {'translation': 'Hoi'},
|
||||
final i = build(
|
||||
catalogs: {
|
||||
'builtin.x': {
|
||||
const Locale('nl'): {
|
||||
'greet': {'translation': 'Hoi'},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, initial: const Locale('nl', 'NL'));
|
||||
await i.ensureNamespaceLoaded('builtin.x');
|
||||
expect(
|
||||
i.string('greet', namespace: 'builtin.x', placeholder: 'fb'),
|
||||
'Hoi',
|
||||
initial: const Locale('nl', 'NL'),
|
||||
);
|
||||
await i.ensureNamespaceLoaded('builtin.x');
|
||||
expect(i.string('greet', namespace: 'builtin.x', placeholder: 'fb'), 'Hoi');
|
||||
});
|
||||
|
||||
test('falls through to default-locale when current locale is empty', () async {
|
||||
final i = build(catalogs: {
|
||||
'builtin.x': {
|
||||
const Locale('en', 'US'): {
|
||||
'greet': {'translation': 'Hello'},
|
||||
final i = build(
|
||||
catalogs: {
|
||||
'builtin.x': {
|
||||
const Locale('en', 'US'): {
|
||||
'greet': {'translation': 'Hello'},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, initial: const Locale('nl', 'NL'));
|
||||
await i.ensureNamespaceLoaded('builtin.x');
|
||||
expect(
|
||||
i.string('greet', namespace: 'builtin.x', placeholder: 'fb'),
|
||||
'Hello',
|
||||
initial: const Locale('nl', 'NL'),
|
||||
);
|
||||
await i.ensureNamespaceLoaded('builtin.x');
|
||||
expect(i.string('greet', namespace: 'builtin.x', placeholder: 'fb'), 'Hello');
|
||||
});
|
||||
|
||||
test('interpolation replaces all replacers; missing ones silent', () async {
|
||||
final i = build(catalogs: {
|
||||
'builtin.x': {
|
||||
const Locale('en', 'US'): {
|
||||
'welcome': {'translation': 'Hi {name} at {path}'},
|
||||
final i = build(
|
||||
catalogs: {
|
||||
'builtin.x': {
|
||||
const Locale('en', 'US'): {
|
||||
'welcome': {'translation': 'Hi {name} at {path}'},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
await i.ensureNamespaceLoaded('builtin.x');
|
||||
expect(
|
||||
i.interpolated(
|
||||
@@ -135,18 +126,20 @@ void main() {
|
||||
});
|
||||
|
||||
test('namespace isolation — same key, different values', () async {
|
||||
final i = build(catalogs: {
|
||||
'a': {
|
||||
const Locale('en', 'US'): {
|
||||
'k': {'translation': 'A'},
|
||||
final i = build(
|
||||
catalogs: {
|
||||
'a': {
|
||||
const Locale('en', 'US'): {
|
||||
'k': {'translation': 'A'},
|
||||
},
|
||||
},
|
||||
'b': {
|
||||
const Locale('en', 'US'): {
|
||||
'k': {'translation': 'B'},
|
||||
},
|
||||
},
|
||||
},
|
||||
'b': {
|
||||
const Locale('en', 'US'): {
|
||||
'k': {'translation': 'B'},
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
await i.ensureNamespaceLoaded('a');
|
||||
await i.ensureNamespaceLoaded('b');
|
||||
expect(i.string('k', namespace: 'a', placeholder: '-'), 'A');
|
||||
@@ -154,16 +147,18 @@ void main() {
|
||||
});
|
||||
|
||||
test('setLocale refreshes cached namespaces and notifies listeners', () async {
|
||||
final i = build(catalogs: {
|
||||
'x': {
|
||||
const Locale('en', 'US'): {
|
||||
'k': {'translation': 'Hello'},
|
||||
},
|
||||
const Locale('nl'): {
|
||||
'k': {'translation': 'Hallo'},
|
||||
final i = build(
|
||||
catalogs: {
|
||||
'x': {
|
||||
const Locale('en', 'US'): {
|
||||
'k': {'translation': 'Hello'},
|
||||
},
|
||||
const Locale('nl'): {
|
||||
'k': {'translation': 'Hallo'},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
await i.ensureNamespaceLoaded('x');
|
||||
var notified = 0;
|
||||
i.addListener(() => notified++);
|
||||
@@ -177,10 +172,7 @@ void main() {
|
||||
i.registerCatalog('ext.linear', const Locale('en', 'US'), const {
|
||||
'issue.title': {'translation': 'Issues'},
|
||||
});
|
||||
expect(
|
||||
i.string('issue.title', namespace: 'ext.linear', placeholder: '-'),
|
||||
'Issues',
|
||||
);
|
||||
expect(i.string('issue.title', namespace: 'ext.linear', placeholder: '-'), 'Issues');
|
||||
});
|
||||
|
||||
test('unregisterCatalog forgets a namespace', () async {
|
||||
@@ -196,11 +188,13 @@ void main() {
|
||||
test('plain-string shape (no nested translation) is accepted', () async {
|
||||
// Forward-compat: if a catalog later switches to `"k": "v"`
|
||||
// instead of `"k": {"translation": "v"}`, lookup still works.
|
||||
final i = build(catalogs: {
|
||||
'x': {
|
||||
const Locale('en', 'US'): {'k': 'direct'},
|
||||
final i = build(
|
||||
catalogs: {
|
||||
'x': {
|
||||
const Locale('en', 'US'): {'k': 'direct'},
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
await i.ensureNamespaceLoaded('x');
|
||||
expect(i.string('k', namespace: 'x', placeholder: '-'), 'direct');
|
||||
});
|
||||
@@ -208,23 +202,12 @@ void main() {
|
||||
|
||||
group('FallbackChain.resolve', () {
|
||||
test('ordering: exact, lang, default, default-lang', () {
|
||||
final chain = const FallbackChain(
|
||||
current: Locale('nl', 'NL'),
|
||||
defaultLocale: Locale('en', 'US'),
|
||||
).resolve();
|
||||
expect(chain.map((l) => l.toString()).toList(), [
|
||||
'nl_NL',
|
||||
'nl',
|
||||
'en_US',
|
||||
'en',
|
||||
]);
|
||||
final chain = const FallbackChain(current: Locale('nl', 'NL'), defaultLocale: Locale('en', 'US')).resolve();
|
||||
expect(chain.map((l) => l.toString()).toList(), ['nl_NL', 'nl', 'en_US', 'en']);
|
||||
});
|
||||
|
||||
test('deduplicates when current == default', () {
|
||||
final chain = const FallbackChain(
|
||||
current: Locale('en', 'US'),
|
||||
defaultLocale: Locale('en', 'US'),
|
||||
).resolve();
|
||||
final chain = const FallbackChain(current: Locale('en', 'US'), defaultLocale: Locale('en', 'US')).resolve();
|
||||
expect(chain, ['en_US', 'en'].map((_) => isA<Locale>()));
|
||||
expect(chain.length, 2);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user