diff --git a/.flutter-plugins b/.flutter-plugins deleted file mode 100644 index 88614b0..0000000 --- a/.flutter-plugins +++ /dev/null @@ -1,17 +0,0 @@ -# This is a generated file; do not edit or check into version control. -flutter_secure_storage=E:\\Sdk\\cache\\hosted\\pub.dev\\flutter_secure_storage-9.2.4\\ -flutter_secure_storage_linux=E:\\Sdk\\cache\\hosted\\pub.dev\\flutter_secure_storage_linux-1.2.2\\ -flutter_secure_storage_macos=E:\\Sdk\\cache\\hosted\\pub.dev\\flutter_secure_storage_macos-3.1.3\\ -flutter_secure_storage_web=E:\\Sdk\\cache\\hosted\\pub.dev\\flutter_secure_storage_web-1.2.1\\ -flutter_secure_storage_windows=E:\\Sdk\\cache\\hosted\\pub.dev\\flutter_secure_storage_windows-3.1.2\\ -path_provider=E:\\Sdk\\cache\\hosted\\pub.dev\\path_provider-2.0.15\\ -path_provider_android=E:\\Sdk\\cache\\hosted\\pub.dev\\path_provider_android-2.0.27\\ -path_provider_foundation=E:\\Sdk\\cache\\hosted\\pub.dev\\path_provider_foundation-2.2.4\\ -path_provider_linux=E:\\Sdk\\cache\\hosted\\pub.dev\\path_provider_linux-2.1.11\\ -path_provider_windows=E:\\Sdk\\cache\\hosted\\pub.dev\\path_provider_windows-2.1.7\\ -shared_preferences=E:\\Sdk\\cache\\hosted\\pub.dev\\shared_preferences-2.3.5\\ -shared_preferences_android=E:\\Sdk\\cache\\hosted\\pub.dev\\shared_preferences_android-2.4.0\\ -shared_preferences_foundation=E:\\Sdk\\cache\\hosted\\pub.dev\\shared_preferences_foundation-2.5.2\\ -shared_preferences_linux=E:\\Sdk\\cache\\hosted\\pub.dev\\shared_preferences_linux-2.4.1\\ -shared_preferences_web=E:\\Sdk\\cache\\hosted\\pub.dev\\shared_preferences_web-2.4.2\\ -shared_preferences_windows=E:\\Sdk\\cache\\hosted\\pub.dev\\shared_preferences_windows-2.4.1\\ diff --git a/.flutter-plugins-dependencies b/.flutter-plugins-dependencies deleted file mode 100644 index 85a21cd..0000000 --- a/.flutter-plugins-dependencies +++ /dev/null @@ -1 +0,0 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_secure_storage","path":"E:\\\\Sdk\\\\cache\\\\hosted\\\\pub.dev\\\\flutter_secure_storage-9.2.4\\\\","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"E:\\\\Sdk\\\\cache\\\\hosted\\\\pub.dev\\\\path_provider_foundation-2.2.4\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"E:\\\\Sdk\\\\cache\\\\hosted\\\\pub.dev\\\\shared_preferences_foundation-2.5.2\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"android":[{"name":"flutter_secure_storage","path":"E:\\\\Sdk\\\\cache\\\\hosted\\\\pub.dev\\\\flutter_secure_storage-9.2.4\\\\","native_build":true,"dependencies":[]},{"name":"path_provider_android","path":"E:\\\\Sdk\\\\cache\\\\hosted\\\\pub.dev\\\\path_provider_android-2.0.27\\\\","native_build":true,"dependencies":[]},{"name":"shared_preferences_android","path":"E:\\\\Sdk\\\\cache\\\\hosted\\\\pub.dev\\\\shared_preferences_android-2.4.0\\\\","native_build":true,"dependencies":[]}],"macos":[{"name":"flutter_secure_storage_macos","path":"E:\\\\Sdk\\\\cache\\\\hosted\\\\pub.dev\\\\flutter_secure_storage_macos-3.1.3\\\\","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"E:\\\\Sdk\\\\cache\\\\hosted\\\\pub.dev\\\\path_provider_foundation-2.2.4\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"E:\\\\Sdk\\\\cache\\\\hosted\\\\pub.dev\\\\shared_preferences_foundation-2.5.2\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"linux":[{"name":"flutter_secure_storage_linux","path":"E:\\\\Sdk\\\\cache\\\\hosted\\\\pub.dev\\\\flutter_secure_storage_linux-1.2.2\\\\","native_build":true,"dependencies":[]},{"name":"path_provider_linux","path":"E:\\\\Sdk\\\\cache\\\\hosted\\\\pub.dev\\\\path_provider_linux-2.1.11\\\\","native_build":false,"dependencies":[]},{"name":"shared_preferences_linux","path":"E:\\\\Sdk\\\\cache\\\\hosted\\\\pub.dev\\\\shared_preferences_linux-2.4.1\\\\","native_build":false,"dependencies":["path_provider_linux"]}],"windows":[{"name":"flutter_secure_storage_windows","path":"E:\\\\Sdk\\\\cache\\\\hosted\\\\pub.dev\\\\flutter_secure_storage_windows-3.1.2\\\\","native_build":true,"dependencies":[]},{"name":"path_provider_windows","path":"E:\\\\Sdk\\\\cache\\\\hosted\\\\pub.dev\\\\path_provider_windows-2.1.7\\\\","native_build":false,"dependencies":[]},{"name":"shared_preferences_windows","path":"E:\\\\Sdk\\\\cache\\\\hosted\\\\pub.dev\\\\shared_preferences_windows-2.4.1\\\\","native_build":false,"dependencies":["path_provider_windows"]}],"web":[{"name":"flutter_secure_storage_web","path":"E:\\\\Sdk\\\\cache\\\\hosted\\\\pub.dev\\\\flutter_secure_storage_web-1.2.1\\\\","dependencies":[]},{"name":"shared_preferences_web","path":"E:\\\\Sdk\\\\cache\\\\hosted\\\\pub.dev\\\\shared_preferences_web-2.4.2\\\\","dependencies":[]}]},"dependencyGraph":[{"name":"flutter_secure_storage","dependencies":["flutter_secure_storage_linux","flutter_secure_storage_macos","flutter_secure_storage_web","flutter_secure_storage_windows"]},{"name":"flutter_secure_storage_linux","dependencies":[]},{"name":"flutter_secure_storage_macos","dependencies":[]},{"name":"flutter_secure_storage_web","dependencies":[]},{"name":"flutter_secure_storage_windows","dependencies":["path_provider"]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]}],"date_created":"2025-01-19 20:43:59.275999","version":"3.27.2","swift_package_manager_enabled":false} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 96486fd..5c5c63d 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,5 @@ migrate_working_dir/ .dart_tool/ .packages build/ +.flutter-plugins-dependencies +.flutter-plugins diff --git a/CHANGELOG.md b/CHANGELOG.md index 491c1ab..bea86bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,43 @@ # Changelog +## 0.3.0 + +### Enhancements + +* **Improved Delegate Configuration**: + + * Added `extraDelegates` to inject custom localization delegates. + * Added `customLocalizationDelegateBuilder` to fully customize the delegate list. +* **Logging Enhancements**: + + * Added `logLocaleChanges` toggle in `PlayxLocaleConfig`. + * Improved logs to show locale name, value, and index. + +* Removed `easy_localization` dependency; replaced with a custom, flexible system. + +### New Extensions + +#### `BuildContext` Locale Helpers + +* Easily access locale info like `currentLocale`, `localeTag`, `languageCode`, `isRtl`, `fontFamily`, and more. + +#### Number Extensions + +* Enhanced currency formatting with `toFormattedCurrencyNumber` and `toLocalizedCurrencyNumber`. +* Added format options to all localized number methods. +* New `isZero` utility for floating-point comparisons. + +#### String Localization Extensions + +* Check for RTL/LTR, Arabic/English text, numbers, and diacritics. +* Convert digits between Arabic and Western formats. +* Normalize Arabic letters and clean up extra spaces. + +#### Locale Utilities + +* New extensions for `Locale`, `XLocale`, and `String` for parsing and formatting. + + ## 0.2.2 - Update packages. diff --git a/example/.flutter-plugins-dependencies b/example/.flutter-plugins-dependencies new file mode 100644 index 0000000..36ba674 --- /dev/null +++ b/example/.flutter-plugins-dependencies @@ -0,0 +1 @@ +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_secure_storage","path":"/Users/basemosama/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/basemosama/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.1/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"/Users/basemosama/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.4/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"flutter_secure_storage","path":"/Users/basemosama/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_android","path":"/Users/basemosama/.pub-cache/hosted/pub.dev/path_provider_android-2.2.17/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_android","path":"/Users/basemosama/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.10/","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"flutter_secure_storage_macos","path":"/Users/basemosama/.pub-cache/hosted/pub.dev/flutter_secure_storage_macos-3.1.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/basemosama/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.1/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"/Users/basemosama/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.4/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"flutter_secure_storage_linux","path":"/Users/basemosama/.pub-cache/hosted/pub.dev/flutter_secure_storage_linux-1.2.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_linux","path":"/Users/basemosama/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_linux","path":"/Users/basemosama/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/","native_build":false,"dependencies":["path_provider_linux"],"dev_dependency":false}],"windows":[{"name":"flutter_secure_storage_windows","path":"/Users/basemosama/.pub-cache/hosted/pub.dev/flutter_secure_storage_windows-3.1.2/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_windows","path":"/Users/basemosama/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_windows","path":"/Users/basemosama/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/","native_build":false,"dependencies":["path_provider_windows"],"dev_dependency":false}],"web":[{"name":"flutter_secure_storage_web","path":"/Users/basemosama/.pub-cache/hosted/pub.dev/flutter_secure_storage_web-1.2.1/","dependencies":[],"dev_dependency":false},{"name":"shared_preferences_web","path":"/Users/basemosama/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.3/","dependencies":[],"dev_dependency":false}]},"dependencyGraph":[{"name":"flutter_secure_storage","dependencies":["flutter_secure_storage_linux","flutter_secure_storage_macos","flutter_secure_storage_web","flutter_secure_storage_windows"]},{"name":"flutter_secure_storage_linux","dependencies":[]},{"name":"flutter_secure_storage_macos","dependencies":[]},{"name":"flutter_secure_storage_web","dependencies":[]},{"name":"flutter_secure_storage_windows","dependencies":["path_provider"]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]}],"date_created":"2025-07-01 14:18:51.931116","version":"3.32.5","swift_package_manager_enabled":{"ios":false,"macos":false}} \ No newline at end of file diff --git a/example/.gitignore b/example/.gitignore index 24476c5..8e14b83 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -5,9 +5,11 @@ *.swp .DS_Store .atom/ +.build/ .buildlog/ .history .svn/ +.swiftpm/ migrate_working_dir/ # IntelliJ related @@ -42,3 +44,4 @@ app.*.map.json /android/app/debug /android/app/profile /android/app/release +!/.flutter-plugins-dependencies diff --git a/example/.metadata b/example/.metadata index b02a7e4..603c641 100644 --- a/example/.metadata +++ b/example/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: "17025dd88227cd9532c33fa78f5250d548d87e9a" + revision: "6fba2447e95c451518584c35e25f5433f14d888c" channel: "stable" project_type: app @@ -13,26 +13,26 @@ project_type: app migration: platforms: - platform: root - create_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a - base_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + create_revision: 6fba2447e95c451518584c35e25f5433f14d888c + base_revision: 6fba2447e95c451518584c35e25f5433f14d888c - platform: android - create_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a - base_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + create_revision: 6fba2447e95c451518584c35e25f5433f14d888c + base_revision: 6fba2447e95c451518584c35e25f5433f14d888c - platform: ios - create_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a - base_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + create_revision: 6fba2447e95c451518584c35e25f5433f14d888c + base_revision: 6fba2447e95c451518584c35e25f5433f14d888c - platform: linux - create_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a - base_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + create_revision: 6fba2447e95c451518584c35e25f5433f14d888c + base_revision: 6fba2447e95c451518584c35e25f5433f14d888c - platform: macos - create_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a - base_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + create_revision: 6fba2447e95c451518584c35e25f5433f14d888c + base_revision: 6fba2447e95c451518584c35e25f5433f14d888c - platform: web - create_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a - base_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + create_revision: 6fba2447e95c451518584c35e25f5433f14d888c + base_revision: 6fba2447e95c451518584c35e25f5433f14d888c - platform: windows - create_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a - base_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a + create_revision: 6fba2447e95c451518584c35e25f5433f14d888c + base_revision: 6fba2447e95c451518584c35e25f5433f14d888c # User provided section diff --git a/example/android/.gitignore b/example/android/.gitignore index 55afd91..be3943c 100644 --- a/example/android/.gitignore +++ b/example/android/.gitignore @@ -5,6 +5,7 @@ gradle-wrapper.jar /gradlew.bat /local.properties GeneratedPluginRegistrant.java +.cxx/ # Remember to never publicly share your keystore. # See https://flutter.dev/to/reference-keystore diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle.kts similarity index 63% rename from example/android/app/build.gradle rename to example/android/app/build.gradle.kts index d2a9c4c..c57c1f9 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle.kts @@ -1,8 +1,8 @@ plugins { - id "com.android.application" - id "kotlin-android" + id("com.android.application") + id("kotlin-android") // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. - id "dev.flutter.flutter-gradle-plugin" + id("dev.flutter.flutter-gradle-plugin") } android { @@ -11,17 +11,17 @@ android { ndkVersion = flutter.ndkVersion compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8 + jvmTarget = JavaVersion.VERSION_17.toString() } defaultConfig { - applicationId "io.sourcya.playx.localization" - minSdk = flutter.minSdkVersion + applicationId = "io.sourcya.playx.localization.example" + minSdk = 23 targetSdk = flutter.targetSdkVersion versionCode = flutter.versionCode versionName = flutter.versionName @@ -31,7 +31,7 @@ android { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig = signingConfigs.debug + signingConfig = signingConfigs.getByName("debug") } } } diff --git a/example/android/app/src/main/kotlin/com/example/playx_localization_example/MainActivity.kt b/example/android/app/src/main/kotlin/com/example/playx_localization_example/MainActivity.kt index c5f281e..f6476a8 100644 --- a/example/android/app/src/main/kotlin/com/example/playx_localization_example/MainActivity.kt +++ b/example/android/app/src/main/kotlin/com/example/playx_localization_example/MainActivity.kt @@ -2,4 +2,4 @@ package com.example.playx_localization_example import io.flutter.embedding.android.FlutterActivity -class MainActivity: FlutterActivity() +class MainActivity : FlutterActivity() diff --git a/example/android/build.gradle b/example/android/build.gradle deleted file mode 100644 index d2ffbff..0000000 --- a/example/android/build.gradle +++ /dev/null @@ -1,18 +0,0 @@ -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = "../build" -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(":app") -} - -tasks.register("clean", Delete) { - delete rootProject.buildDir -} diff --git a/example/android/build.gradle.kts b/example/android/build.gradle.kts new file mode 100644 index 0000000..89176ef --- /dev/null +++ b/example/android/build.gradle.kts @@ -0,0 +1,21 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/example/android/gradle.properties b/example/android/gradle.properties index 2597170..f018a61 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -1,3 +1,3 @@ -org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true android.enableJetifier=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index 7bb2df6..348c409 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle deleted file mode 100644 index a42444d..0000000 --- a/example/android/settings.gradle +++ /dev/null @@ -1,25 +0,0 @@ -pluginManagement { - def flutterSdkPath = { - def properties = new Properties() - file("local.properties").withInputStream { properties.load(it) } - def flutterSdkPath = properties.getProperty("flutter.sdk") - assert flutterSdkPath != null, "flutter.sdk not set in local.properties" - return flutterSdkPath - }() - - includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") - - repositories { - google() - mavenCentral() - gradlePluginPortal() - } -} - -plugins { - id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "8.2.1" apply false - id "org.jetbrains.kotlin.android" version "1.8.22" apply false -} - -include ":app" diff --git a/example/android/settings.gradle.kts b/example/android/settings.gradle.kts new file mode 100644 index 0000000..ab39a10 --- /dev/null +++ b/example/android/settings.gradle.kts @@ -0,0 +1,25 @@ +pluginManagement { + val flutterSdkPath = run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "8.7.3" apply false + id("org.jetbrains.kotlin.android") version "2.1.0" apply false +} + +include(":app") diff --git a/example/ios/Flutter/Debug.xcconfig b/example/ios/Flutter/Debug.xcconfig index 592ceee..ec97fc6 100644 --- a/example/ios/Flutter/Debug.xcconfig +++ b/example/ios/Flutter/Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/example/ios/Flutter/Release.xcconfig b/example/ios/Flutter/Release.xcconfig index 592ceee..c4855bf 100644 --- a/example/ios/Flutter/Release.xcconfig +++ b/example/ios/Flutter/Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/example/ios/Podfile b/example/ios/Podfile new file mode 100644 index 0000000..e549ee2 --- /dev/null +++ b/example/ios/Podfile @@ -0,0 +1,43 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '12.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index ecf8573..f109a11 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -362,6 +362,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = B996ZRP255; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -541,6 +542,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = B996ZRP255; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -563,6 +565,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = B996ZRP255; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 8e3ca5d..e3773d4 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -26,6 +26,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" shouldUseLaunchSchemeArgsEnv = "YES"> diff --git a/example/lib/main.dart b/example/lib/main.dart index 8e94089..30daaf5 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,125 +1,126 @@ import 'package:flutter/material.dart'; +import 'package:playx_localization/playx_localization.dart'; +import 'package:playx_localization_example/translation/app_trans.dart'; + +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + await PlayxCore.bootCore(); + + const locales = [ + XLocale(id: 'en', name: 'English', languageCode: 'en'), + XLocale(id: 'ar', name: 'العربية', languageCode: 'ar'), + ]; + + final config = PlayxLocaleConfig( + supportedLocales: locales, + startLocale: locales.first, + fallbackLocale: locales.first, + useFallbackTranslations: true, + ); + await PlayxLocalization.boot(config: config); -void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); - // This widget is the root of your application. @override Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - // This is the theme of your application. - // - // TRY THIS: Try running your application with "flutter run". You'll see - // the application has a purple toolbar. Then, without quitting the app, - // try changing the seedColor in the colorScheme below to Colors.green - // and then invoke "hot reload" (save your changes or press the "hot - // reload" button in a Flutter-supported IDE, or press "r" if you used - // the command line to start the app). - // - // Notice that the counter didn't reset back to zero; the application - // state is not lost during the reload. To reset the state, use hot - // restart instead. - // - // This works for code too, not just values: Most code changes can be - // tested with just a hot reload. - colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), - useMaterial3: true, - ), - home: const MyHomePage(title: 'Flutter Demo Home Page'), - ); + return PlayxLocalizationBuilder(builder: (_, locale) { + return MaterialApp( + supportedLocales: PlayxLocalization.supportedLocales, + localizationsDelegates: PlayxLocalization.localizationDelegates, + locale: locale.locale, + home: const MyHomePage(), + ); + }); } } -class MyHomePage extends StatefulWidget { - const MyHomePage({super.key, required this.title}); - - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - - final String title; - - @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; - }); - } +class MyHomePage extends StatelessWidget { + const MyHomePage({super.key}); @override Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. + print( + 'Current Locale: ${PlayxLocalization.currentLocale} context :${context.locale.toStringWithSeparator()}'); return Scaffold( - appBar: AppBar( - // TRY THIS: Try changing the color here to a specific color (to - // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar - // change color while the other colors stay the same. - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget.title), - ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - // - // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint" - // action in the IDE, or press "p" in the console), to see the - // wireframe for each widget. - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'You have pushed the button this many times:', - ), - Text( - '$_counter', - style: Theme.of(context).textTheme.headlineMedium, + appBar: AppBar( + title: Text( + AppTrans.changeLanguageTitle.tr(context: context), + ), + ), + body: SafeArea( + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + '${context.tr(AppTrans.changeLanguageTitle)} : بلاي', + style: TextStyle( + fontSize: 18, + color: + ('${context.tr(AppTrans.changeLanguageTitle)} : بلاي') + .isArabic + ? Colors.blueAccent + : Colors.black), + ), + const SizedBox( + height: 20, + ), + ElevatedButton( + onPressed: () { + showDialog( + context: context, + builder: (ctx) => Center( + child: Card( + margin: const EdgeInsets.all(8), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + AppTrans.changeLanguageTitle + .tr(context: context), + style: const TextStyle(fontSize: 20), + ), + ...PlayxLocalization.supportedXLocales + .map((e) => ListTile( + onTap: () { + PlayxLocalization.updateById( + e.id, + forceAppUpdate: false); + Navigator.pop(ctx); + }, + title: Text(e.name), + trailing: PlayxLocalization + .currentXLocale.id == + e.id + ? const Icon( + Icons.done, + color: Colors.lightBlue, + ) + : const SizedBox.shrink(), + )), + ], + ), + ))); + }, + child: Text(AppTrans.chooseLanguage.tr(context: context))) + ], ), - ], + ), ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. - ); + floatingActionButton: FloatingActionButton.extended( + onPressed: () { + PlayxLocalization.updateByIndex( + PlayxLocalization.isCurrentLocaleArabic() ? 0 : 1, + forceAppUpdate: false); + }, + label: Text( + 'change_language_title'.tr(context: context), + ), + icon: const Icon(Icons.update), + )); } } diff --git a/example/lib/translation/app_trans.dart b/example/lib/translation/app_trans.dart new file mode 100644 index 0000000..07dabd5 --- /dev/null +++ b/example/lib/translation/app_trans.dart @@ -0,0 +1,10 @@ +// DO NOT EDIT. This is code generated via package:easy_localization/generate.dart + +abstract class AppTrans { + AppTrans._(); + + static const appName = 'app_name'; + static const chooseLanguage = 'choose_language'; + static const changeLanguageTitle = 'change_language_title'; + static const title = 'title'; +} diff --git a/example/macos/.gitignore b/example/macos/.gitignore index 746adbb..21a0d06 100644 --- a/example/macos/.gitignore +++ b/example/macos/.gitignore @@ -5,3 +5,4 @@ # Xcode-related **/dgph **/xcuserdata/ +/Podfile.lock diff --git a/example/macos/Flutter/Flutter-Debug.xcconfig b/example/macos/Flutter/Flutter-Debug.xcconfig index c2efd0b..4b81f9b 100644 --- a/example/macos/Flutter/Flutter-Debug.xcconfig +++ b/example/macos/Flutter/Flutter-Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig" diff --git a/example/macos/Flutter/Flutter-Release.xcconfig b/example/macos/Flutter/Flutter-Release.xcconfig index c2efd0b..5caa9d1 100644 --- a/example/macos/Flutter/Flutter-Release.xcconfig +++ b/example/macos/Flutter/Flutter-Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig" diff --git a/example/macos/Podfile b/example/macos/Podfile new file mode 100644 index 0000000..29c8eb3 --- /dev/null +++ b/example/macos/Podfile @@ -0,0 +1,42 @@ +platform :osx, '10.14' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/example/macos/Runner.xcodeproj/project.pbxproj b/example/macos/Runner.xcodeproj/project.pbxproj index aee9050..2478db5 100644 --- a/example/macos/Runner.xcodeproj/project.pbxproj +++ b/example/macos/Runner.xcodeproj/project.pbxproj @@ -27,6 +27,8 @@ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 8B44AA877385D68EC44D5391 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 131C8BFB6EFD785F48B1CD75 /* Pods_RunnerTests.framework */; }; + C0386B32D6070ADD8290B582 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C4A75E8D5063ABAC3506EBEF /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -60,11 +62,13 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 12B6DA19D15460159D3C28F4 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 131C8BFB6EFD785F48B1CD75 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* playx_localization_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "playx_localization_example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10ED2044A3C60003C045 /* playx_localization_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = playx_localization_example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; @@ -76,8 +80,14 @@ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 34CAEC91CC0D5EABB0F07A0B /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + 9B24FF06405C1D7B37A85299 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + B9539FC04843A34568666C5B /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + C4A75E8D5063ABAC3506EBEF /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C4FDF5DC35CBE98629384323 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + C6D25D62760F056B7A8FCA8F /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -85,6 +95,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 8B44AA877385D68EC44D5391 /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -92,6 +103,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + C0386B32D6070ADD8290B582 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -125,6 +137,7 @@ 331C80D6294CF71000263BE5 /* RunnerTests */, 33CC10EE2044A3C60003C045 /* Products */, D73912EC22F37F3D000D13A0 /* Frameworks */, + 55B9EC6008FF5A7ED8E13E22 /* Pods */, ); sourceTree = ""; }; @@ -172,9 +185,25 @@ path = Runner; sourceTree = ""; }; + 55B9EC6008FF5A7ED8E13E22 /* Pods */ = { + isa = PBXGroup; + children = ( + B9539FC04843A34568666C5B /* Pods-Runner.debug.xcconfig */, + 9B24FF06405C1D7B37A85299 /* Pods-Runner.release.xcconfig */, + 34CAEC91CC0D5EABB0F07A0B /* Pods-Runner.profile.xcconfig */, + C6D25D62760F056B7A8FCA8F /* Pods-RunnerTests.debug.xcconfig */, + 12B6DA19D15460159D3C28F4 /* Pods-RunnerTests.release.xcconfig */, + C4FDF5DC35CBE98629384323 /* Pods-RunnerTests.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; D73912EC22F37F3D000D13A0 /* Frameworks */ = { isa = PBXGroup; children = ( + C4A75E8D5063ABAC3506EBEF /* Pods_Runner.framework */, + 131C8BFB6EFD785F48B1CD75 /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -186,6 +215,7 @@ isa = PBXNativeTarget; buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( + 156CD16E2CA168FE8400C308 /* [CP] Check Pods Manifest.lock */, 331C80D1294CF70F00263BE5 /* Sources */, 331C80D2294CF70F00263BE5 /* Frameworks */, 331C80D3294CF70F00263BE5 /* Resources */, @@ -204,11 +234,13 @@ isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 85F6AC850285891EA27470F9 /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, + 3ECC30B42FC4F8B10FD7A893 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -291,6 +323,28 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 156CD16E2CA168FE8400C308 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 3399D490228B24CF009A79C7 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -329,6 +383,45 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; + 3ECC30B42FC4F8B10FD7A893 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 85F6AC850285891EA27470F9 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -380,6 +473,7 @@ /* Begin XCBuildConfiguration section */ 331C80DB294CF71000263BE5 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = C6D25D62760F056B7A8FCA8F /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; @@ -394,6 +488,7 @@ }; 331C80DC294CF71000263BE5 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 12B6DA19D15460159D3C28F4 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; @@ -408,6 +503,7 @@ }; 331C80DD294CF71000263BE5 /* Profile */ = { isa = XCBuildConfiguration; + baseConfigurationReference = C4FDF5DC35CBE98629384323 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; diff --git a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a2ebdcc..f35dc28 100644 --- a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -59,6 +59,7 @@ ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" debugServiceExtension = "internal" + enableGPUValidationMode = "1" allowLocationSimulation = "YES"> diff --git a/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/example/macos/Runner.xcworkspace/contents.xcworkspacedata index 1d526a1..21a3cc1 100644 --- a/example/macos/Runner.xcworkspace/contents.xcworkspacedata +++ b/example/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/example/pubspec.lock b/example/pubspec.lock index d4fac28..f767bb1 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -1,54 +1,54 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: - args: + ansicolor: dependency: transitive description: - name: args - sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + name: ansicolor + sha256: "50e982d500bc863e1d703448afdbf9e5a72eb48840a4f766fa361ffd6877055f" url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.0.3" async: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.13.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" characters: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" clock: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" collection: dependency: transitive description: name: collection - sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.19.0" + version: "1.19.1" cupertino_icons: dependency: "direct main" description: @@ -57,22 +57,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" - easy_localization: - dependency: transitive - description: - name: easy_localization - sha256: fa59bcdbbb911a764aa6acf96bbb6fa7a5cf8234354fc45ec1a43a0349ef0201 - url: "https://pub.dev" - source: hosted - version: "3.0.7" - easy_logger: - dependency: transitive - description: - name: easy_logger - sha256: c764a6e024846f33405a2342caf91c62e357c24b02c04dbc712ef232bf30ffb7 - url: "https://pub.dev" - source: hosted - version: "0.0.2" equatable: dependency: transitive description: @@ -85,26 +69,26 @@ packages: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.3" ffi: dependency: transitive description: name: ffi - sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" + sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" file: dependency: transitive description: name: file - sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 url: "https://pub.dev" source: hosted - version: "6.1.4" + version: "7.0.1" flutter: dependency: "direct main" description: flutter @@ -122,10 +106,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" + sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "6.0.0" flutter_localizations: dependency: transitive description: flutter @@ -143,10 +127,10 @@ packages: dependency: transitive description: name: flutter_secure_storage_linux - sha256: bf7404619d7ab5c0a1151d7c4e802edad8f33535abfbeff2f9e1fe1274e2d705 + sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.3" flutter_secure_storage_macos: dependency: transitive description: @@ -201,10 +185,10 @@ packages: dependency: transitive description: name: intl - sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" url: "https://pub.dev" source: hosted - version: "0.19.0" + version: "0.20.2" js: dependency: transitive description: @@ -217,18 +201,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" url: "https://pub.dev" source: hosted - version: "10.0.7" + version: "10.0.9" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 url: "https://pub.dev" source: hosted - version: "3.0.8" + version: "3.0.9" leak_tracker_testing: dependency: transitive description: @@ -241,18 +225,18 @@ packages: dependency: transitive description: name: lints - sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" + sha256: a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0 url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "6.0.0" matcher: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.17" material_color_utilities: dependency: transitive description: @@ -265,89 +249,89 @@ packages: dependency: transitive description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.16.0" path: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" path_provider: dependency: transitive description: name: path_provider - sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2" + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" url: "https://pub.dev" source: hosted - version: "2.0.15" + version: "2.1.5" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86" + sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 url: "https://pub.dev" source: hosted - version: "2.0.27" + version: "2.2.17" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "916731ccbdce44d545414dd9961f26ba5fbaa74bcbb55237d8e65a623a8c7297" + sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" url: "https://pub.dev" source: hosted - version: "2.2.4" + version: "2.4.1" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57 + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 url: "https://pub.dev" source: hosted - version: "2.1.11" + version: "2.2.1" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec" + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" url: "https://pub.dev" source: hosted - version: "2.0.6" + version: "2.1.2" path_provider_windows: dependency: transitive description: name: path_provider_windows - sha256: "1cb68ba4cd3a795033de62ba1b7b4564dace301f952de6bfb3cd91b202b6ee96" + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 url: "https://pub.dev" source: hosted - version: "2.1.7" + version: "2.3.0" platform: dependency: transitive description: name: platform - sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.6" playx_core: dependency: transitive description: name: playx_core - sha256: "22366f8a8cf82230eb762858f009cf4c46616e6308c4a0ee0d6cb736264a0661" + sha256: "2ec52210e11918d2d4d86580ee9f859da434346ffb2fecbb3f903d931414f369" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.3" playx_localization: dependency: "direct main" description: path: ".." relative: true source: path - version: "0.2.2" + version: "0.3.0" plugin_platform_interface: dependency: transitive description: @@ -360,26 +344,26 @@ packages: dependency: transitive description: name: shared_preferences - sha256: a752ce92ea7540fc35a0d19722816e04d0e72828a4200e83a98cf1a1eb524c9a + sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" url: "https://pub.dev" source: hosted - version: "2.3.5" + version: "2.5.3" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "02a7d8a9ef346c9af715811b01fbd8e27845ad2c41148eefd31321471b41863d" + sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.4.10" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation - sha256: c4b35f6cb8f63c147312c054ce7c2254c8066745125264f0c88739c417fc9d9f + sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" url: "https://pub.dev" source: hosted - version: "2.5.2" + version: "2.5.4" shared_preferences_linux: dependency: transitive description: @@ -400,10 +384,10 @@ packages: dependency: transitive description: name: shared_preferences_web - sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.4.3" shared_preferences_windows: dependency: transitive description: @@ -421,10 +405,10 @@ packages: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.10.1" sprintf: dependency: transitive description: @@ -437,42 +421,50 @@ packages: dependency: transitive description: name: stack_trace - sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" string_scanner: dependency: transitive description: name: string_scanner - sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.1" + talker_logger: + dependency: transitive + description: + name: talker_logger + sha256: f1755d517e5ca8b119b65ad2fc1079746a8d03bd565e75d6b9d5aedf5c1d5b15 + url: "https://pub.dev" + source: hosted + version: "4.9.1" term_glyph: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" test_api: dependency: transitive description: name: test_api - sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.7.3" + version: "0.7.4" vector_math: dependency: transitive description: @@ -485,42 +477,42 @@ packages: dependency: transitive description: name: vm_service - sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 url: "https://pub.dev" source: hosted - version: "14.3.0" + version: "15.0.0" web: dependency: transitive description: name: web - sha256: d43c1d6b787bf0afad444700ae7f4db8827f701bc61c255ac8d328c6f4d52062 + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.1.1" win32: - dependency: "direct overridden" + dependency: transitive description: name: win32 - sha256: "68d1e89a91ed61ad9c370f9f8b6effed9ae5e0ede22a270bdfa6daf79fc2290a" + sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03" url: "https://pub.dev" source: hosted - version: "5.5.4" + version: "5.14.0" worker_manager: dependency: transitive description: name: worker_manager - sha256: "0c6c4e7d246bcbe7221273ef955732dafb097347d536ebe6acd6547d0398c49c" + sha256: af3db5e6c6c8a74ab8f72e25e9d305f8ff60984ca55551397e3c8828ebf30509 url: "https://pub.dev" source: hosted - version: "7.2.2" + version: "7.2.6" xdg_directories: dependency: transitive description: name: xdg_directories - sha256: e0b1147eec179d3911f1f19b59206448f78195ca1d20514134e10641b7d7fbff + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.1.0" sdks: - dart: ">=3.5.0 <4.0.0" - flutter: ">=3.24.0" + dart: ">=3.8.0 <4.0.0" + flutter: ">=3.27.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 96740ea..9e23c53 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -13,14 +13,12 @@ dependencies: playx_localization: path: ../ -dependency_overrides: - win32: ^5.5.4 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^4.0.0 + flutter_lints: ^6.0.0 flutter: diff --git a/lib/playx_localization.dart b/lib/playx_localization.dart index 24dbd4d..bfc8bba 100644 --- a/lib/playx_localization.dart +++ b/lib/playx_localization.dart @@ -1,16 +1,6 @@ library; -export 'package:easy_localization/easy_localization.dart' - hide - TextTranslateExtension, - StringTranslateExtension, - BuildContextEasyLocalizationExtension, - TextDirection, - MapExtension, - tr, - trExists, - plural; -export 'package:intl/date_symbol_data_local.dart'; +export 'package:intl/intl.dart'; export 'package:playx_core/playx_core.dart'; export 'package:playx_localization/src/config/playx_locale_config.dart'; export 'package:playx_localization/src/easy_localization/public.dart'; @@ -21,3 +11,6 @@ export 'package:playx_localization/src/extensions/playx_localization_extensions. export 'package:playx_localization/src/model/x_locale.dart'; export 'package:playx_localization/src/playx_localization.dart'; export 'package:playx_localization/src/widgets/playx_localization_builder.dart'; +export 'package:playx_localization/src/widgets/playx_inherited_localization.dart'; +export 'package:playx_localization/src/extensions/locale_extensions.dart'; +export 'package:playx_localization/src/easy_localization/asset_loader.dart'; diff --git a/lib/src/config/playx_locale_config.dart b/lib/src/config/playx_locale_config.dart index 99389c7..bd17e4a 100644 --- a/lib/src/config/playx_locale_config.dart +++ b/lib/src/config/playx_locale_config.dart @@ -1,3 +1,6 @@ +import 'package:flutter/cupertino.dart'; +import 'package:playx_localization/src/delegate/playx_localization_delegate.dart'; + import '../../playx_localization.dart'; /// Locale config : @@ -49,12 +52,23 @@ class PlayxLocaleConfig { /// @Default value true final bool saveLocale; - /// Log missing keys in the console. + /// Log missing keys in the console when a key is not found in the current locale file in debug mode. final bool logMissingKeys; + /// Log locale changes in the console when the locale is changed. + final bool logLocaleChanges; + /// Migrate preferences to async storage. final bool migratePrefsToAsync; + /// Additional custom delegates, e.g., from third-party packages. + final List? extraDelegates; + + /// Custom localization delegate builder. + /// This allows you to create a custom list of delegates based on the provided [PlayxLocalizationDelegate]. + final List Function( + PlayxLocalizationDelegate delegate)? customLocalizationDelegateBuilder; + PlayxLocaleConfig({ required this.supportedLocales, this.startLocale, @@ -65,7 +79,10 @@ class PlayxLocaleConfig { this.assetLoader = const RootBundleAssetLoader(), this.saveLocale = true, this.logMissingKeys = false, + this.logLocaleChanges = true, this.migratePrefsToAsync = false, + this.extraDelegates, + this.customLocalizationDelegateBuilder, }) : assert(path.isNotEmpty, 'path can not be empty'), assert( supportedLocales.isNotEmpty, 'supportedLocales can not be empty'); diff --git a/lib/src/controller/controller.dart b/lib/src/controller/controller.dart index 0c0456c..e39036c 100644 --- a/lib/src/controller/controller.dart +++ b/lib/src/controller/controller.dart @@ -46,6 +46,9 @@ class PlayxLocaleController extends ValueNotifier { // Returns the device locale. Locale? deviceLocale; + static PlayxBaseLogger? get logger => + PlayxLogger.getLogger('Playx Localization'); + /// current locale index int get currentIndex { if (value == null) { @@ -57,32 +60,27 @@ class PlayxLocaleController extends ValueNotifier { /// set up the base controller to load locales. Future boot() async { - EasyLocalization.logger('Booting Localization'); - + final logger = PlayxLogger.initLogger( + name: 'Playx Localization', + setAsDefault: false, + useColoredFormatter: true); _localizationControllerInstance = this; final lastKnownIndex = await getLastSavedIndexFromPrefs( migratePrefsToAsync: config.migratePrefsToAsync); final foundPlatformLocale = await findSystemLocale(); deviceLocale = foundPlatformLocale.toLocale(); - EasyLocalization.logger( - 'Device Locale ${deviceLocale?.toStringWithSeparator()}'); + logger.i('Device Locale ${deviceLocale?.toStringWithSeparator()}'); XLocale? lastSavedLocale = config.supportedLocales.atOrNull( lastKnownIndex ?? -1, ); - EasyLocalization.logger( - 'Last Saved Locale ${lastSavedLocale?.locale.toStringWithSeparator()}'); - final locale = _getStartLocale(savedLocale: lastSavedLocale); - EasyLocalization.logger( - 'Start Locale ${locale.locale.toStringWithSeparator()}'); - //Load translations from assets await loadTranslations(locale); - EasyLocalization.logger('Loaded Translation from assets'); + logger.i('Loaded Translation from assets'); delegate = PlayxLocalizationDelegate( localizationController: this, @@ -90,8 +88,8 @@ class PlayxLocaleController extends ValueNotifier { ); value = locale; - EasyLocalization.logger( - 'translation booted with ${locale.locale.toStringWithSeparator()}✔'); + logger.i( + 'Translation booted with locale ${locale.name} -> ${locale.toStringWithSeparator()} at index ${supportedXLocales.indexOf(locale)}'); } /// Retrieves the last saved theme index from preferences. @@ -109,7 +107,7 @@ class PlayxLocaleController extends ValueNotifier { final lastKnownIndexInPrefs = PlayxPrefs.maybeGetInt( _lastKnownIndexKey, ); - EasyLocalization.logger( + logger?.i( 'Migrating preferences to SharedPreferenceAsync found index $lastKnownIndexInPrefs'); if (lastKnownIndexInPrefs != null) { @@ -149,7 +147,7 @@ class PlayxLocaleController extends ValueNotifier { return getFallbackLocale(); } - ///Get fallback Locale + /// Get fallback Locale /// if fallbackLocale is not null then return it /// if fallbackLocale is null then return english locale if it's supported in the config supported locales. /// if english locale is not supported then return the first locale in the config supported locales. @@ -180,7 +178,12 @@ class PlayxLocaleController extends ValueNotifier { /// if [forceAppUpdate] is true it will force the app to update. Future updateTo(XLocale locale, {bool forceAppUpdate = false}) async { final index = supportedXLocales.indexOf(locale); - if (index < 0) return false; + if (index < 0) { + if (config.logLocaleChanges) { + logger?.error('Locale not found in supported Locales'); + } + return false; + } return _updateLocale( locale: locale, forceAppUpdate: forceAppUpdate, @@ -192,9 +195,10 @@ class PlayxLocaleController extends ValueNotifier { /// if [forceAppUpdate] is true it will force the app to update. Future nextLocale({bool forceAppUpdate = false}) async { final isLastLocale = currentIndex == config.supportedLocales.length - 1; + final index = isLastLocale ? 0 : currentIndex + 1; await updateByIndex( - isLastLocale ? 0 : currentIndex + 1, + index, forceAppUpdate: forceAppUpdate, ); } @@ -204,7 +208,13 @@ class PlayxLocaleController extends ValueNotifier { /// if [forceAppUpdate] is true it will force the app to update. Future updateByIndex(int index, {bool forceAppUpdate = false}) async { final locale = config.supportedLocales.atOrNull(index); - if (locale == null) return false; + if (locale == null) { + if (config.logLocaleChanges) { + logger + ?.error('Locale with index $index not found in supported Locales'); + } + return false; + } return _updateLocale(locale: locale, forceAppUpdate: forceAppUpdate); } @@ -213,7 +223,12 @@ class PlayxLocaleController extends ValueNotifier { Future updateById(String id, {bool forceAppUpdate = false}) async { final locale = config.supportedLocales.firstWhereOrNull((element) => element.id == id); - if (locale == null) return false; + if (locale == null) { + if (config.logLocaleChanges) { + logger?.error('Locale with id $id not found in supported Locales'); + } + return false; + } return _updateLocale(locale: locale, forceAppUpdate: forceAppUpdate); } @@ -273,7 +288,9 @@ class PlayxLocaleController extends ValueNotifier { try { final index = supportedXLocales.indexOf(locale); if (index < 0) { - EasyLocalization.logger.error('Locale not found in supported Locales'); + if (config.logLocaleChanges) { + logger?.error('Locale not found in supported Locales'); + } return false; } @@ -283,18 +300,22 @@ class PlayxLocaleController extends ValueNotifier { if (config.saveLocale) { await PlayxAsyncPrefs.setInt(_lastKnownIndexKey, index); } - + final oldLocale = value; value = locale; if (forceAppUpdate) { await _forceAppUpdate(); } - EasyLocalization.logger( - 'Updated locale to ${locale.name} with code ${locale.locale.toStringWithSeparator()}'); + if (config.logLocaleChanges) { + logger?.i( + 'Updated locale to ${locale.name} with code ${locale.locale.toStringWithSeparator()} at index $index from ${oldLocale?.locale.toStringWithSeparator()}'); + } return true; } catch (e) { - EasyLocalization.logger.error(e); + if (config.logLocaleChanges) { + logger?.error(e); + } return false; } } @@ -355,8 +376,11 @@ class PlayxLocaleController extends ValueNotifier { } //delegates to be used in material app. - List get delegates => [ + List get delegates => + config.customLocalizationDelegateBuilder?.call(delegate) ?? + [ delegate, + ...?config.extraDelegates, GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, diff --git a/lib/src/controller/translation_manager.dart b/lib/src/controller/translation_manager.dart index 2a446d7..f6ab38f 100644 --- a/lib/src/controller/translation_manager.dart +++ b/lib/src/controller/translation_manager.dart @@ -2,6 +2,7 @@ import 'dart:ui'; import 'package:flutter/foundation.dart'; import 'package:playx_localization/playx_localization.dart'; +import 'package:playx_localization/src/controller/controller.dart'; import 'package:playx_localization/src/easy_localization/translations.dart'; class TranslationManager { @@ -44,12 +45,14 @@ class TranslationManager { ); } return (translations: translations, fallbackTranslations: null); - } on FlutterError catch (e) { + } on FlutterError catch (e, s) { // onLoadError(e); - EasyLocalization.logger.error(e.message); + PlayxLocaleController.logger + ?.error('Error loading translations: ', error: e, stackTrace: s); return (translations: null, fallbackTranslations: null); - } catch (e) { - EasyLocalization.logger.error(e); + } catch (e, s) { + PlayxLocaleController.logger + ?.error('Error loading translations: ', error: e, stackTrace: s); // onLoadError(FlutterError(e.toString())); return ( translations: null, @@ -62,9 +65,10 @@ class TranslationManager { {required XLocale locale, required PlayxLocaleConfig config}) async { try { return await loadTranslationData(locale: locale, config: config); - } on FlutterError catch (e) { + } on FlutterError catch (e, s) { // Disregard asset not found FlutterError when attempting to load base language fallback - EasyLocalization.logger.error(e.message); + PlayxLocaleController.logger + ?.error('Error loading translations: ', error: e, stackTrace: s); } return null; } diff --git a/lib/src/delegate/playx_localization_delegate.dart b/lib/src/delegate/playx_localization_delegate.dart index a89feda..8f96597 100644 --- a/lib/src/delegate/playx_localization_delegate.dart +++ b/lib/src/delegate/playx_localization_delegate.dart @@ -15,9 +15,7 @@ class PlayxLocalizationDelegate extends LocalizationsDelegate { // final bool useOnlyLangCode; PlayxLocalizationDelegate( - {this.localizationController, this.supportedLocales}) { - EasyLocalization.logger.debug('Init Localization Delegate'); - } + {this.localizationController, this.supportedLocales}); @override bool isSupported(Locale locale) => @@ -25,7 +23,6 @@ class PlayxLocalizationDelegate extends LocalizationsDelegate { @override Future load(Locale locale) async { - EasyLocalization.logger.debug('Load Localization Delegate'); if (localizationController!.translations == null) { final xLocale = localizationController!.searchLocaleByLanguageCode( languageCode: locale.languageCode, countryCode: locale.countryCode); diff --git a/lib/src/easy_localization/asset_loader.dart b/lib/src/easy_localization/asset_loader.dart new file mode 100644 index 0000000..7858df3 --- /dev/null +++ b/lib/src/easy_localization/asset_loader.dart @@ -0,0 +1,38 @@ +import 'dart:convert'; +import 'dart:ui'; + +import 'package:flutter/services.dart'; +import 'package:playx_localization/src/extensions/locale_extensions.dart'; + +/// abstract class used to building your Custom AssetLoader +/// Example: +/// ``` +///class FileAssetLoader extends AssetLoader { +/// @override +/// Future> load(String path, Locale locale) async { +/// final file = File(path); +/// return json.decode(await file.readAsString()); +/// } +///} +/// ``` +abstract class AssetLoader { + const AssetLoader(); + Future?> load(String path, Locale locale); +} + +/// +/// default used is RootBundleAssetLoader which uses flutter's assetloader +/// +class RootBundleAssetLoader extends AssetLoader { + const RootBundleAssetLoader(); + + String getLocalePath(String basePath, Locale locale) { + return '$basePath/${locale.toStringWithSeparator(separator: "-")}.json'; + } + + @override + Future?> load(String path, Locale locale) async { + var localePath = getLocalePath(path, locale); + return json.decode(await rootBundle.loadString(localePath)); + } +} diff --git a/lib/src/easy_localization/localization.dart b/lib/src/easy_localization/localization.dart index 1a78b16..68af8c5 100644 --- a/lib/src/easy_localization/localization.dart +++ b/lib/src/easy_localization/localization.dart @@ -1,5 +1,5 @@ -import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/widgets.dart'; +import 'package:intl/intl.dart'; import '../controller/controller.dart'; import 'plural_rules.dart'; @@ -97,7 +97,7 @@ class Localization { translated = _modifiers[formatterName]!(translated); } else { if (logging) { - EasyLocalization.logger.warning( + PlayxLocaleController.logger?.warning( 'Undefined modifier $formatterName, available modifiers: ${_modifiers.keys.toString()}'); } } @@ -212,7 +212,8 @@ class Localization { if (resource == null || (_useFallbackTranslationsForEmptyResources && resource.isEmpty)) { if (logging) { - EasyLocalization.logger.warning('Localization key [$key] not found'); + PlayxLocaleController.logger + ?.warning('Localization key [$key] not found'); } if (_fallbackTranslations == null || !fallback) { return key; @@ -221,8 +222,8 @@ class Localization { if (resource == null || (_useFallbackTranslationsForEmptyResources && resource.isEmpty)) { if (logging) { - EasyLocalization.logger - .warning('Fallback localization key [$key] not found'); + PlayxLocaleController.logger + ?.warning('Fallback localization key [$key] not found'); } return key; } diff --git a/lib/src/extensions/locale_extensions.dart b/lib/src/extensions/locale_extensions.dart new file mode 100644 index 0000000..8fd7682 --- /dev/null +++ b/lib/src/extensions/locale_extensions.dart @@ -0,0 +1,46 @@ +import 'dart:ui'; + +import '../../playx_localization.dart'; + +extension LocaleExtension on Locale { + bool supports(Locale locale) { + if (this == locale) { + return true; + } + if (languageCode != locale.languageCode) { + return false; + } + if (countryCode != null && + countryCode!.isNotEmpty && + countryCode != locale.countryCode) { + return false; + } + if (scriptCode != null && scriptCode != locale.scriptCode) { + return false; + } + + return true; + } + + String toStringWithSeparator({String separator = '-'}) { + final parts = []; + if (languageCode.isNotEmpty) { + parts.add(languageCode); + } + if (countryCode != null && countryCode!.isNotEmpty) { + parts.add(countryCode!); + } + if (scriptCode != null && scriptCode!.isNotEmpty) { + parts.add(scriptCode!); + } + return parts.join(separator); + } +} + +extension XLocaleExtension on XLocale { + Locale get locale => Locale(languageCode, countryCode); + + String toStringWithSeparator({String separator = '-'}) { + return locale.toStringWithSeparator(separator: separator); + } +} diff --git a/lib/src/extensions/localization_string_extensions.dart b/lib/src/extensions/localization_string_extensions.dart index dce70d5..0c6ac8a 100644 --- a/lib/src/extensions/localization_string_extensions.dart +++ b/lib/src/extensions/localization_string_extensions.dart @@ -1,21 +1,132 @@ -import 'package:easy_localization/easy_localization.dart'; +import 'dart:ui'; +import '../../playx_localization.dart'; + +/// Extension on [String] to help with localization and language-specific analysis. extension LocalizationStringExtensions on String { - /// Check if the string is RTL + /// Returns `true` if the string contains any **right-to-left** characters (e.g. Arabic, Hebrew). bool get isRtl => Bidi.hasAnyRtl(this); - /// Check if the string is LTR + /// Returns `true` if the string does **not** contain RTL characters. bool get isLtr => !isRtl; - /// Regex for english characters - RegExp get englishRegExp => RegExp(r'[a-zA-Z]'); + /// Regular expression for matching **any English letters** (a–z or A–Z). + static final RegExp _englishRegExp = RegExp(r'[a-zA-Z]'); + + /// Regular expression for matching **any Arabic letters** (includes Arabic and Arabic Supplement Unicode blocks). + static final RegExp _arabicRegExp = RegExp(r'[\u0600-\u06FF\u0750-\u077F]'); + + /// Regular expression for matching **only Arabic letters** (ignores digits or punctuation). + static final RegExp _onlyArabicRegExp = RegExp(r'^[\u0600-\u06FF\s]+$'); + + /// Regular expression for matching **only English letters**. + static final RegExp _onlyEnglishRegExp = RegExp(r'^[a-zA-Z\s]+$'); + + /// Regular expression to check for any digits. + static final RegExp _numberRegExp = RegExp(r'[0-9]'); + + /// Returns `true` if the string contains **any English** letters. + bool get isEnglish => _englishRegExp.hasMatch(this); + + /// Returns `true` if the string contains **any Arabic** letters. + bool get isArabic => _arabicRegExp.hasMatch(this); + + /// Returns `true` if the string contains **only Arabic** letters (and spaces). + bool get containsOnlyArabic => _onlyArabicRegExp.hasMatch(trim()); + + /// Returns `true` if the string contains **only English** letters (and spaces). + bool get containsOnlyEnglish => _onlyEnglishRegExp.hasMatch(trim()); + + /// Returns `true` if the string contains **any numeric** digits. + bool get hasNumbers => _numberRegExp.hasMatch(this); + + /// A list of Arabic diacritics (tashkeel) used in the Arabic language. + static const List _diacritics = [ + '\u064B', // fathatan + '\u064C', // dammatan + '\u064D', // kasratan + '\u064E', // fatha + '\u064F', // damma + '\u0650', // kasra + '\u0651', // shadda + '\u0652', // sukun + '\u0653', // maddah above + '\u0654', // hamza above + '\u0655', // hamza below + ]; + + /// Removes **Arabic diacritics (tashkeel)** from the string. + /// + /// Useful for comparing Arabic words or simplifying them. + String get stripDiacritics { + return replaceAll(RegExp('[${_diacritics.join()}]'), ''); + } + + /// Converts European digits to Arabic-Indic digits. + String toArabicIndicDigits() { + const english = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; + const arabic = ['٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩']; + + var result = this; + for (int i = 0; i < english.length; i++) { + result = result.replaceAll(english[i], arabic[i]); + } + return result; + } + + /// Converts Arabic-Indic digits to English digits. + String toEnglishDigits() { + const arabic = ['٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩']; + const english = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; + + var result = this; + for (int i = 0; i < arabic.length; i++) { + result = result.replaceAll(arabic[i], english[i]); + } + return result; + } + + /// Converts the string to localized digits based on the current locale. + String toLocalizedDigits({String? locale, bool toArabic = true}) { + final formatLocale = + locale ?? PlayxLocalization.currentLocale.toLanguageTag(); + if (formatLocale.startsWith('ar') && toArabic) { + return toArabicIndicDigits(); + } + return toEnglishDigits(); + } - /// Regex for arabic characters - RegExp get arabicRegExp => RegExp(r'[\u0600-\u06FF]'); + /// Normalizes Arabic characters like Alef forms to a standard form. + String normalizeArabicLetters() { + return replaceAll(RegExp('[إأآٱ]'), 'ا') + .replaceAll('ى', 'ي') + .replaceAll('ة', 'ه'); + } - /// Check if the string is english - bool get isEnglish => englishRegExp.hasMatch(this); + /// Removes redundant spaces from the string. + String removeExtraSpaces() { + return trim().replaceAll(RegExp(r'\s+'), ' '); + } - /// Check if the string is arabic - bool get isArabic => arabicRegExp.hasMatch(this); + /// Convert string to [Locale] object + Locale toLocale({String separator = '_'}) { + final localeList = split(separator); + switch (localeList.length) { + case 2: + return localeList.last.length == 4 // scriptCode length is 4 + ? Locale.fromSubtags( + languageCode: localeList.first, + scriptCode: localeList.last, + ) + : Locale(localeList.first, localeList.last); + case 3: + return Locale.fromSubtags( + languageCode: localeList.first, + scriptCode: localeList[1], + countryCode: localeList.last, + ); + default: + return Locale(localeList.first); + } + } } diff --git a/lib/src/extensions/num_extensions.dart b/lib/src/extensions/num_extensions.dart index d920829..b406062 100644 --- a/lib/src/extensions/num_extensions.dart +++ b/lib/src/extensions/num_extensions.dart @@ -3,49 +3,114 @@ import 'dart:math'; import 'package:intl/intl.dart'; import 'package:playx_localization/src/playx_localization.dart'; -/// Extension functions to help operations on numbers. +/// Extension functions to help perform common operations and formatting on [num] values. extension NumExtensions on num { - ///Extension function to round number to certain number + /// Rounds the number to the given number of decimal places. + /// + /// Example: + /// ```dart + /// 3.14159.roundToPrecision(numbersToRoundTo: 2); // → 3.14 + /// ``` double roundToPrecision({int numbersToRoundTo = 2}) { - final fac = pow(10, numbersToRoundTo).toInt(); - return (this * fac).round() / fac; + final factor = pow(10, numbersToRoundTo).toInt(); + return (this * factor).round() / factor; } - /// Extension function to format number to currency number - /// ``` - /// 1000000 => will be converted to 1000,000.00 + /// Formats the number using a custom pattern and locale, optionally prefixing with a currency symbol. + /// + /// Example: + /// ```dart + /// 1000000.toFormattedCurrencyNumber(currencySymbol: '\$', locale: 'en'); + /// // → $1,000,000.00 /// ``` - String toFormattedCurrencyNumber( - {String format = "#,##0.00", String locale = 'en'}) { + String toFormattedCurrencyNumber({ + String format = "#,##0.00##", + String locale = 'en', + String? currencySymbol, + }) { final numberFormat = NumberFormat(format, locale); - return numberFormat.format(this); + final formatted = numberFormat.format(this); + return currencySymbol != null ? '$currencySymbol$formatted' : formatted; + } + + /// Formats the number as a currency string using the current locale from [PlayxLocalization]. + /// + /// Defaults to Arabic (`ar_EG`) if current locale is Arabic. + String toLocalizedCurrencyNumber({ + String format = "#,##0.00##", + String? locale, + String? currencySymbol, + }) { + final formatLocale = locale ?? + (PlayxLocalization.isCurrentLocaleArabic() + ? 'ar_EG' + : PlayxLocalization.currentLocale.toLanguageTag()); + + final numberFormat = NumberFormat(format, formatLocale); + final formatted = numberFormat.format(this); + return currencySymbol != null ? '$currencySymbol$formatted' : formatted; } - /// Extension function to format number to String - String toFormattedNumber({required String format, String locale = 'en'}) { + /// Formats the number using the given pattern and locale. + /// + /// Example: + /// ```dart + /// 1234.56.toFormattedNumber(format: "#,##0.0", locale: "en_US"); // → 1,234.6 + /// ``` + String toFormattedNumber({ + required String format, + String locale = 'en', + }) { final numberFormat = NumberFormat(format, locale); return numberFormat.format(this); } - /// Extension function to format number to arabic numbers String - String toLocalizedArabicNumber() { - return NumberFormat('#.##', 'ar_EG').format(this); + /// Formats the number using Arabic numerals with optional format. + /// + /// Example: + /// ```dart + /// 123.toLocalizedArabicNumber(); // → ١٢٣ + /// ``` + String toLocalizedArabicNumber({String format = '#.##'}) { + return NumberFormat(format, 'ar_EG').format(this); } - /// Extension function to format number to english numbers String - String toLocalizedEnglishNumber() { - return NumberFormat('#.##', 'en_US').format(this); + /// Formats the number using English numerals with optional format. + /// + /// Example: + /// ```dart + /// 123.toLocalizedEnglishNumber(); // → 123 + /// ``` + String toLocalizedEnglishNumber({String format = '#.##'}) { + return NumberFormat(format, 'en_US').format(this); } - /// Extension function to format number to current arabic or english numbers String - String toLocalizedArabicOrEnglishNumber() { + /// Formats the number using Arabic or English numerals depending on the current locale. + /// + /// Useful for localizing number format without manually checking the locale. + String toLocalizedArabicOrEnglishNumber({String format = '#.##'}) { return PlayxLocalization.isCurrentLocaleArabic() - ? toLocalizedArabicNumber() - : toLocalizedEnglishNumber(); + ? toLocalizedArabicNumber(format: format) + : toLocalizedEnglishNumber(format: format); } - /// Extension function to format number to localized numbers String - String toLocalizedNumber({String locale = 'en'}) { - return NumberFormat('#.##', locale).format(this); + /// Formats the number according to the given or current locale and format. + /// + /// If [locale] is not provided, it uses the current locale from [PlayxLocalization]. + String toLocalizedNumber({ + String? locale, + String format = '#.##', + }) { + final formatLocale = + locale ?? PlayxLocalization.currentLocale.toLanguageTag(); + return NumberFormat(format, formatLocale).format(this); } + + /// Returns `true` if the number is equal to 0 within a small tolerance (useful for floating-point precision). + /// + /// Example: + /// ```dart + /// (0.00000001).isZero(); // → true + /// ``` + bool isZero({double precision = 1e-8}) => abs() < precision; } diff --git a/lib/src/extensions/playx_localization_extensions.dart b/lib/src/extensions/playx_localization_extensions.dart index 62c3e15..fb8deb1 100644 --- a/lib/src/extensions/playx_localization_extensions.dart +++ b/lib/src/extensions/playx_localization_extensions.dart @@ -204,4 +204,34 @@ extension BuildContextLocalizationExtension on BuildContext { format: format, ); } + + /// Returns the current `XLocale` object with locale and font info. + XLocale get currentXLocale { + final locale = PlayxInheritedLocalization.of(this); + return locale.locale; + } + + /// Returns the current [Locale] used in the app. + Locale get currentLocale => currentXLocale.locale; + + /// Returns the locale as a BCP-47 language tag (e.g., `en-US`, `ar-EG`). + String get localeTag => currentLocale.toLanguageTag(); + + /// Returns the current locale's language code (e.g., 'en', 'ar'). + String get currentLanguageCode => currentLocale.languageCode; + + /// Returns the font family for the current locale, if provided. + String? get fontFamily => currentXLocale.fontFamily; + + /// Returns true if the current locale is Arabic. + bool get isCurrentLocaleArabic => currentLanguageCode == 'ar'; + + /// Returns true if the current locale is English. + bool get isCurrentLocaleEnglish => currentLanguageCode == 'en'; + + /// Returns true if the current locale is RTL (e.g., Arabic, Hebrew). + bool get isRtl => Bidi.hasAnyRtl(currentLanguageCode); + + /// Returns true if the current locale is LTR. + bool get isLtr => !isRtl; } diff --git a/lib/src/model/x_locale.dart b/lib/src/model/x_locale.dart index a68dd0d..eb36ec2 100644 --- a/lib/src/model/x_locale.dart +++ b/lib/src/model/x_locale.dart @@ -1,4 +1,3 @@ -import 'package:flutter/material.dart'; import 'package:playx_core/playx_core.dart'; ///Defines locales with more information like id and name. @@ -19,8 +18,6 @@ class XLocale extends Equatable { this.fontFamily, }); - Locale get locale => Locale(languageCode, countryCode); - @override List get props => [id, name, languageCode, countryCode, fontFamily]; } diff --git a/lib/src/playx_localization.dart b/lib/src/playx_localization.dart index d012c8e..403a329 100644 --- a/lib/src/playx_localization.dart +++ b/lib/src/playx_localization.dart @@ -1,6 +1,6 @@ -import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:playx_localization/src/controller/controller.dart'; +import 'package:playx_localization/src/extensions/locale_extensions.dart'; import 'package:playx_localization/src/model/x_locale.dart'; import 'config/playx_locale_config.dart'; @@ -23,8 +23,6 @@ abstract class PlayxLocalization { required PlayxLocaleConfig config, }) async { WidgetsFlutterBinding.ensureInitialized(); - EasyLocalization.logger.name = 'Playx_localization'; - EasyLocalization.logger('boot Localization'); final controller = PlayxLocaleController(config: config); return controller.boot(); } diff --git a/lib/src/widgets/playx_localization_builder.dart b/lib/src/widgets/playx_localization_builder.dart index 33c4d73..3034a31 100644 --- a/lib/src/widgets/playx_localization_builder.dart +++ b/lib/src/widgets/playx_localization_builder.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:playx_localization/playx_localization.dart'; import 'package:playx_localization/src/controller/controller.dart'; -import 'package:playx_localization/src/widgets/playx_inherited_localization.dart'; /// PlayxLocalizationBuilder: /// It allows us to create a widget with current locale. @@ -28,7 +27,10 @@ class PlayxLocalizationBuilder extends StatelessWidget { } return PlayxInheritedLocalization( locale: xLocale, - child: builder(context, xLocale), + child: Localizations( + locale: xLocale.locale, + delegates: controller.delegates, + child: builder(context, xLocale)), ); }, ); diff --git a/pubspec.yaml b/pubspec.yaml index 66bf51f..2811c28 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: playx_localization description: Easily manage and update app localization with a simple implementation and a lot of utilities. -version: 0.2.2 +version: 0.3.0 homepage: https://sourcya.io repository: https://github.com/playx-flutter/playx_localization issue_tracker: https://github.com/playx-flutter/playx_localization/issues @@ -11,25 +11,23 @@ topics: - internationalization environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.5.0 <4.0.0" + flutter: ">=3.24.0 <4.0.0" dependencies: flutter: sdk: flutter flutter_localizations: sdk: flutter - playx_core: ^0.6.1 - intl: ^0.19.0 - easy_localization: ^3.0.7 + playx_core: ^0.7.3 + intl: any shared_preferences_platform_interface: ^2.4.1 -dependency_overrides: - win32: ^5.10.0 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^5.0.0 - lints: ^5.1.1 + flutter_lints: ^6.0.0 + lints: ^6.0.0 flutter: