diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 9709356..c9d89f9 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -8,7 +8,7 @@ on:
branches: [ main ]
env:
- FLUTTER_VERSION: '3.29.3'
+ FLUTTER_VERSION: '3.38.7'
jobs:
test:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 39ba272..679dea3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,29 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## 1.0.0
+
+- First stable release
+
+### Breaking Changes
+
+- **Minimum requirements updated**: Dart SDK `>=3.4.0 <4.0.0`, Flutter `>=3.22.0`
+- Default `synthetic-package` changed from `true` to `false` to align with Flutter 3.32+ deprecation
+- Import paths changed from `package:flutter_gen/gen_l10n/...` to direct source imports
+
+### Migration
+
+1. Add `synthetic-package: false` to your `l10n.yaml` (or rely on new default)
+2. Update imports:
+ ```dart
+ // Before
+ import 'package:flutter_gen/gen_l10n/crowdin_localizations.dart';
+
+ // After
+ import 'package:your_app/l10n/crowdin_localizations.dart';
+ ```
+3. Run `flutter pub run crowdin_sdk:gen` to regenerate
+
## 0.8.1
- fix: add undeclared placeholders to the placeholders list
diff --git a/README.md b/README.md
index 368baf4..2b042ba 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
-# Crowdin Flutter SDK [
](https://github.com/crowdin/flutter-sdk)
+# Crowdin Flutter SDK
The Crowdin Flutter SDK enables Over-The-Air (OTA) translation updates, delivering new translations from your Crowdin project directly to users without requiring app store updates. The SDK works on top of Flutter's standard localization system (`flutter_localizations`), providing a seamless bridge between your local ARB files and Crowdin's Content Delivery Network.
@@ -60,7 +60,8 @@ This architecture ensures your app always has working translations (from local A
## Requirements
-* Dart >=2.17.0
+* Flutter >=3.22.0
+* Dart >=3.4.0
## Setup
@@ -87,7 +88,7 @@ To manage distributions, open the Crowdin project and go to the *Translations* >
```yml
dependencies:
- crowdin_sdk: ^0.8.1
+ crowdin_sdk: ^1.0.0
flutter_localizations:
sdk: flutter
@@ -103,16 +104,16 @@ To manage distributions, open the Crowdin project and go to the *Translations* >
flutter pub run crowdin_sdk:gen
```
- This generates `crowdin_localizations.dart` in the `{FLUTTER_PROJECT}/.dart_tool/flutter_gen/gen_l10n` directory. This wrapper class extends Flutter's generated localization classes to integrate Crowdin OTA translations.
-
+ This generates `crowdin_localizations.dart` in the same directory as your `app_localizations.dart` (by default, in your `arb-dir` directory, e.g., `lib/l10n/`). This wrapper class extends Flutter's generated localization classes to integrate Crowdin OTA translations.
+
> **Important:** Re-run this command whenever you modify the structure of your ARB files (e.g., add/remove keys or change parameters).
- Update localizationsDelegates in your project:
```dart
- import 'package:flutter_gen/gen_l10n/app_localizations.dart';
+ import 'package:your_app/l10n/app_localizations.dart';
import 'package:crowdin_sdk/crowdin_sdk.dart';
- import 'package:flutter_gen/gen_l10n/crowdin_localizations.dart';
+ import 'package:your_app/l10n/crowdin_localizations.dart';
```
```dart
@@ -341,15 +342,10 @@ For more information about OAuth authorization in Crowdin, please check [this ar
- Swedish - `sv`: `sv-SE`
- Urdu (India) - `ur`: `ur-IN`
-- Since flutter tool no longer generate a synthetic package:flutter_gen, please follow 1st way from [Migration Guide](https://docs.flutter.dev/release/breaking-changes/flutter-generate-i10n-source#migration-guide):
- - Specify synthetic-package: false in the accompanying [l10n.yaml](https://docs.flutter.dev/ui/accessibility-and-internationalization/internationalization#configuring-the-l10n-yaml-file) file:
- `synthetic-package: false`
-
- - The files are generated into the path specified by arb-dir
- `arb-dir: lib/i18n`
-
- - Or, specifically provide an output path:
- `output-dir: lib/src/generated/i18n`
+- **synthetic-package default**: Starting from v1.0.0, the SDK defaults to `synthetic-package: false` to align with Flutter 3.32+ deprecation of the synthetic `flutter_gen` package. If you're upgrading from an earlier version, see the [Migration Guide](https://docs.flutter.dev/release/breaking-changes/flutter-generate-i10n-source#migration-guide):
+ - Add `synthetic-package: false` to your `l10n.yaml` (or rely on the new default)
+ - Update imports from `package:flutter_gen/gen_l10n/...` to your local path (e.g., `package:your_app/l10n/...`)
+ - Run `flutter pub run crowdin_sdk:gen` to regenerate
## Contributing
diff --git a/example/lib/main.dart b/example/lib/main.dart
index 51e5d2f..e18b6d5 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -2,8 +2,8 @@ import 'dart:convert';
import 'package:crowdin_sdk/crowdin_sdk.dart';
import 'package:flutter/material.dart';
-import 'package:flutter_gen/gen_l10n/app_localizations.dart';
-import 'package:flutter_gen/gen_l10n/crowdin_localizations.dart';
+import 'l10n/app_localizations.dart';
+import 'l10n/crowdin_localizations.dart';
import 'package:intl/intl.dart';
diff --git a/example/pubspec.lock b/example/pubspec.lock
index 269e8d7..6992b22 100644
--- a/example/pubspec.lock
+++ b/example/pubspec.lock
@@ -103,7 +103,7 @@ packages:
path: ".."
relative: true
source: path
- version: "0.8.1"
+ version: "1.0.0"
crypto:
dependency: transitive
description:
@@ -132,10 +132,10 @@ packages:
dependency: transitive
description:
name: fake_async
- sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
+ sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.dev"
source: hosted
- version: "1.3.2"
+ version: "1.3.3"
ffi:
dependency: transitive
description:
@@ -208,34 +208,34 @@ packages:
dependency: "direct main"
description:
name: intl
- sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
+ sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
url: "https://pub.dev"
source: hosted
- version: "0.19.0"
+ version: "0.20.2"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
- sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
+ sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
url: "https://pub.dev"
source: hosted
- version: "10.0.8"
+ version: "11.0.2"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
- sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
+ sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
url: "https://pub.dev"
source: hosted
- version: "3.0.9"
+ version: "3.0.10"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
- sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
+ sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
url: "https://pub.dev"
source: hosted
- version: "3.0.1"
+ version: "3.0.2"
lints:
dependency: transitive
description:
@@ -264,10 +264,10 @@ packages:
dependency: transitive
description:
name: meta
- sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
+ sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
url: "https://pub.dev"
source: hosted
- version: "1.16.0"
+ version: "1.17.0"
nm:
dependency: transitive
description:
@@ -445,10 +445,10 @@ packages:
dependency: transitive
description:
name: test_api
- sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
+ sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
url: "https://pub.dev"
source: hosted
- version: "0.7.4"
+ version: "0.7.7"
typed_data:
dependency: transitive
description:
@@ -525,10 +525,10 @@ packages:
dependency: transitive
description:
name: vector_math
- sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
+ sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
url: "https://pub.dev"
source: hosted
- version: "2.1.4"
+ version: "2.2.0"
vm_service:
dependency: transitive
description:
@@ -586,5 +586,5 @@ packages:
source: hosted
version: "3.1.3"
sdks:
- dart: ">=3.7.0 <4.0.0"
+ dart: ">=3.8.0-0 <4.0.0"
flutter: ">=3.27.0"
diff --git a/lib/src/common/gen_l10n_types.dart b/lib/src/common/gen_l10n_types.dart
index cbddfe7..34b4b1c 100644
--- a/lib/src/common/gen_l10n_types.dart
+++ b/lib/src/common/gen_l10n_types.dart
@@ -459,7 +459,7 @@ class Message {
static final RegExp _pluralRE = RegExp(r'\s*\{([\w\s,]*),\s*plural\s*,');
- bool get isPlural => _pluralMatch != null && _pluralMatch!.groupCount == 1;
+ bool get isPlural => _pluralMatch != null && _pluralMatch.groupCount == 1;
Placeholder getCountPlaceholder() {
assert(isPlural);
diff --git a/lib/src/crowdin_request_limiter.dart b/lib/src/crowdin_request_limiter.dart
index 95959e2..9eb27be 100644
--- a/lib/src/crowdin_request_limiter.dart
+++ b/lib/src/crowdin_request_limiter.dart
@@ -23,7 +23,7 @@ class CrowdinRequestLimiter {
bool get pauseRequests =>
_stopPermanently || _pauseRequests || _checkIsPausedForToday();
- init(CrowdinStorage storage) {
+ void init(CrowdinStorage storage) async {
_storage = storage;
_stopPermanently = _storage.getIsPausedPermanently() ?? false;
_errorMap = _storage.getErrorMap() ?? {};
@@ -59,7 +59,7 @@ class CrowdinRequestLimiter {
_storage.setErrorMap(_cleanErrorMapFromUnusedDays());
}
- reset() {
+ void reset() {
if (!_stopPermanently) {
_pauseRequests = false;
_errorMap = {};
diff --git a/lib/src/gen/l10n_config.dart b/lib/src/gen/l10n_config.dart
index bae67e8..1c8d36f 100644
--- a/lib/src/gen/l10n_config.dart
+++ b/lib/src/gen/l10n_config.dart
@@ -18,7 +18,7 @@ class L10nConfig {
required this.outputLocalizationFile,
required this.outputDir,
required this.outputClass,
- this.syntheticPackage = true,
+ this.syntheticPackage = false,
});
String get finalOutputDir => syntheticPackage
@@ -41,7 +41,7 @@ class L10nConfig {
String outputClass = yamlGenConfig['output-class'] ?? 'AppLocalizations';
- bool syntheticPackage = yamlGenConfig['synthetic-package'] ?? true;
+ bool syntheticPackage = yamlGenConfig['synthetic-package'] ?? false;
return L10nConfig(
arbDir: arbDir,
diff --git a/lib/src/real_time_preview/crowdin_preview_manager.dart b/lib/src/real_time_preview/crowdin_preview_manager.dart
index a531361..483ac0c 100644
--- a/lib/src/real_time_preview/crowdin_preview_manager.dart
+++ b/lib/src/real_time_preview/crowdin_preview_manager.dart
@@ -145,12 +145,11 @@ class CrowdinPreviewManager {
'Something went wrong when subscribing to translations for real-time preview. Metadata is not provided');
} else {
_CrowdinMetadata metadata = _metadata!;
- final webSocketTicketEvent =
- 'update-draft:${metadata.wsHash}:${metadata.projectId}:${metadata.userId}';
- final ticket = await _getWebsocketTicket(
- credentials: _credentials, event: webSocketTicketEvent);
for (var id in finalMapping.values) {
- final String event = '$webSocketTicketEvent:$langCode:$id';
+ final String event =
+ 'update-draft:${metadata.wsHash}:${metadata.projectId}:${metadata.userId}:$langCode:$id';
+ final ticket =
+ await _getWebsocketTicket(credentials: _credentials, event: event);
if (ticket != null) {
var data = jsonEncode({
'action': 'subscribe',
diff --git a/pubspec.yaml b/pubspec.yaml
index bdae595..7e03f91 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,12 +1,12 @@
name: crowdin_sdk
description: Crowdin Flutter SDK for instant translation delivery Over-The-Air directly to your application
-version: 0.8.1
+version: 1.0.0
repository: https://github.com/crowdin/flutter-sdk
homepage: https://github.com/crowdin/flutter-sdk
environment:
- sdk: '>=2.17.0 <4.0.0'
- flutter: ">=1.17.0"
+ sdk: '>=3.4.0 <4.0.0'
+ flutter: ">=3.22.0"
dependencies:
flutter:
@@ -14,13 +14,13 @@ dependencies:
http: '>=0.13.3 <2.0.0'
intl: '>=0.17.0 <=0.20.2'
shared_preferences: ^2.0.4
- connectivity_plus: '>=2.1.0 <7.0.0'
+ connectivity_plus: '>=2.1.0 <8.0.0'
yaml: ^3.1.1
meta: ^1.7.0
path: ^1.8.1
oauth2: ^2.0.1
url_launcher: ^6.1.0
- app_links: '>=3.4.2 <7.0.0'
+ app_links: '>=3.4.2 <8.0.0'
web_socket_channel: '>=2.2.0 <4.0.0'
flutter_localizations:
diff --git a/test/crowdin_api_test.dart b/test/crowdin_api_test.dart
index 4c330bb..e2b7b97 100644
--- a/test/crowdin_api_test.dart
+++ b/test/crowdin_api_test.dart
@@ -101,7 +101,7 @@ void main() {
test('getManifest returns null and increment error count on 400 status',
() async {
- await requestLimiter.init(storage);
+ requestLimiter.init(storage);
final uri =
Uri.parse('https://distributions.crowdin.net/hash/manifest.json');
@@ -119,7 +119,7 @@ void main() {
'getManifest returns null and do not call request when requests paused',
() async {
storage.setIsPausedPermanently(true);
- await requestLimiter.init(storage);
+ requestLimiter.init(storage);
final uri =
Uri.parse('https://distributions.crowdin.net/hash/manifest.json');
diff --git a/test/crowdin_request_limiter_test.dart b/test/crowdin_request_limiter_test.dart
index 8279f89..6f94785 100644
--- a/test/crowdin_request_limiter_test.dart
+++ b/test/crowdin_request_limiter_test.dart
@@ -34,7 +34,7 @@ void main() {
test('should initialize with storage values', () async {
storage.setIsPausedPermanently(true);
- await requestLimiter.init(storage);
+ requestLimiter.init(storage);
expect(storage.getIsPausedPermanently(), true);
expect(requestLimiter.pauseRequests, true);
});
@@ -49,13 +49,13 @@ void main() {
test('should pause requests after max errors in a day', () async {
storage.setErrorMap({getTodayDateString(): 10});
- await requestLimiter.init(storage);
+ requestLimiter.init(storage);
expect(requestLimiter.pauseRequests, true);
});
test('should reset error map and pause state', () async {
storage.setErrorMap({getTodayDateString(): 10});
- await requestLimiter.init(storage);
+ requestLimiter.init(storage);
expect(requestLimiter.pauseRequests, true);
requestLimiter.reset();
expect(requestLimiter.pauseRequests, false);
@@ -67,7 +67,7 @@ void main() {
_formatter.format(DateTime.now().subtract(const Duration(days: 1))): 10,
_formatter.format(DateTime.now().subtract(const Duration(days: 2))): 10,
});
- await requestLimiter.init(storage);
+ requestLimiter.init(storage);
requestLimiter.incrementErrorCounter();
expect(requestLimiter.pauseRequests, true);
});