diff --git a/.github/workflows/pr_check.yml b/.github/workflows/pr_check.yml new file mode 100644 index 0000000..b1fca1b --- /dev/null +++ b/.github/workflows/pr_check.yml @@ -0,0 +1,11 @@ +name: PR Check + +on: + workflow_dispatch: + pull_request: + +jobs: + test: + name: Test + uses: ./.github/workflows/test.yml + secrets: inherit diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..053bd2b --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,39 @@ +name: Release to pub.dev + +on: + workflow_dispatch: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+*' + +jobs: + test: + name: Test + uses: ./.github/workflows/test.yml + secrets: inherit + + publish: + needs: [test] + name: Publish + permissions: + id-token: write # This is required for authentication using OIDC + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - uses: actions/checkout@v3 + + - uses: dart-lang/setup-dart@v1 + + - uses: subosito/flutter-action@v2 + with: + channel: "stable" + + - name: Install dependencies + run: dart pub get + + - name: code format + run: dart format lib/*/*.dart lib/*.dart + + - name: Publish + run: dart pub publish --force diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..ab11363 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,29 @@ +name: Test + +on: + workflow_call: + +jobs: + test: + name: Test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-java@v1 + with: + java-version: '12.x' + - uses: subosito/flutter-action@v1 + with: + channel: 'stable' + + - name: Install packages dependencies + run: flutter pub get + + - name: Analyze the project's Dart code + run: flutter analyze + + - name: Run tests + run: flutter test + + - name: Run tests coverage + run: flutter test --coverage \ No newline at end of file diff --git a/.gitignore b/.gitignore index 5b89f90..e37c091 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ *.ipr *.iws .idea/ +.vscode/ # The .vscode folder contains launch configuration and tasks you configure in # VS Code which you may wish to be included in version control, so this line diff --git a/CHANGELOG.md b/CHANGELOG.md index b0a20b7..67a9e97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,22 @@ -## [0.0.1] +## 2.0.1 -- initial release +- Extend delimiters for csv loader + +## 2.0.0 + +- **BREAKING**: The local `AssetLoader` class deleted, now using the one from + [easy_localization](https://pub.dev/documentation/easy_localization/latest/easy_localization/AssetLoader-class.html) itself. +- **BREAKING**: Depends on [connectivity_plus](https://pub.dev/packages/connectivity_plus) ^4.0.0 + and [http](https://pub.dev/packages/http) ^1.0.0. +- Const constructors in: + - `FileAssetLoader` + - `HttpAssetLoader` + - `JsonAssetLoader` + - `TestsAssetLoader` + - `XmlAssetLoader` + - `YamlAssetLoader` +- Fixed deprecations + +## 0.0.1 + +- Initial release. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..4570e50 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,11 @@ +# Contributing + +## Release process + + 1. Make sure that the changelog is updated + + 2. Make sure that the version in pubspec.yaml is correct + + 3. Create a release in the github UI. Name the release like the version, but with a v (3.7.5 -> v3.7.5). Name the tag like the release + + 4. A pipeline will run and deploy the new version to pub.dev diff --git a/README.md b/README.md index b837f8a..46939f9 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ dependencies: #Easy Localization main package easy_localization: - # stable version install from https://pub.dev/packages + # stable version install from https://pub.dev/packages easy_localization_loader: # Dev version install from git REPO @@ -40,10 +40,11 @@ dependencies: ``` -2. Change assetLoader and patch +2. Change assetLoader and path ```dart -...void main(){ +... +void main(){ runApp(EasyLocalization( child: MyApp(), supportedLocales: [Locale('en', 'US'), Locale('ar', 'DZ')], @@ -54,4 +55,20 @@ assetLoader: CsvAssetLoader() ... ``` -3. All done!. \ No newline at end of file +3. All done!. + + +### Loaders Specification + +#### HttpAssetLoader + +In order to use HttpAssetLoader you must provide a path to a folder (i.e. base path) where all your translations are placed like `https://example.com/translations` + +Your translations should be created as separate files with `.json` extension. Placing translations as individual files reduces the size of the file to load on application init. +Example: + +``` +translations/ +├── en-US.json +└── uk-UA.json +``` diff --git a/lib/easy_localization_loader.dart b/lib/easy_localization_loader.dart index 5625836..20eeb9a 100644 --- a/lib/easy_localization_loader.dart +++ b/lib/easy_localization_loader.dart @@ -1,11 +1,9 @@ -library easy_localization_loader; - +export 'package:easy_localization_loader/src/csv_asset_loader.dart'; export 'package:easy_localization_loader/src/file_asset_loader.dart'; -export 'package:easy_localization_loader/src/arb_asset_loader.dart'; -export 'package:easy_localization_loader/src/json_asset_loader.dart'; export 'package:easy_localization_loader/src/http_asset_loader.dart'; -export 'package:easy_localization_loader/src/csv_asset_loader.dart'; -export 'package:easy_localization_loader/src/yaml_asset_loader.dart'; -export 'package:easy_localization_loader/src/xml_asset_loader.dart'; -export 'package:easy_localization_loader/src/tests_asset_loader.dart'; +export 'package:easy_localization_loader/src/json_asset_loader.dart'; export 'package:easy_localization_loader/src/smart_network_asset_loader.dart'; +export 'package:easy_localization_loader/src/tests_asset_loader.dart'; +export 'package:easy_localization_loader/src/xml_asset_loader.dart'; +export 'package:easy_localization_loader/src/yaml_asset_loader.dart'; +export 'package:easy_localization_loader/src/arb_asset_loader.dart'; diff --git a/lib/src/arb_asset_loader.dart b/lib/src/arb_asset_loader.dart index 6e13384..f159b14 100644 --- a/lib/src/arb_asset_loader.dart +++ b/lib/src/arb_asset_loader.dart @@ -4,18 +4,19 @@ import 'dart:ui'; import 'package:flutter/services.dart'; -import 'asset_loader.dart'; +import 'package:easy_localization/easy_localization.dart'; class ArbAssetLoader extends AssetLoader { + const ArbAssetLoader(); + String getLocalePath(String basePath, Locale locale) { - return '$basePath/${localeToString(locale, separator: "-")}.arb'; + return '$basePath/${locale.toStringWithSeparator(separator: "-")}.arb'; } @override Future> load(String path, Locale locale) async { var localePath = getLocalePath(path, locale); log('easy localization loader: load arb file $localePath'); - return jsonDecode(await rootBundle.loadString(localePath)); } } @@ -32,7 +33,6 @@ class ArbSingleAssetLoader extends AssetLoader { } else { log('easy localization loader: arb already loaded, read cache'); } - return jsonData![locale.toString()]; } } diff --git a/lib/src/asset_loader.dart b/lib/src/asset_loader.dart deleted file mode 100644 index 9e6015d..0000000 --- a/lib/src/asset_loader.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'dart:ui'; - -// show AssetLoader; - -abstract class AssetLoader { - const AssetLoader(); - Future> load(String path, Locale locale); -} - -Locale localeFromString(String localeString) { - final localeList = localeString.split('_'); - switch (localeList.length) { - case 2: - return Locale(localeList.first, localeList.last); - case 3: - return Locale.fromSubtags( - languageCode: localeList.first, - scriptCode: localeList[1], - countryCode: localeList.last); - default: - return Locale(localeList.first); - } -} - -String localeToString(Locale locale, {String separator = '_'}) { - return locale.toString().split('_').join(separator); -} diff --git a/lib/src/csv_asset_loader.dart b/lib/src/csv_asset_loader.dart index 7c2c483..2a7ecfb 100644 --- a/lib/src/csv_asset_loader.dart +++ b/lib/src/csv_asset_loader.dart @@ -3,21 +3,28 @@ import 'dart:ui'; import 'package:csv/csv.dart'; import 'package:csv/csv_settings_autodetection.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/services.dart'; -import 'asset_loader.dart'; - // // load example/resources/langs/langs.csv // class CsvAssetLoader extends AssetLoader { CSVParser? csvParser; + final bool useAutodetect; + + CsvAssetLoader({ + this.useAutodetect = true, + }); @override Future> load(String path, Locale locale) async { if (csvParser == null) { log('easy localization loader: load csv file $path'); - csvParser = CSVParser(await rootBundle.loadString(path)); + csvParser = CSVParser( + await rootBundle.loadString(path), + useAutodetect: useAutodetect, + ); } else { log('easy localization loader: CSV parser already loaded, read cache'); } @@ -60,8 +67,10 @@ class CSVParser { csvSettingsDetector: useAutodetect && fieldDelimiter == null && eol == null ? FirstOccurrenceSettingsDetector( + fieldDelimiters: [',', ';', '\t'], + textDelimiters: ['"', "'", '”'], + textEndDelimiters: ['"', "'", '”'], eols: ['\r\n', '\n'], - fieldDelimiters: [',', '\t'], ) : null, ); diff --git a/lib/src/file_asset_loader.dart b/lib/src/file_asset_loader.dart index c52989d..8346843 100644 --- a/lib/src/file_asset_loader.dart +++ b/lib/src/file_asset_loader.dart @@ -3,13 +3,11 @@ import 'dart:developer'; import 'dart:io'; import 'dart:ui'; -import 'asset_loader.dart'; +import 'package:easy_localization/easy_localization.dart'; -// -// -// -// class FileAssetLoader extends AssetLoader { + const FileAssetLoader(); + @override Future> load(String path, Locale locale) async { final file = File(path); diff --git a/lib/src/http_asset_loader.dart b/lib/src/http_asset_loader.dart index 6e31e14..ecfe8c3 100644 --- a/lib/src/http_asset_loader.dart +++ b/lib/src/http_asset_loader.dart @@ -1,27 +1,24 @@ -// -// -// -// import 'dart:convert'; import 'dart:developer'; import 'dart:ui'; +import 'package:easy_localization/easy_localization.dart'; import 'package:http/http.dart' as http; -import 'asset_loader.dart'; - class HttpAssetLoader extends AssetLoader { + const HttpAssetLoader(); + @override Future> load(String path, Locale locale) async { log('easy localization loader: load http $path'); try { - var url = Uri.parse(path); + var url = Uri.parse('$path/${locale.toLanguageTag()}.json'); return http .get(url) - .then((response) => json.decode(response.body.toString())); + .then((response) => json.decode(utf8.decode(response.bodyBytes))); } catch (e) { //Catch network exceptions - return Future.value(); + return {}; } } } diff --git a/lib/src/json_asset_loader.dart b/lib/src/json_asset_loader.dart index aee968a..08e1cc5 100644 --- a/lib/src/json_asset_loader.dart +++ b/lib/src/json_asset_loader.dart @@ -2,13 +2,14 @@ import 'dart:convert'; import 'dart:developer'; import 'dart:ui'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/services.dart'; -import 'asset_loader.dart'; - class JsonAssetLoader extends AssetLoader { + const JsonAssetLoader(); + String getLocalePath(String basePath, Locale locale) { - return '$basePath/${localeToString(locale, separator: "-")}.json'; + return '$basePath/${locale.toStringWithSeparator(separator: "-")}.json'; } @override diff --git a/lib/src/smart_network_asset_loader.dart b/lib/src/smart_network_asset_loader.dart index bbe7e0c..315dabe 100644 --- a/lib/src/smart_network_asset_loader.dart +++ b/lib/src/smart_network_asset_loader.dart @@ -2,13 +2,12 @@ import 'dart:convert'; import 'dart:io'; import 'dart:ui'; import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:http/http.dart' as http; import 'package:path_provider/path_provider.dart' as paths; import 'package:flutter/services.dart'; -import 'asset_loader.dart'; - /// ```dart /// SmartNetworkAssetLoader( /// assetsPath: 'assets/translations', diff --git a/lib/src/tests_asset_loader.dart b/lib/src/tests_asset_loader.dart index f17c1fa..60958da 100644 --- a/lib/src/tests_asset_loader.dart +++ b/lib/src/tests_asset_loader.dart @@ -2,14 +2,15 @@ import 'dart:convert'; import 'dart:ui'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/services.dart'; -import 'asset_loader.dart'; - // asset loader to be used when doing integration tests // default AssetLoader suffers from this issue // https://github.com/flutter/flutter/issues/44182 class TestsAssetLoader extends AssetLoader { + const TestsAssetLoader(); + @override Future> load(String path, Locale locale) async { final byteData = await rootBundle.load(path); diff --git a/lib/src/xml_asset_loader.dart b/lib/src/xml_asset_loader.dart index 68b265b..a30a148 100644 --- a/lib/src/xml_asset_loader.dart +++ b/lib/src/xml_asset_loader.dart @@ -1,15 +1,16 @@ import 'dart:developer'; import 'dart:ui'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/services.dart'; import 'package:xml/xml.dart'; -import 'asset_loader.dart'; - //Loader for multiple xml files class XmlAssetLoader extends AssetLoader { + const XmlAssetLoader(); + String getLocalePath(String basePath, Locale locale) { - return '$basePath/${localeToString(locale, separator: "-")}.xml'; + return '$basePath/${locale.toStringWithSeparator(separator: "-")}.xml'; } @override @@ -50,7 +51,7 @@ Map convertXmlNodeToMap(XmlNode xmlNode) { if (entry is XmlElement) { switch (entry.children.length) { case 1: - map[entry.name.toString()] = entry.text; + map[entry.name.toString()] = entry.value; break; case 0: print(entry.name.toString()); diff --git a/lib/src/yaml_asset_loader.dart b/lib/src/yaml_asset_loader.dart index 52135b8..a4ddbf2 100644 --- a/lib/src/yaml_asset_loader.dart +++ b/lib/src/yaml_asset_loader.dart @@ -1,15 +1,16 @@ import 'dart:developer'; import 'dart:ui'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/services.dart'; import 'package:yaml/yaml.dart'; -import 'asset_loader.dart'; - //Loader for multiple yaml files class YamlAssetLoader extends AssetLoader { + const YamlAssetLoader(); + String getLocalePath(String basePath, Locale locale) { - return '$basePath/${localeToString(locale, separator: "-")}.yaml'; + return '$basePath/${locale.toStringWithSeparator(separator: "-")}.yaml'; } @override diff --git a/pubspec.yaml b/pubspec.yaml index 32a118c..fa54fa9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,22 +4,21 @@ description: Easy Localization Loader custom assets loaders for easy_localizatio homepage: https://github.com/aissat/easy_localization_loader issue_tracker: https://github.com/aissat/easy_localization_loader/issues -version: 1.0.1+1 +version: 2.0.1 environment: - sdk: '>=2.12.0-0 <3.0.0' + sdk: '>=2.12.0 <4.0.0' dependencies: - http: ^0.13.5 - csv: ^5.0.1 - yaml: ^3.1.1 + connectivity_plus: ^5.0.1 + csv: ^6.0.0 + easy_localization: ^3.0.3 + flutter: { sdk: flutter } + http: ^1.1.0 + path_provider: ^2.1.1 xml: ^6.1.0 - flutter: - sdk: flutter - connectivity_plus: ^2.3.7 - path_provider: ^2.0.11 + yaml: ^3.1.1 dev_dependencies: pedantic: ^1.11.1 test: ^1.21.6 - diff --git a/test/easy_localization_loader_test.dart b/test/easy_localization_loader_test.dart index 184340f..10b2bdb 100644 --- a/test/easy_localization_loader_test.dart +++ b/test/easy_localization_loader_test.dart @@ -13,6 +13,8 @@ void main() { 'str\ten_US\t$localeName\r\nscreen_language\tInterface language\tЯзык интерфейса\r\n'; const testCommaCRLFString = 'str,en_US,$localeName\r\nscreen_language,Interface language,Язык интерфейса\r\n'; + const testCommaQuotesInsideCRLFString = + 'str,en_US,$localeName\r\nscreen_language,"Interface language, Test","Язык интерфейса, Тест"\r\n'; Map getResult( String testString, @@ -45,6 +47,11 @@ void main() { {'screen_language': 'Язык интерфейса'}, getResult(testCommaCRLFString, localeName), ); + output( + 'testCommaQuotesInsideCRLFString'.toUpperCase(), + {'screen_language': 'Язык интерфейса, Тест'}, + getResult(testCommaQuotesInsideCRLFString, localeName, false), + ); output( 'testTabLFString, autodetect = false'.toUpperCase(), {},