| name | remote-localization |
| description | Remote translation updates using the `anas_localization` remote localization system — implement RemoteLocalizationConnector for fetching translations from a remote source, configure with RemoteLocalizationConfig, manage cache via RemoteLocalizationCacheStore, handle update results (updated/noUpdate/failed), and merge remote translations with local assets. Use when asked to implement remote/over-the-air translation updates, dynamic locale content delivery, or live translation refreshing in Flutter apps using anas_localization. |
| compatibility | opencode |
| license | MIT |
Remote Localization
Overview
The remote localization system in anas_localization allows apps to fetch and apply translation updates from a remote source (CDN, API, Firebase, etc.) without requiring an app store release.
Precedence: Package assets → App assets → Remote cached translations
When to Use This Skill
- Implement over-the-air translation updates.
- Fetch translations from a remote API or CDN.
- Cache remote translations for offline use.
- Handle update checks per-locale or globally.
- Merge remote translations with local asset translations.
Quick Start
1. Implement a Connector
Create a class implementing RemoteLocalizationConnector:
import 'package:anas_localization/anas_localization.dart';
class MyCdnConnector implements RemoteLocalizationConnector {
@override
bool get supportsGlobalCheck => true;
@override
bool get supportsLocaleCheck => true;
@override
Future<RemoteCheckResponse> checkForUpdates(
RemoteVersionSnapshot cachedVersions,
) async {
// Query your API/CDN for updates
final response = await http.get(Uri.parse('https://cdn.example.com/checks'));
return RemoteCheckResponse.fromJson(response.body);
}
@override
Future<RemoteCheckResponse> checkForLocaleUpdate(
Locale locale,
RemoteLocalizationVersion? cachedVersion,
) async {
// Query for a single locale update
final response = await http.get(
Uri.parse('https://cdn.example.com/checks/${locale.languageCode}'),
);
return RemoteCheckResponse.fromJson(response.body);
}
@override
Future<RemoteLocalizationPayload> downloadPayload(
RemoteUpdateDescriptor update,
) async {
// Download the actual translation payload
final response = await http.get(
Uri.parse('https://cdn.example.com/translations/${update.locale}'),
);
return RemoteLocalizationPayload.fromJson(response.body);
}
}
2. Configure in the App
AnasLocalization(
app: MyApp(),
remoteConfig: RemoteLocalizationConfig(
connector: MyCdnConnector(),
checkOnStartup: true, // Auto-check on app launch
),
// ... other params
)
3. Trigger Updates Manually
// Global check — checks all locales
final result = await AnasLocalization.remote.checkForUpdates();
// Per-locale check
final result = await AnasLocalization.remote.checkForLocaleUpdate(
Locale('ar'),
);
// Read cache
final cache = await AnasLocalization.remote.readCache();
4. Handle Results
switch (result.status) {
case RemoteLocalizationUpdateStatus.updated:
final success = result as RemoteLocalizationUpdateSuccess;
print('Applied: ${success.appliedLocales}');
case RemoteLocalizationUpdateStatus.noUpdate:
print('Already up to date');
case RemoteLocalizationUpdateStatus.failed:
final failed = result as RemoteLocalizationFailed;
print('Error: ${failed.failure.message}');
case RemoteLocalizationUpdateStatus.skippedDuplicate:
print('Check already in progress');
case RemoteLocalizationUpdateStatus.unsupported:
print('Remote not configured');
}
Core API Reference
RemoteLocalizationConfig
| Parameter | Type | Default | Description |
|---|
connector | RemoteLocalizationConnector | required | The connector implementation |
checkOnStartup | bool | false | Auto-check on app launch |
cacheStore | RemoteLocalizationCacheStore? | null | Custom cache store (defaults to file-based) |
metrics | RemoteLocalizationMetrics? | null | Optional metrics collector |
RemoteLocalizationConnector
abstract interface class RemoteLocalizationConnector {
bool get supportsGlobalCheck;
bool get supportsLocaleCheck;
Future<RemoteCheckResponse> checkForUpdates(RemoteVersionSnapshot cachedVersions);
Future<RemoteCheckResponse> checkForLocaleUpdate(Locale locale, RemoteLocalizationVersion? cachedVersion);
Future<RemoteLocalizationPayload> downloadPayload(RemoteUpdateDescriptor update);
}
RemoteLocalizationService (accessed via AnasLocalization.remote)
abstract interface class RemoteLocalizationService {
Future<RemoteLocalizationUpdateResult> checkForUpdates();
Future<RemoteLocalizationUpdateResult> checkForLocaleUpdate(Locale locale);
Future<RemoteLocalizationCacheSnapshot> readCache();
}
Result Types
| Class | Status | Description |
|---|
RemoteLocalizationUpdateSuccess | updated | Translations were downloaded and applied |
RemoteLocalizationNoUpdate | noUpdate | Already up to date |
RemoteLocalizationSkippedDuplicate | skippedDuplicate | Duplicate check prevented |
RemoteLocalizationUnsupported | unsupported | Remote not configured |
RemoteLocalizationFailed | failed | Error with RemoteLocalizationFailure details |
RemoteLocalizationFailure
| Field | Type | Description |
|---|
code | RemoteLocalizationFailureCode | checkFailed, downloadFailed, timeout, parseFailed, cacheReadFailed, cacheWriteFailed, etc. |
message | String | Error description |
locale | String? | Affected locale |
retryAttempted | bool | Whether a retry was attempted |
recoverable | bool | Whether the error is recoverable |
Cache
// Read the entire cache
final snapshot = await AnasLocalization.remote.readCache();
// Access cached payloads
snapshot.payloads // Map<String, RemoteLocalizationPayload>
snapshot.payloadFor('en') // RemoteLocalizationPayload?
snapshot.lastWriteAt // DateTime?
snapshot.fallbackMode // String (default: 'persistent')
// Each RemoteLocalizationPayload has:
payload.locale // String
payload.version // RemoteLocalizationVersion
payload.translations // Map<String, Object?>
payload.receivedAt // DateTime
payload.isValid // bool
payload.isNewerThan(other) // Version comparison
Merge Behavior
Package assets (lowest priority)
↓ overlays
App assets
↓ overlays
Remote cached translations (highest priority)
Remote translations can override app/package translations. To protect a key from remote override, use a metadata wrapper in app assets:
{
"protected_key": {
"__override__": false,
"value": "This cannot be overridden remotely"
}
}
Architecture
AnasLocalization.remote
└─ RemoteLocalizationCoordinator
├─ Timeout & retry (configurable via RemoteLocalizationConfig)
├─ Duplicate in-flight detection
└─ RemoteLocalizationRepositoryImpl
├─ RemoteLocalizationConnector (your implementation)
│ ├─ checkForUpdates / checkForLocaleUpdate
│ └─ downloadPayload
└─ RemoteLocalizationCacheStore
└─ RemoteLocalizationFileCacheStore (default)
Additional Resources