Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

http-local-asset translation loader #31

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 155 additions & 0 deletions lib/src/local-http-asset-assetloader.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import 'dart:convert';
import 'dart:developer';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;

import 'asset_loader.dart';

/// if you are in need of particular type of requirement such as:

/// your translations in asset are not complete so that you want to use the remote translations but want to make available for offilne use
/// this makes app useable if there is no internet connection to fetch the new transaltions everytime.

///if there is translation file locally(cached) then

///1. merge the local translation file with the asset-bundler translation file but give priority to the local translation.
///2. if the local localization is older than [one day] then fetch the new localization from backend and save in local(cache) and perform 1st step.
class HttpLocalAssetLoader extends AssetLoader {
final String httpPathBase;

/// path where the transaltion file is stored
/// use as such
// final applicationDirectory = await getApplicationSupportDirectory(); using path_provider pacakge
final String localFilePath;

/// base path to the localization in remote http
HttpLocalAssetLoader(
{required this.httpPathBase, required this.localFilePath});

/// here asset path is the path to the assetbundle file
@override
Future<Map<String, dynamic>> load(String assetPath, Locale locale) async {
try {
final localeFromLocal = await _getJsonFromLocaleFile(locale);
final localeFromAsset = await _getJsonFromAsset(assetPath, locale);

if (localeFromLocal != null) {
if (localeFromAsset != null) {
localeFromAsset.addAll(localeFromLocal);
return localeFromAsset;
} else {
return localeFromLocal;
}
} else {
final localeFromHtpp = await _getFromHttp(httpPathBase, locale);

if (localeFromHtpp != null) {
if (localeFromAsset != null) {
localeFromAsset.addAll(localeFromHtpp);
return localeFromAsset;
} else {
return localeFromHtpp;
}
} else {
if (localeFromAsset != null) {
return localeFromAsset;
} else {
return Future.value({'error': 'no translation'});
}
}
}
} catch (e) {
return Future.value({'error_${locale.countryCode}': '$e'});
}
}

String _getAssetBundleLocalePath(String basePath, Locale locale) {
return '$basePath/${locale.toStringWithSeparator(separator: "-")}.json';
}

Future<String> _getLocalLocalePath(Locale locale) async {
return "$localFilePath/${locale.toStringWithSeparator(separator: "-")}.json";
}

String _getHttpPath(String base, Locale local) {
return '$base/${local.languageCode}';
}

Future<Map<String, dynamic>?> _getJsonFromAsset(
String path, Locale locale) async {
try {
var localePath = _getAssetBundleLocalePath(path, locale);
log('Load asset from $path');
return json.decode(await rootBundle.loadString(localePath));
} catch (e) {
log('Load asset failed from $path $e');

return Future.value();
}
}

Future<Map<String, dynamic>?> _getJsonFromLocaleFile(Locale locale) async {
try {
log('getting transalation from local file');
var localePath = await _getLocalLocalePath(locale);
final localFile = File(localePath);
if (await localFile.exists()) {
log(' transalation in local file found');
final lastModified = await localFile.lastModified();
if (lastModified.difference(DateTime.now()).abs().inHours > 24) {
log(' transalation in local file is older than 24hr');
return Future.value();
}

final translation = json.decode(await localFile.readAsString());
log('Load asset from $localePath');
return translation;
}
log(' no transalation found in local file ');

return Future.value();
} catch (e) {
log('Load asset failed from local file $e');

return Future.value();
}
}

Future<Map<String, dynamic>?> _getFromHttp(String path, Locale locale) async {
try {
log('Loading asset from http');

var url = Uri.parse(_getHttpPath(path, locale));
final localeResponse = await http.get(url);
if (localeResponse.statusCode != 200) {
log('Loading asset from ${url.toString()} failed ${localeResponse.body}');
return Future.value();
}

try {
log('Writing the translation json in local');
final file = File(await _getLocalLocalePath(locale));
await file.create(recursive: true);
await file.writeAsString(localeResponse.body, flush: true);
} catch (e) {
log('Failed to cache the remote translation in cache $e');
}

return jsonDecode(localeResponse.body);
} catch (e) {
log('easy localization loader: load http $path error: $e');
//Catch network exceptions
return Future.value();
}
}
}

extension LocaleToStringHelper on Locale {
/// Convert [locale] to String with custom separator
String toStringWithSeparator({String separator = '_'}) {
return toString().split('_').join(separator);
}
}
25 changes: 16 additions & 9 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ packages:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.5.0"
version: "2.8.2"
boolean_selector:
dependency: transitive
description:
Expand All @@ -21,14 +21,14 @@ packages:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
version: "1.2.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "1.3.1"
clock:
dependency: transitive
description:
Expand Down Expand Up @@ -87,14 +87,21 @@ packages:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.10"
version: "0.12.11"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.3"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
version: "1.7.0"
path:
dependency: transitive
description:
Expand Down Expand Up @@ -127,7 +134,7 @@ packages:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
version: "1.8.1"
stack_trace:
dependency: transitive
description:
Expand Down Expand Up @@ -162,7 +169,7 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.19"
version: "0.4.8"
typed_data:
dependency: transitive
description:
Expand All @@ -176,7 +183,7 @@ packages:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
version: "2.1.1"
xml:
dependency: "direct main"
description:
Expand All @@ -192,4 +199,4 @@ packages:
source: hosted
version: "3.1.0"
sdks:
dart: ">=2.12.0 <3.0.0"
dart: ">=2.14.0 <3.0.0"