this is asset loader class for fetching the translation.json file from api.
class SmartNetworkAssetLoader extends AssetLoader {
final Function localeUrl;
final Duration timeout;
final String assetsPath;
final Duration localCacheDuration;
SmartNetworkAssetLoader(
{required this.localeUrl,
this.timeout = const Duration(seconds: 30),
required this.assetsPath,
this.localCacheDuration = const Duration(days: 1)});
@override
Future<Map<String, dynamic>> load(String localePath, Locale locale) async {
var string = '';
print("localPath $localePath");
print("locale $locale");
// try loading local previously-saved localization file
if (await localTranslationExists(locale.toString())) {
string = await loadFromLocalFile(locale.toString());
}
// no local or failed, check if internet and download the file
if (string == '' && await isInternetConnectionAvailable()) {
string = await loadFromNetwork(locale.toString());
}
// local cache duration was reached or no internet access but prefer local file to assets
if (string == '' &&
await localTranslationExists(locale.toString(),
ignoreCacheDuration: false)) {
string = await loadFromLocalFile(locale.toString());
}
// still nothing? Load from assets
if (string == '') {
String localeString = locale.toString();
// Replace underscores with hyphens
String convertedLocale = localeString.replaceAll('_', '-');
string = await rootBundle
.loadString('$assetsPath/$convertedLocale.json');
}
// then returns the json file
return json.decode(string);
}
Future<bool> localeExists(String localePath) => Future.value(true);
Future<bool> isInternetConnectionAvailable() async {
try{
ConnectivityResult connectivityResult =
await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.none) return false;
final result = await Future.any(
[InternetAddress.lookup('google.com'), Future.delayed(timeout)]);
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
return true;
}
}catch(e)
{
print(e);
}
return false;
}
Future<String> loadFromNetwork(String localeName) async {
String url = localeUrl(localeName);
print("loccaleName$localeName");
print("url $url");
url = '$url$localeName';
print("final Url $url");
try {
final response =
await Future.any([http.get(Uri.parse(url)), Future.delayed(timeout)]);
if (response != null && response.statusCode == 200) {
var content = utf8.decode(response.bodyBytes);
log("content1234 $content");
try {
var jsonResponse = json.decode(content);
if (jsonResponse != null && jsonResponse['data'] != null) {
// Extract only the "data" content
var dataContent = jsonResponse['data'];
// Convert it back to a JSON string
var dataContentString = json.encode(dataContent);
print("data content: $dataContentString");
print("localeName $localeName");
// Save the translation
await saveTranslation(localeName, dataContentString);
return dataContentString;
}
} catch (e) {
showFalseSnackbar("$e");
print("Error decoding JSON: $e");
}
}else{
showFalseSnackbar("response null or ${response.statusCode}");
}
} catch (e) {
showFalseSnackbar("$e");
print(e.toString());
}
return '';
}
Future<bool> localTranslationExists(String localeName,
{bool ignoreCacheDuration = false}) async {
var translationFile = await getFileForLocale(localeName);
if (!await translationFile.exists()) {
return false;
}
// don't check file's age
if (!ignoreCacheDuration) {
var difference =
DateTime.now().difference(await translationFile.lastModified());
if (difference > (localCacheDuration)) {
return false;
}
}
return true;
}
Future<String> loadFromLocalFile(String localeName) async {
return await (await getFileForLocale(localeName)).readAsString();
}
Future<void> saveTranslation(String localeName, String content) async {
try{
var file = File(await getFilenameForLocale(localeName));
await file.create(recursive: true);
return print('saved');
}catch(e)
{
print("error creating file $e");
}
}
Future<String> get _localPath async {
final directory = await paths.getTemporaryDirectory();
return directory.path;
}
Future<String> getFilenameForLocale(String localeName) async {
return '${await _localPath}/translations/$localeName.json';
}
Future<File> getFileForLocale(String localeName) async {
return File(await getFilenameForLocale(localeName));
}
}
main.dart file code
runApp((EasyLocalization(
supportedLocales:[
Locale('en', 'US'),
Locale('es', 'ES')
],
path: 'assets/translations',
fallbackLocale: const Locale('en', 'US'),
assetLoader: SmartNetworkAssetLoader(
assetsPath: 'assets/translations',
localCacheDuration: const Duration(days: 1),
localeUrl: (String localeName) => Constants.appLangUrl,
timeout: const Duration(seconds: 30),
),
child: const MyApp())));
Dropdown onchanged function.
DropdownButton<String>(
value: state.selectedValue,
onChanged: (newValue) async {
context.read<LoginBloc>().add(DropDownButtonClickedEvent(newValue!));
await changeLanguageHandler(context, newValue);
},
items: state.list
?.map<DropdownMenuItem<String>>((element) {
return DropdownMenuItem<String>(
value: element,
child: Text(
element,
style: const TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w400,
),
),
);
}).toList(),
icon: Image.asset(
"assets/images/arrowdown.png",
width: 16.w,
height: 7.h,
),
),
on dropdown click will call the below function.
Future<void> changeLanguageHandler( BuildContext context, String language) async {
try {
await LocalizationChecker.changeLanguage(context, language);
setState(() {});
} catch (error) {
print("error $error");
}
}
class LocalizationChecker {
static changeLanguage(BuildContext context, String language) {
// Locale? currentLocal = EasyLocalization.of(context)!.currentLocale;
print("inside localization");
if (language == 'Spanish') {
EasyLocalization.of(context)!.setLocale(const Locale('es', 'ES'));
} else {
EasyLocalization.of(context)!.setLocale(const Locale('en', 'US'));
}
}
}
here my main issue is translation is not working consistently when we are using through api call that is (SmartAssetLoader class), why it happens is there any issue on my smartassetloader class or any libray issue. Here for changing the translation through api call we are using the library called easy_localization_loader 2.0.1 and easy_localization 3.0.3 please help i am new to this.
Your
SmartNetworkAssetLoader(from easy_localizationAssetLoader) checks for local translations, then network, and again local before finally trying the assets(!). That complex logic may cause issues if any step has unexpected behavior. Especially, the network loading part might be failing silently: network issues could cause inconsistent loading of translations. If the network request fails or times out, the loader will fall back to local or asset-based translations.Plus, your
saveTranslationfunction creates a file but does not actually write the content to it. That means your local translations may never actually be saved.Modify the
SmartNetworkAssetLoaderclass to include simplifying the loading logic, enhancing error handling and logging, checking network reliability, and fixing the file saving logic.The function now checks for local translation first, then network, and finally falls back to assets. That simplifies the flow. The loader now catches any exceptions during the loading process, logs them, and falls back to assets if needed.
The existing logic for network checks remains the same, but any failure in network loading leads to an immediate fallback to assets.
And the
saveTranslationfunction now correctly writes the content to the file.You could indeed add a loading indicator when switching between languages in your Flutter app. You would update your UI to show a loading spinner or similar indicator while the app is fetching and applying the new translations.
First, you need a way to track whether your app is currently loading new language data. You can use a state management solution (like
setState,Provider,Bloc, etc.) to manage a boolean variable that represents the loading state.Modify the function responsible for switching languages to toggle the loading state before and after the language change process. And update your UI to show a loading indicator (like
CircularProgressIndicator) when the app is in the loading state.Using a simple
setStateapproach:_isLoadingis used to track the loading state.The
changeLanguagefunction updates_isLoadingbefore starting and after completing the language switch.The
buildmethod checks_isLoadingand displays aCircularProgressIndicatorwhen loading is in progress.That would make sure users are aware that the app is working on something and improves the overall user experience, especially when dealing with network delays.
Inside the
SmartNetworkAssetLoader? No.It would not be the best practice because the
AssetLoaderclass in theeasy_localizationpackage is designed for loading assets and should not be responsible for UI-related tasks like showing loading indicators.Mixing data fetching logic with UI handling can lead to code that is harder to maintain and debug.
However, you can achieve the desired behavior by handling the loading indicator at the UI level, where you have access to the
BuildContext.Before invoking the language change or data loading process, show the loading indicator. Perform the language change operation and wait for it to complete. Since this operation is asynchronous, you can await it in the UI layer. Once the language change and data loading are complete, hide the loading indicator.
Your code would be:
changeLanguageis responsible for showing the loading indicator, performing the language change, and then hiding the indicator.The actual loading of translation data is abstracted in
loadTranslationData, which can be your logic to fetch and apply translations.That way, you maintain a separation of concerns:
SmartNetworkAssetLoaderfocuses solely on loading assets, while your widget manages the UI state, including showing and hiding loading indicators.