setLanguageGroupFallback method
T023: Sets a language group fallback for a locale. Validates that the fallback is in the same language group, prevents circular references, and checks locale existence. Also enforces FR-010: directionality constraint for fallbacks.
Implementation
Future<void> setLanguageGroupFallback({
required String locale,
required String newFallback,
List<String>? validLocales,
}) async {
final state = await _stateStore.load(
config: config,
projectRootPath: projectRootPath,
);
// Check self-reference
if (locale == newFallback) {
throw CatalogOperationException(
'Locale cannot fallback to itself',
);
}
// Check language group constraint
if (!sameLanguageGroup(locale, newFallback)) {
throw CatalogOperationException(
'Fallback must be in same language group',
);
}
// FR-010: Enforce fallback directionality constraint
// Base language can only fallback to: base language or other base languages
// Regional variant can fallback to: regional variant or base language
// NOT allowed: Base → Regional (e.g., en → ar_SA)
final sourceIsRegional = _isLocaleRegional(locale);
final targetIsRegional = _isLocaleRegional(newFallback);
if (!sourceIsRegional && targetIsRegional) {
throw CatalogOperationException(
'Invalid fallback direction: base language "$locale" cannot fall back to regional variant "$newFallback". '
'Only these directions allowed: Regional→Regional, Regional→Base, Base→Base',
);
}
// Check circular fallback
if (hasCircularFallback(state.languageGroupFallbacks, locale, newFallback)) {
throw CatalogOperationException(
'Setting this fallback would create circular reference',
);
}
// Check if locales exist (if validLocales provided)
if (validLocales != null) {
if (!validLocales.contains(locale)) {
throw CatalogOperationException('Locale "$locale" does not exist');
}
if (!validLocales.contains(newFallback)) {
throw CatalogOperationException('Fallback locale "$newFallback" does not exist');
}
}
// Update state
final updatedFallbacks = Map<String, String>.from(state.languageGroupFallbacks)..[locale] = newFallback;
final updatedState = state.copyWith(languageGroupFallbacks: updatedFallbacks);
await _stateStore.save(
config: config,
projectRootPath: projectRootPath,
state: updatedState,
);
}