adding i18n for dynamic page
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
/// Single source of truth for app language (Group-2).
|
||||
/// Drives BOTH systems together:
|
||||
/// - ARB static words via MaterialApp.locale (AppLocalizations)
|
||||
/// - wireframe field words via DynamicStrings.tr(key) backed by
|
||||
/// assets/translations_dynamic/{lang}.json (free, offline, KBs).
|
||||
/// Revert: remove provider wiring in main.dart; generated screens fall back
|
||||
/// to English keys (DynamicStrings.tr returns readable fallback, never crash).
|
||||
class LocaleProvider with ChangeNotifier {
|
||||
static const _prefKey = 'app_locale_code';
|
||||
static const supportedCodes = ['en', 'hi'];
|
||||
|
||||
Locale _locale = const Locale('en');
|
||||
Locale get locale => _locale;
|
||||
String get code => _locale.languageCode;
|
||||
|
||||
/// lang -> (key -> translated value). Embedded assets loaded once at startup.
|
||||
final Map<String, Map<String, String>> _tables = {};
|
||||
|
||||
/// Server-supplied diff cache (future: portal-backend translations API).
|
||||
/// Key format: "lang::key" -> value, persisted to SharedPreferences.
|
||||
final Map<String, String> _serverDiff = {};
|
||||
|
||||
bool _ready = false;
|
||||
bool get ready => _ready;
|
||||
|
||||
Future<void> init() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final saved = prefs.getString(_prefKey);
|
||||
if (saved != null && supportedCodes.contains(saved)) {
|
||||
_locale = Locale(saved);
|
||||
}
|
||||
_ready = true;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Called once from bootstrap with preloaded asset tables.
|
||||
void seedTables(Map<String, Map<String, String>> tables) {
|
||||
_tables.addAll(tables);
|
||||
}
|
||||
|
||||
/// Merge server diff (new wireframe fields added after release).
|
||||
/// Works offline afterwards because diff is persisted.
|
||||
Future<void> updateFromServer(Map<String, String> words, String lang) async {
|
||||
if (words.isEmpty) return;
|
||||
final table = _tables.putIfAbsent(lang, () => {});
|
||||
table.addAll(words);
|
||||
for (final e in words.entries) {
|
||||
_serverDiff['$lang::${e.key}'] = e.value;
|
||||
}
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
// Persist each entry individually to survive restarts (small volume).
|
||||
for (final e in words.entries) {
|
||||
await prefs.setString('dyn_${lang}_${e.key}', e.value);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> loadServerCache() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
for (final lang in supportedCodes) {
|
||||
final table = _tables.putIfAbsent(lang, () => {});
|
||||
for (final key in prefs.getKeys().where((k) => k.startsWith('dyn_${lang}_'))) {
|
||||
final field = key.substring('dyn_${lang}_'.length);
|
||||
final v = prefs.getString(key);
|
||||
if (v != null) table[field] = v;
|
||||
}
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> setLocale(String code) async {
|
||||
if (!supportedCodes.contains(code)) return;
|
||||
_locale = Locale(code);
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString(_prefKey, code);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Runtime lookup for wireframe-driven strings.
|
||||
/// Never throws: missing key -> English table -> pretty fallback.
|
||||
String trDynamic(String key) {
|
||||
final norm = key.trim().toLowerCase().replaceAll(RegExp(r'\s+'), '_');
|
||||
final cur = _tables[code]?[norm];
|
||||
if (cur != null && cur.isNotEmpty) return cur;
|
||||
final en = _tables['en']?[norm];
|
||||
if (en != null && en.isNotEmpty) return en;
|
||||
return key.replaceAll('_', ' ');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user