78 lines
2.6 KiB
Dart
78 lines
2.6 KiB
Dart
import 'package:fluent/fluent.dart';
|
|
import 'package:flutter/services.dart' show rootBundle;
|
|
import 'package:flutter/widgets.dart';
|
|
|
|
import 'locales.dart';
|
|
|
|
/// Loads and formats the app's Fluent messages. All user-facing strings come
|
|
/// from `locales/<tag>/main.ftl`.
|
|
///
|
|
/// A message missing from the chosen language falls back to English, and a
|
|
/// message missing from English too comes back as its own id, so a gap shows
|
|
/// in the interface rather than crashing or rendering as nothing.
|
|
class Strings {
|
|
FluentBundle _bundle = FluentBundle(fallbackLocale);
|
|
FluentBundle _fallback = FluentBundle(fallbackLocale);
|
|
|
|
String _tag = fallbackLocale;
|
|
|
|
/// The language now in use, as a shipped locale tag.
|
|
String get tag => _tag;
|
|
|
|
bool get isRtl => isRightToLeft(_tag);
|
|
|
|
/// Loads English, which every other language falls back to.
|
|
Future<void> load() async {
|
|
_fallback = await _bundleFor(fallbackLocale);
|
|
_bundle = _fallback;
|
|
_tag = fallbackLocale;
|
|
}
|
|
|
|
/// Switches to [tag], keeping English underneath it.
|
|
Future<void> use(String tag) async {
|
|
if (tag == _tag) return;
|
|
_bundle = tag == fallbackLocale ? _fallback : await _bundleFor(tag);
|
|
_tag = tag;
|
|
}
|
|
|
|
Future<FluentBundle> _bundleFor(String tag) async {
|
|
final bundle = FluentBundle(tag);
|
|
bundle.addMessages(await rootBundle.loadString('locales/$tag/main.ftl'));
|
|
return bundle;
|
|
}
|
|
|
|
/// Loads messages from an already-read source, so tests can read files from
|
|
/// disk instead of the asset bundle.
|
|
void loadSource(String source, {String tag = fallbackLocale}) {
|
|
final bundle = FluentBundle(tag);
|
|
bundle.addMessages(source);
|
|
_bundle = bundle;
|
|
_tag = tag;
|
|
if (tag == fallbackLocale) _fallback = bundle;
|
|
}
|
|
|
|
/// The message for [id], or null when neither the chosen language nor
|
|
/// English has one. Used where a missing message is expected, such as a
|
|
/// plural form a language does not need.
|
|
String? lookup(String id, [Map<String, Object?> args = const {}]) =>
|
|
_bundle.format(id, args: args) ?? _fallback.format(id, args: args);
|
|
|
|
String t(String id, [Map<String, Object?> args = const {}]) =>
|
|
lookup(id, args) ?? id;
|
|
|
|
/// Which plural message [count] needs in the language in use.
|
|
String pluralSuffixFor(int count) => pluralSuffix(_tag, count);
|
|
}
|
|
|
|
/// Provides [Strings] to the widget tree.
|
|
class StringsScope extends InheritedWidget {
|
|
const StringsScope({required this.strings, required super.child, super.key});
|
|
|
|
final Strings strings;
|
|
|
|
static Strings of(BuildContext context) =>
|
|
context.getInheritedWidgetOfExactType<StringsScope>()!.strings;
|
|
|
|
@override
|
|
bool updateShouldNotify(StringsScope oldWidget) => false;
|
|
}
|