├── .github ├── FUNDING.yml └── workflows │ ├── pr_check.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── .gitpod.Dockerfile ├── .gitpod.yml ├── .metadata ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── bin └── generate.dart ├── easy_localization.iml ├── example ├── .gitignore ├── .metadata ├── README.md ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── io │ │ │ │ │ └── aissat │ │ │ │ │ └── example │ │ │ │ │ └── example │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable-v21 │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── values-night │ │ │ │ └── styles.xml │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── .last_build_id │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Podfile │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ ├── Runner-Bridging-Header.h │ │ ├── ar-DZ.lproj │ │ ├── LaunchScreen.strings │ │ └── Main.strings │ │ └── ru.lproj │ │ ├── LaunchScreen.strings │ │ └── Main.strings ├── lib │ ├── generated │ │ ├── codegen_loader.g.dart │ │ └── locale_keys.g.dart │ ├── lang_view.dart │ └── main.dart ├── linux │ ├── .gitignore │ ├── CMakeLists.txt │ ├── flutter │ │ ├── CMakeLists.txt │ │ ├── generated_plugin_registrant.cc │ │ ├── generated_plugin_registrant.h │ │ └── generated_plugins.cmake │ ├── main.cc │ ├── my_application.cc │ └── my_application.h ├── macos │ ├── .gitignore │ ├── Flutter │ │ ├── Flutter-Debug.xcconfig │ │ ├── Flutter-Release.xcconfig │ │ └── GeneratedPluginRegistrant.swift │ ├── Podfile │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ └── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── app_icon_1024.png │ │ │ ├── app_icon_128.png │ │ │ ├── app_icon_16.png │ │ │ ├── app_icon_256.png │ │ │ ├── app_icon_32.png │ │ │ ├── app_icon_512.png │ │ │ └── app_icon_64.png │ │ ├── Base.lproj │ │ └── MainMenu.xib │ │ ├── Configs │ │ ├── AppInfo.xcconfig │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── Warnings.xcconfig │ │ ├── DebugProfile.entitlements │ │ ├── Info.plist │ │ ├── MainFlutterWindow.swift │ │ └── Release.entitlements ├── pubspec.yaml ├── resources │ ├── fonts │ │ └── MyFlutterApp.ttf │ └── langs │ │ ├── ar-DZ.json │ │ ├── ar-DZ.xml │ │ ├── ar-DZ.yaml │ │ ├── ar.json │ │ ├── de-DE.json │ │ ├── de-DE.xml │ │ ├── de-DE.yaml │ │ ├── de.json │ │ ├── en-US.json │ │ ├── en-US.xml │ │ ├── en-US.yaml │ │ ├── en.json │ │ ├── langs.csv │ │ ├── langs.xml │ │ ├── langs.yaml │ │ ├── ru-RU.json │ │ ├── ru-RU.xml │ │ ├── ru-RU.yaml │ │ └── ru.json ├── test │ └── widget_test.dart └── web │ ├── favicon.png │ ├── icons │ ├── Icon-192.png │ └── Icon-512.png │ ├── index.html │ └── manifest.json ├── i18n ├── ar-DZ.json ├── ar.json ├── en-US.json └── en.json ├── lib ├── easy_localization.dart └── src │ ├── asset_loader.dart │ ├── easy_localization_app.dart │ ├── easy_localization_controller.dart │ ├── exceptions.dart │ ├── localization.dart │ ├── plural_rules.dart │ ├── public.dart │ ├── public_ext.dart │ ├── translations.dart │ ├── utils.dart │ └── widgets.dart ├── logo └── logo.svg ├── packages └── easy_logger │ ├── .idea │ ├── libraries │ │ └── Dart_SDK.xml │ ├── modules.xml │ └── workspace.xml │ ├── .metadata │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── analysis_options.yaml │ ├── lib │ ├── easy_logger.dart │ └── src │ │ ├── enums.dart │ │ ├── logger.dart │ │ └── logger_printer.dart │ ├── pubspec.yaml │ └── test │ └── easy_logger_test.dart ├── pubspec.yaml ├── screenshots ├── Screenshot_ar.png ├── Screenshot_en.png └── Screenshot_err.png └── test ├── easy_localization_context_test.dart ├── easy_localization_extra_asset_loaders_test.dart ├── easy_localization_init_test.dart ├── easy_localization_language_specific_test.dart ├── easy_localization_logger_test.dart ├── easy_localization_plural_rules_unit_test.dart ├── easy_localization_test.dart ├── easy_localization_utils_test.dart ├── easy_localization_widget_test.dart └── utils └── test_asset_loaders.dart /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: ['https://www.buymeacoffee.com/aissat'] 2 | ko_fi: aissat 3 | open_collective: flutter_easy_localization 4 | -------------------------------------------------------------------------------- /.github/workflows/pr_check.yml: -------------------------------------------------------------------------------- 1 | name: PR Check 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | 7 | jobs: 8 | test: 9 | name: Test 10 | uses: ./.github/workflows/test.yml 11 | secrets: inherit 12 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release to pub.dev 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | tags: 7 | - 'v[0-9]+.[0-9]+.[0-9]+*' 8 | 9 | jobs: 10 | test: 11 | name: Test 12 | uses: ./.github/workflows/test.yml 13 | secrets: inherit 14 | 15 | publish: 16 | needs: [test] 17 | name: Publish 18 | permissions: 19 | id-token: write # This is required for authentication using OIDC 20 | runs-on: ubuntu-latest 21 | timeout-minutes: 5 22 | 23 | steps: 24 | - uses: actions/checkout@v3 25 | 26 | - uses: dart-lang/setup-dart@v1 27 | 28 | - uses: subosito/flutter-action@v2 29 | with: 30 | flutter-version: "3.7.12" 31 | 32 | - name: Install dependencies 33 | run: dart pub get 34 | 35 | - name: code format 36 | run: dart format lib/*/*.dart lib/*.dart 37 | 38 | - name: Publish 39 | run: dart pub publish --force 40 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | workflow_call: 5 | 6 | jobs: 7 | test: 8 | name: Test 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: actions/setup-java@v1 13 | with: 14 | java-version: '12.x' 15 | 16 | - uses: subosito/flutter-action@v2 17 | with: 18 | flutter-version: "3.7.12" 19 | 20 | - name: Install packages dependencies 21 | run: flutter pub get 22 | 23 | - name: Analyze the project's Dart code 24 | run: flutter analyze 25 | 26 | - name: Run tests 27 | run: flutter test 28 | 29 | - name: Run tests coverage 30 | run: flutter test --coverage -------------------------------------------------------------------------------- /.gitpod.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gitpod/workspace-full 2 | 3 | USER gitpod 4 | 5 | WORKDIR /home/gitpod 6 | 7 | RUN git clone https://github.com/flutter/flutter && \ 8 | /home/gitpod/flutter/bin/flutter channel stable && \ 9 | /home/gitpod/flutter/bin/flutter upgrade && \ 10 | /home/gitpod/flutter/bin/flutter config --enable-web && \ 11 | /home/gitpod/flutter/bin/flutter --version 12 | 13 | ENV PUB_CACHE=/workspace/.pub_cache 14 | ENV PATH="/home/gitpod/flutter/bin:$PATH" 15 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | image: 2 | file: .gitpod.Dockerfile 3 | 4 | # List the start up tasks. You can start them in parallel in multiple terminals. See https://www.gitpod.io/docs/config-start-tasks/ 5 | tasks: 6 | - init: | 7 | flutter upgrade 8 | flutter update-packages --force-upgrade 9 | flutter run -d web-server --no-resident 10 | ports: 11 | - port: 8080 12 | onOpen: notify 13 | vscode: 14 | extensions: 15 | - Dart-Code.dart-code@3.10.0-beta.2:tUOuxmX97tnVRlRhi8Z1og== 16 | - Dart-Code.flutter@3.9.1:Ef3b5HLzz0C/TIa0n9xSyA== 17 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: d675bbbd3a0f1e0992961e52161227c6ee37d0e8 8 | channel: dev 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Release process 4 | 5 | 1. Make sure that the changelog is updated 6 | 7 | 2. Make sure that the version in pubspec.yaml is correct 8 | 9 | 3. Create a release in the github UI. Name the release like the version, but with a v (3.7.5 -> v3.7.5). Name the tag like the release 10 | 11 | 4. A pipeline will run and deploy the new version to pub.dev 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | #https://dart.dev/guides/language/analysis-options 2 | include: package:flutter_lints/flutter.yaml -------------------------------------------------------------------------------- /easy_localization.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | *.lock 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | /build/ 32 | 33 | # Web related 34 | lib/generated_plugin_registrant.dart 35 | 36 | # Exceptions to above rules. 37 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 38 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 37f9c541165c3516f727cd36bd502d411fdad3b8 8 | channel: dev 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 28 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | lintOptions { 36 | disable 'InvalidPackage' 37 | } 38 | 39 | defaultConfig { 40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 41 | applicationId "io.aissat.example.example" 42 | minSdkVersion 16 43 | targetSdkVersion 28 44 | versionCode flutterVersionCode.toInteger() 45 | versionName flutterVersionName 46 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 47 | } 48 | 49 | buildTypes { 50 | release { 51 | // TODO: Add your own signing config for the release build. 52 | // Signing with the debug keys for now, so `flutter run --release` works. 53 | signingConfig signingConfigs.debug 54 | } 55 | } 56 | } 57 | 58 | flutter { 59 | source '../..' 60 | } 61 | 62 | dependencies { 63 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 64 | testImplementation 'junit:junit:4.12' 65 | androidTestImplementation 'androidx.test:runner:1.1.1' 66 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' 67 | } 68 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 13 | 17 | 21 | 26 | 30 | 31 | 32 | 33 | 34 | 35 | 37 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/io/aissat/example/example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package io.aissat.example.example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.5.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip 7 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /example/ios/Flutter/.last_build_id: -------------------------------------------------------------------------------- 1 | 95aad2d4f53601773338c4ce51791765 -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 11.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 3 | #include "Generated.xcconfig" 4 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 3 | #include "Generated.xcconfig" 4 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '11.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleAllowMixedLocalizations 6 | 7 | CFBundleDevelopmentRegion 8 | $(DEVELOPMENT_LANGUAGE) 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | example 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | CADisableMinimumFrameDurationOnPhone 47 | 48 | UIApplicationSupportsIndirectInputEvents 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" -------------------------------------------------------------------------------- /example/ios/Runner/ar-DZ.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /example/ios/Runner/ar-DZ.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /example/ios/Runner/ru.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /example/ios/Runner/ru.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /example/lib/generated/locale_keys.g.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:easy_localization/generate.dart 2 | 3 | abstract class LocaleKeys { 4 | static const title = 'title'; 5 | static const msg = 'msg'; 6 | static const msg_named = 'msg_named'; 7 | static const clickMe = 'clickMe'; 8 | static const reset_password_label = 'reset_password.label'; 9 | static const reset_password_username = 'reset_password.username'; 10 | static const reset_password_password = 'reset_password.password'; 11 | static const profile_reset_password = 'profile.reset_password'; 12 | static const profile = 'profile'; 13 | static const clicked = 'clicked'; 14 | static const amount = 'amount'; 15 | static const gender_with_arg = 'gender.with_arg'; 16 | static const gender = 'gender'; 17 | static const reset_locale = 'reset_locale'; 18 | } 19 | -------------------------------------------------------------------------------- /example/lib/lang_view.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:easy_localization/easy_localization.dart'; 5 | 6 | class LanguageView extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | return Scaffold( 10 | appBar: AppBar( 11 | title: Text( 12 | '', 13 | style: TextStyle(color: Colors.black), 14 | ), 15 | backgroundColor: Colors.white, 16 | iconTheme: IconThemeData(color: Colors.black), 17 | elevation: 0, 18 | ), 19 | body: Container( 20 | color: Colors.white, 21 | child: Column( 22 | crossAxisAlignment: CrossAxisAlignment.start, 23 | children: [ 24 | Container( 25 | padding: EdgeInsets.only(top: 26), 26 | margin: EdgeInsets.symmetric( 27 | horizontal: 24, 28 | ), 29 | child: Text( 30 | 'Choose language', 31 | style: TextStyle( 32 | color: Colors.blue, 33 | fontFamily: 'Montserrat', 34 | fontWeight: FontWeight.w700, 35 | fontSize: 18, 36 | ), 37 | ), 38 | ), 39 | _SwitchListTileMenuItem( 40 | title: 'عربي', 41 | subtitle: 'عربي', 42 | locale: 43 | context.supportedLocales[1] //BuildContext extension method 44 | ), 45 | _Divider(), 46 | _SwitchListTileMenuItem( 47 | title: 'English', 48 | subtitle: 'English', 49 | locale: context.supportedLocales[0]), 50 | _Divider(), 51 | _SwitchListTileMenuItem( 52 | title: 'German', 53 | subtitle: 'German', 54 | locale: context.supportedLocales[2]), 55 | _Divider(), 56 | _SwitchListTileMenuItem( 57 | title: 'Русский', 58 | subtitle: 'Русский', 59 | locale: context.supportedLocales[3]), 60 | _Divider(), 61 | ], 62 | ), 63 | ), 64 | ); 65 | } 66 | } 67 | 68 | class _Divider extends StatelessWidget { 69 | const _Divider({Key? key}) : super(key: key); 70 | 71 | @override 72 | Widget build(BuildContext context) { 73 | return Container( 74 | margin: EdgeInsets.symmetric( 75 | horizontal: 24, 76 | ), 77 | child: Divider( 78 | color: Colors.grey, 79 | ), 80 | ); 81 | } 82 | } 83 | 84 | class _SwitchListTileMenuItem extends StatelessWidget { 85 | const _SwitchListTileMenuItem({ 86 | Key? key, 87 | required this.title, 88 | required this.subtitle, 89 | required this.locale, 90 | }) : super(key: key); 91 | 92 | final String title; 93 | final String subtitle; 94 | final Locale locale; 95 | 96 | bool isSelected(BuildContext context) => locale == context.locale; 97 | 98 | @override 99 | Widget build(BuildContext context) { 100 | return Container( 101 | margin: EdgeInsets.only(left: 10, right: 10, top: 5), 102 | decoration: BoxDecoration( 103 | border: 104 | isSelected(context) ? Border.all(color: Colors.blueAccent) : null, 105 | ), 106 | child: ListTile( 107 | dense: true, 108 | // isThreeLine: true, 109 | title: Text( 110 | title, 111 | ), 112 | subtitle: Text( 113 | subtitle, 114 | ), 115 | onTap: () async { 116 | log(locale.toString(), name: toString()); 117 | await context.setLocale(locale); //BuildContext extension method 118 | Navigator.pop(context); 119 | }), 120 | ); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_localization/easy_localization.dart'; 2 | //import 'package:easy_localization_loader/easy_localization_loader.dart'; // import custom loaders 3 | import 'package:flutter/material.dart'; 4 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 5 | 6 | import 'generated/locale_keys.g.dart'; 7 | import 'lang_view.dart'; 8 | 9 | void main() async { 10 | WidgetsFlutterBinding.ensureInitialized(); 11 | await EasyLocalization.ensureInitialized(); 12 | 13 | runApp(EasyLocalization( 14 | supportedLocales: [ 15 | Locale('en', 'US'), 16 | Locale('ar', 'DZ'), 17 | Locale('de', 'DE'), 18 | Locale('ru', 'RU') 19 | ], 20 | path: 'resources/langs', 21 | child: MyApp(), 22 | // fallbackLocale: Locale('en', 'US'), 23 | // startLocale: Locale('de', 'DE'), 24 | // saveLocale: false, 25 | // useOnlyLangCode: true, 26 | // ignorePluralRules: false, 27 | 28 | // optional assetLoader default used is RootBundleAssetLoader which uses flutter's assetloader 29 | // install easy_localization_loader for enable custom loaders 30 | // assetLoader: RootBundleAssetLoader() 31 | // assetLoader: HttpAssetLoader() 32 | // assetLoader: FileAssetLoader() 33 | // assetLoader: CsvAssetLoader() 34 | // assetLoader: YamlAssetLoader() //multiple files 35 | // assetLoader: YamlSingleAssetLoader() //single file 36 | // assetLoader: XmlAssetLoader() //multiple files 37 | // assetLoader: XmlSingleAssetLoader() //single file 38 | // assetLoader: CodegenLoader() 39 | )); 40 | } 41 | 42 | class MyApp extends StatelessWidget { 43 | @override 44 | Widget build(BuildContext context) { 45 | return MaterialApp( 46 | localizationsDelegates: context.localizationDelegates, 47 | supportedLocales: context.supportedLocales, 48 | locale: context.locale, 49 | theme: ThemeData( 50 | primarySwatch: Colors.blue, 51 | ), 52 | home: MyHomePage(title: 'Easy localization'), 53 | ); 54 | } 55 | } 56 | 57 | class MyHomePage extends StatefulWidget { 58 | MyHomePage({Key? key, required this.title}) : super(key: key); 59 | 60 | final String title; 61 | 62 | @override 63 | _MyHomePageState createState() => _MyHomePageState(); 64 | } 65 | 66 | class _MyHomePageState extends State { 67 | int counter = 0; 68 | bool _gender = true; 69 | 70 | void incrementCounter() { 71 | setState(() { 72 | counter++; 73 | }); 74 | } 75 | 76 | void switchGender(bool val) { 77 | setState(() { 78 | _gender = val; 79 | }); 80 | } 81 | 82 | @override 83 | Widget build(BuildContext context) { 84 | return Scaffold( 85 | appBar: AppBar( 86 | title: Text(LocaleKeys.title).tr(), 87 | actions: [ 88 | TextButton( 89 | onPressed: () { 90 | Navigator.push( 91 | context, 92 | MaterialPageRoute( 93 | builder: (_) => LanguageView(), fullscreenDialog: true), 94 | ); 95 | }, 96 | child: Icon( 97 | Icons.language, 98 | color: Colors.white, 99 | ), 100 | ), 101 | ], 102 | ), 103 | body: Center( 104 | child: Column( 105 | mainAxisAlignment: MainAxisAlignment.center, 106 | children: [ 107 | Spacer( 108 | flex: 1, 109 | ), 110 | Text( 111 | LocaleKeys.gender_with_arg, 112 | style: TextStyle( 113 | color: Colors.grey.shade600, 114 | fontSize: 19, 115 | fontWeight: FontWeight.bold), 116 | ).tr(args: ['aissat'], gender: _gender ? 'female' : 'male'), 117 | Text( 118 | tr(LocaleKeys.gender, gender: _gender ? 'female' : 'male'), 119 | style: TextStyle( 120 | color: Colors.grey.shade600, 121 | fontSize: 15, 122 | fontWeight: FontWeight.bold), 123 | ), 124 | Row( 125 | mainAxisAlignment: MainAxisAlignment.center, 126 | children: [ 127 | FaIcon(FontAwesomeIcons.male), 128 | Switch(value: _gender, onChanged: switchGender), 129 | FaIcon(FontAwesomeIcons.female), 130 | ], 131 | ), 132 | Spacer( 133 | flex: 1, 134 | ), 135 | Text(LocaleKeys.msg).tr(args: ['aissat', 'Flutter']), 136 | Text(LocaleKeys.msg_named) 137 | .tr(namedArgs: {'lang': 'Dart'}, args: ['Easy localization']), 138 | Text(LocaleKeys.clicked).plural(counter), 139 | TextButton( 140 | onPressed: () { 141 | incrementCounter(); 142 | }, 143 | child: Text(LocaleKeys.clickMe).tr(), 144 | ), 145 | SizedBox( 146 | height: 15, 147 | ), 148 | Text( 149 | plural(LocaleKeys.amount, counter, 150 | format: NumberFormat.currency( 151 | locale: Intl.defaultLocale, symbol: '€')), 152 | style: TextStyle( 153 | color: Colors.grey.shade900, 154 | fontSize: 18, 155 | fontWeight: FontWeight.bold)), 156 | SizedBox( 157 | height: 20, 158 | ), 159 | ElevatedButton( 160 | onPressed: () { 161 | context.resetLocale(); 162 | }, 163 | child: Text(LocaleKeys.reset_locale).tr(), 164 | ), 165 | Spacer( 166 | flex: 1, 167 | ), 168 | ], 169 | ), 170 | ), 171 | floatingActionButton: FloatingActionButton( 172 | onPressed: incrementCounter, 173 | child: Text('+1'), 174 | ), 175 | ); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /example/linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /example/linux/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(runner LANGUAGES CXX) 3 | 4 | set(BINARY_NAME "example") 5 | set(APPLICATION_ID "io.aissat.example.example") 6 | 7 | cmake_policy(SET CMP0063 NEW) 8 | 9 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") 10 | 11 | # Configure build options. 12 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 13 | set(CMAKE_BUILD_TYPE "Debug" CACHE 14 | STRING "Flutter build mode" FORCE) 15 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 16 | "Debug" "Profile" "Release") 17 | endif() 18 | 19 | # Compilation settings that should be applied to most targets. 20 | function(APPLY_STANDARD_SETTINGS TARGET) 21 | target_compile_features(${TARGET} PUBLIC cxx_std_14) 22 | target_compile_options(${TARGET} PRIVATE -Wall -Werror) 23 | target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") 24 | target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") 25 | endfunction() 26 | 27 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 28 | 29 | # Flutter library and tool build rules. 30 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 31 | 32 | # System-level dependencies. 33 | find_package(PkgConfig REQUIRED) 34 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 35 | 36 | add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") 37 | 38 | # Application build 39 | add_executable(${BINARY_NAME} 40 | "main.cc" 41 | "my_application.cc" 42 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 43 | ) 44 | apply_standard_settings(${BINARY_NAME}) 45 | target_link_libraries(${BINARY_NAME} PRIVATE flutter) 46 | target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) 47 | add_dependencies(${BINARY_NAME} flutter_assemble) 48 | # Only the install-generated bundle's copy of the executable will launch 49 | # correctly, since the resources must in the right relative locations. To avoid 50 | # people trying to run the unbundled copy, put it in a subdirectory instead of 51 | # the default top-level location. 52 | set_target_properties(${BINARY_NAME} 53 | PROPERTIES 54 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" 55 | ) 56 | 57 | # Generated plugin build rules, which manage building the plugins and adding 58 | # them to the application. 59 | include(flutter/generated_plugins.cmake) 60 | 61 | 62 | # === Installation === 63 | # By default, "installing" just makes a relocatable bundle in the build 64 | # directory. 65 | set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") 66 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 67 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 68 | endif() 69 | 70 | # Start with a clean build bundle directory every time. 71 | install(CODE " 72 | file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") 73 | " COMPONENT Runtime) 74 | 75 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 76 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") 77 | 78 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 79 | COMPONENT Runtime) 80 | 81 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 82 | COMPONENT Runtime) 83 | 84 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 85 | COMPONENT Runtime) 86 | 87 | if(PLUGIN_BUNDLED_LIBRARIES) 88 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" 89 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 90 | COMPONENT Runtime) 91 | endif() 92 | 93 | # Fully re-copy the assets directory on each build to avoid having stale files 94 | # from a previous install. 95 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 96 | install(CODE " 97 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 98 | " COMPONENT Runtime) 99 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 100 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 101 | 102 | # Install the AOT library on non-Debug builds only. 103 | if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") 104 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 105 | COMPONENT Runtime) 106 | endif() 107 | -------------------------------------------------------------------------------- /example/linux/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 4 | 5 | # Configuration provided via flutter tool. 6 | include(${EPHEMERAL_DIR}/generated_config.cmake) 7 | 8 | # TODO: Move the rest of this into files in ephemeral. See 9 | # https://github.com/flutter/flutter/issues/57146. 10 | 11 | # Serves the same purpose as list(TRANSFORM ... PREPEND ...), 12 | # which isn't available in 3.10. 13 | function(list_prepend LIST_NAME PREFIX) 14 | set(NEW_LIST "") 15 | foreach(element ${${LIST_NAME}}) 16 | list(APPEND NEW_LIST "${PREFIX}${element}") 17 | endforeach(element) 18 | set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) 19 | endfunction() 20 | 21 | # === Flutter Library === 22 | # System-level dependencies. 23 | find_package(PkgConfig REQUIRED) 24 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 25 | pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) 26 | pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) 27 | pkg_check_modules(BLKID REQUIRED IMPORTED_TARGET blkid) 28 | pkg_check_modules(LZMA REQUIRED IMPORTED_TARGET liblzma) 29 | 30 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") 31 | 32 | # Published to parent scope for install step. 33 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 34 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 35 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 36 | set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) 37 | 38 | list(APPEND FLUTTER_LIBRARY_HEADERS 39 | "fl_basic_message_channel.h" 40 | "fl_binary_codec.h" 41 | "fl_binary_messenger.h" 42 | "fl_dart_project.h" 43 | "fl_engine.h" 44 | "fl_json_message_codec.h" 45 | "fl_json_method_codec.h" 46 | "fl_message_codec.h" 47 | "fl_method_call.h" 48 | "fl_method_channel.h" 49 | "fl_method_codec.h" 50 | "fl_method_response.h" 51 | "fl_plugin_registrar.h" 52 | "fl_plugin_registry.h" 53 | "fl_standard_message_codec.h" 54 | "fl_standard_method_codec.h" 55 | "fl_string_codec.h" 56 | "fl_value.h" 57 | "fl_view.h" 58 | "flutter_linux.h" 59 | ) 60 | list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") 61 | add_library(flutter INTERFACE) 62 | target_include_directories(flutter INTERFACE 63 | "${EPHEMERAL_DIR}" 64 | ) 65 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") 66 | target_link_libraries(flutter INTERFACE 67 | PkgConfig::GTK 68 | PkgConfig::GLIB 69 | PkgConfig::GIO 70 | PkgConfig::BLKID 71 | PkgConfig::LZMA 72 | ) 73 | add_dependencies(flutter flutter_assemble) 74 | 75 | # === Flutter tool backend === 76 | # _phony_ is a non-existent file to force this command to run every time, 77 | # since currently there's no way to get a full input/output list from the 78 | # flutter tool. 79 | add_custom_command( 80 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 81 | ${CMAKE_CURRENT_BINARY_DIR}/_phony_ 82 | COMMAND ${CMAKE_COMMAND} -E env 83 | ${FLUTTER_TOOL_ENVIRONMENT} 84 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" 85 | linux-x64 ${CMAKE_BUILD_TYPE} 86 | VERBATIM 87 | ) 88 | add_custom_target(flutter_assemble DEPENDS 89 | "${FLUTTER_LIBRARY}" 90 | ${FLUTTER_LIBRARY_HEADERS} 91 | ) 92 | -------------------------------------------------------------------------------- /example/linux/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | 10 | void fl_register_plugins(FlPluginRegistry* registry) { 11 | } 12 | -------------------------------------------------------------------------------- /example/linux/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void fl_register_plugins(FlPluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /example/linux/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | ) 7 | 8 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 9 | ) 10 | 11 | set(PLUGIN_BUNDLED_LIBRARIES) 12 | 13 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 14 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) 15 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 16 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 17 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 18 | endforeach(plugin) 19 | 20 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 21 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) 22 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 23 | endforeach(ffi_plugin) 24 | -------------------------------------------------------------------------------- /example/linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /example/linux/my_application.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | #include 4 | #ifdef GDK_WINDOWING_X11 5 | #include 6 | #endif 7 | 8 | #include "flutter/generated_plugin_registrant.h" 9 | 10 | struct _MyApplication { 11 | GtkApplication parent_instance; 12 | char** dart_entrypoint_arguments; 13 | }; 14 | 15 | G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) 16 | 17 | // Implements GApplication::activate. 18 | static void my_application_activate(GApplication* application) { 19 | MyApplication* self = MY_APPLICATION(application); 20 | GtkWindow* window = 21 | GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); 22 | 23 | // Use a header bar when running in GNOME as this is the common style used 24 | // by applications and is the setup most users will be using (e.g. Ubuntu 25 | // desktop). 26 | // If running on X and not using GNOME then just use a traditional title bar 27 | // in case the window manager does more exotic layout, e.g. tiling. 28 | // If running on Wayland assume the header bar will work (may need changing 29 | // if future cases occur). 30 | gboolean use_header_bar = TRUE; 31 | #ifdef GDK_WINDOWING_X11 32 | GdkScreen *screen = gtk_window_get_screen(window); 33 | if (GDK_IS_X11_SCREEN(screen)) { 34 | const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); 35 | if (g_strcmp0(wm_name, "GNOME Shell") != 0) { 36 | use_header_bar = FALSE; 37 | } 38 | } 39 | #endif 40 | if (use_header_bar) { 41 | GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); 42 | gtk_widget_show(GTK_WIDGET(header_bar)); 43 | gtk_header_bar_set_title(header_bar, "example"); 44 | gtk_header_bar_set_show_close_button(header_bar, TRUE); 45 | gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); 46 | } 47 | else { 48 | gtk_window_set_title(window, "example"); 49 | } 50 | 51 | gtk_window_set_default_size(window, 1280, 720); 52 | gtk_widget_show(GTK_WIDGET(window)); 53 | 54 | g_autoptr(FlDartProject) project = fl_dart_project_new(); 55 | fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); 56 | 57 | FlView* view = fl_view_new(project); 58 | gtk_widget_show(GTK_WIDGET(view)); 59 | gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); 60 | 61 | fl_register_plugins(FL_PLUGIN_REGISTRY(view)); 62 | 63 | gtk_widget_grab_focus(GTK_WIDGET(view)); 64 | } 65 | 66 | // Implements GApplication::local_command_line. 67 | static gboolean my_application_local_command_line(GApplication* application, gchar ***arguments, int *exit_status) { 68 | MyApplication* self = MY_APPLICATION(application); 69 | // Strip out the first argument as it is the binary name. 70 | self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); 71 | 72 | g_autoptr(GError) error = nullptr; 73 | if (!g_application_register(application, nullptr, &error)) { 74 | g_warning("Failed to register: %s", error->message); 75 | *exit_status = 1; 76 | return TRUE; 77 | } 78 | 79 | g_application_activate(application); 80 | *exit_status = 0; 81 | 82 | return TRUE; 83 | } 84 | 85 | // Implements GObject::dispose. 86 | static void my_application_dispose(GObject *object) { 87 | MyApplication* self = MY_APPLICATION(object); 88 | g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); 89 | G_OBJECT_CLASS(my_application_parent_class)->dispose(object); 90 | } 91 | 92 | static void my_application_class_init(MyApplicationClass* klass) { 93 | G_APPLICATION_CLASS(klass)->activate = my_application_activate; 94 | G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; 95 | G_OBJECT_CLASS(klass)->dispose = my_application_dispose; 96 | } 97 | 98 | static void my_application_init(MyApplication* self) {} 99 | 100 | MyApplication* my_application_new() { 101 | return MY_APPLICATION(g_object_new(my_application_get_type(), 102 | "application-id", APPLICATION_ID, 103 | "flags", G_APPLICATION_NON_UNIQUE, 104 | nullptr)); 105 | } 106 | -------------------------------------------------------------------------------- /example/linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /example/macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/xcuserdata/ 7 | -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | import shared_preferences_foundation 9 | 10 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 11 | SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) 12 | } 13 | -------------------------------------------------------------------------------- /example/macos/Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '10.11' 2 | 3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 5 | 6 | project 'Runner', { 7 | 'Debug' => :debug, 8 | 'Profile' => :release, 9 | 'Release' => :release, 10 | } 11 | 12 | def parse_KV_file(file, separator='=') 13 | file_abs_path = File.expand_path(file) 14 | if !File.exists? file_abs_path 15 | return []; 16 | end 17 | pods_ary = [] 18 | skip_line_start_symbols = ["#", "/"] 19 | File.foreach(file_abs_path) { |line| 20 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 21 | plugin = line.split(pattern=separator) 22 | if plugin.length == 2 23 | podname = plugin[0].strip() 24 | path = plugin[1].strip() 25 | podpath = File.expand_path("#{path}", file_abs_path) 26 | pods_ary.push({:name => podname, :path => podpath}); 27 | else 28 | puts "Invalid plugin specification: #{line}" 29 | end 30 | } 31 | return pods_ary 32 | end 33 | 34 | def pubspec_supports_macos(file) 35 | file_abs_path = File.expand_path(file) 36 | if !File.exists? file_abs_path 37 | return false; 38 | end 39 | File.foreach(file_abs_path) { |line| 40 | return true if line =~ /^\s*macos:/ 41 | } 42 | return false 43 | end 44 | 45 | target 'Runner' do 46 | use_frameworks! 47 | use_modular_headers! 48 | 49 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 50 | # referring to absolute paths on developers' machines. 51 | ephemeral_dir = File.join('Flutter', 'ephemeral') 52 | symlink_dir = File.join(ephemeral_dir, '.symlinks') 53 | symlink_plugins_dir = File.join(symlink_dir, 'plugins') 54 | system("rm -rf #{symlink_dir}") 55 | system("mkdir -p #{symlink_plugins_dir}") 56 | 57 | # Flutter Pods 58 | generated_xcconfig = parse_KV_file(File.join(ephemeral_dir, 'Flutter-Generated.xcconfig')) 59 | if generated_xcconfig.empty? 60 | puts "Flutter-Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first." 61 | end 62 | generated_xcconfig.map { |p| 63 | if p[:name] == 'FLUTTER_FRAMEWORK_DIR' 64 | symlink = File.join(symlink_dir, 'flutter') 65 | File.symlink(File.dirname(p[:path]), symlink) 66 | pod 'FlutterMacOS', :path => File.join(symlink, File.basename(p[:path])) 67 | end 68 | } 69 | 70 | # Plugin Pods 71 | plugin_pods = parse_KV_file('../.flutter-plugins') 72 | plugin_pods.map { |p| 73 | symlink = File.join(symlink_plugins_dir, p[:name]) 74 | File.symlink(p[:path], symlink) 75 | if pubspec_supports_macos(File.join(symlink, 'pubspec.yaml')) 76 | pod p[:name], :path => File.join(symlink, 'macos') 77 | end 78 | } 79 | end 80 | 81 | # Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. 82 | install! 'cocoapods', :disable_input_output_paths => true 83 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /example/macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @NSApplicationMain 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /example/macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = example 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = io.aissat.example.example 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2020 io.aissat.example. All rights reserved. 15 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /example/macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /example/macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /example/macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController.init() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: example 2 | description: A new Flutter project. 3 | publish_to: none 4 | # The following defines the version and build number for your application. 5 | # A version number is three numbers separated by dots, like 1.2.43 6 | # followed by an optional build number separated by a +. 7 | # Both the version and the builder number may be overridden in flutter 8 | # build by specifying --build-name and --build-number, respectively. 9 | # In Android, build-name is used as versionName while build-number used as versionCode. 10 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 11 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 12 | # Read more about iOS versioning at 13 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 14 | version: 1.0.0+1 15 | 16 | environment: 17 | sdk: ">=2.12.0-0 <4.0.0" 18 | 19 | dependencies: 20 | flutter: 21 | sdk: flutter 22 | 23 | # The following adds the Cupertino Icons font to your application. 24 | # Use with the CupertinoIcons class for iOS style icons. 25 | cupertino_icons: ^1.0.2 26 | easy_localization: 27 | path: ../ 28 | font_awesome_flutter: 9.0.0-nullsafety 29 | 30 | #custom loaders 31 | #fixme(DartAndrik): Commented due to [easy_localization_loader] package dependencies issue, uncomment after resolving. 32 | # easy_localization_loader: 33 | # git: https://github.com/aissat/easy_localization_loader.git 34 | 35 | dev_dependencies: 36 | flutter_test: 37 | sdk: flutter 38 | 39 | # For information on the generic Dart part of this file, see the 40 | # following page: https://dart.dev/tools/pub/pubspec 41 | 42 | # The following section is specific to Flutter. 43 | flutter: 44 | # The following line ensures that the Material Icons font is 45 | # included with your application, so that you can use the icons in 46 | # the material Icons class. 47 | uses-material-design: true 48 | 49 | # To add assets to your application, add an assets section, like this: 50 | assets: 51 | - resources/langs/ 52 | # - images/a_dot_burr.jpeg 53 | # - images/a_dot_ham.jpeg 54 | # An image asset can refer to one or more resolution-specific "variants", see 55 | # https://flutter.dev/assets-and-images/#resolution-aware. 56 | 57 | # For details regarding adding assets from package dependencies, see 58 | # https://flutter.dev/assets-and-images/#from-packages 59 | 60 | # To add custom fonts to your application, add a fonts section here, 61 | # in this "flutter" section. Each entry in this list should have a 62 | # "family" key with the font family name, and a "fonts" key with a 63 | # list giving the asset and other descriptors for the font. For 64 | # example: 65 | fonts: 66 | - family: MyFlutterApp 67 | fonts: 68 | - asset: resources/fonts/MyFlutterApp.ttf 69 | # fonts: 70 | # - family: Schyler 71 | # fonts: 72 | # - asset: fonts/Schyler-Regular.ttf 73 | # - asset: fonts/Schyler-Italic.ttf 74 | # style: italic 75 | # - family: Trajan Pro 76 | # fonts: 77 | # - asset: fonts/TrajanPro.ttf 78 | # - asset: fonts/TrajanPro_Bold.ttf 79 | # weight: 700 80 | # 81 | # For details regarding fonts from package dependencies, 82 | # see https://flutter.dev/custom-fonts/#from-packages 83 | -------------------------------------------------------------------------------- /example/resources/fonts/MyFlutterApp.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/resources/fonts/MyFlutterApp.ttf -------------------------------------------------------------------------------- /example/resources/langs/ar-DZ.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "السلام", 3 | "msg": "السلام عليكم يا {} في عالم {}", 4 | "msg_named": "{} مكتوبة باللغة {lang}", 5 | "clickMe": "إضغط هنا", 6 | "profile": { 7 | "reset_password": { 8 | "label": "اعادة تعين كلمة السر", 9 | "username": "المستخدم", 10 | "password": "كلمة السر" 11 | } 12 | }, 13 | "clicked": { 14 | "zero": "لم تنقر بعد!", 15 | "one": "لقد نقرت مرة واحدة!", 16 | "two": "لقد قمت بالنقر مرتين!", 17 | "few": " لقد قمت بالنقر {} مرات!", 18 | "many": "لقد قمت بالنقر {} مرة!", 19 | "other": "{} نقرة!" 20 | }, 21 | "amount": { 22 | "zero": "المبلغ : {}", 23 | "one": " المبلغ : {}", 24 | "two": " المبلغ : {}", 25 | "few": " المبلغ : {}", 26 | "many": " المبلغ : {}", 27 | "other": " المبلغ : {}" 28 | }, 29 | "gender": { 30 | "male": " مرحبا يا رجل", 31 | "female": " مرحبا بك يا فتاة", 32 | "with_arg": { 33 | "male": "{} مرحبا يا رجل", 34 | "female": "{} مرحبا بك يا فتاة" 35 | } 36 | }, 37 | "reset_locale": "إعادة ضبط اللغة" 38 | } -------------------------------------------------------------------------------- /example/resources/langs/ar-DZ.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | المبلغ : {} 5 | المبلغ : {} 6 | المبلغ : {} 7 | المبلغ : {} 8 | المبلغ : {} 9 | المبلغ : {} 10 | 11 | إضغط هنا 12 | 13 | لقد قمت بالنقر {} مرات! 14 | لقد قمت بالنقر {} مرة! 15 | لقد نقرت مرة واحدة! 16 | {} نقرة! 17 | لقد قمت بالنقر مرتين! 18 | لم تنقر بعد! 19 | 20 | 21 | مرحبا بك يا فتاة 22 | مرحبا يا رجل 23 | 24 | {} مرحبا بك يا فتاة 25 | {} مرحبا يا رجل 26 | 27 | 28 | السلام عليكم يا {} في عالم {} 29 | {} مكتوبة باللغة {lang} 30 | 31 | 32 | 33 | كلمة السر 34 | المستخدم 35 | 36 | 37 | إعادة ضبط اللغة 38 | السلام 39 | -------------------------------------------------------------------------------- /example/resources/langs/ar-DZ.yaml: -------------------------------------------------------------------------------- 1 | title: السلام 2 | msg: السلام عليكم يا {} في عالم {} 3 | msg_named: "{} مكتوبة باللغة {lang}" 4 | clickMe: إضغط هنا 5 | profile: 6 | reset_password: 7 | label: اعادة تعين كلمة السر 8 | username: المستخدم 9 | password: كلمة السر 10 | clicked: 11 | zero: لم تنقر بعد! 12 | one: لقد نقرت مرة واحدة! 13 | two: لقد قمت بالنقر مرتين! 14 | few: " لقد قمت بالنقر {} مرات!" 15 | many: لقد قمت بالنقر {} مرة! 16 | other: "{} نقرة!" 17 | amount: 18 | zero: 'المبلغ : {}' 19 | one: " المبلغ : {}" 20 | two: " المبلغ : {}" 21 | few: " المبلغ : {}" 22 | many: " المبلغ : {}" 23 | other: " المبلغ : {}" 24 | gender: 25 | male: " مرحبا يا رجل" 26 | female: " مرحبا بك يا فتاة" 27 | with_arg: 28 | male: "{} مرحبا يا رجل" 29 | female: "{} مرحبا بك يا فتاة" 30 | reset_locale: إعادة ضبط اللغة 31 | -------------------------------------------------------------------------------- /example/resources/langs/ar.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "السلام", 3 | "msg": "السلام عليكم يا {} في عالم {}", 4 | "msg_named": "{} مكتوبة باللغة {lang}", 5 | "clickMe": "إضغط هنا", 6 | "profile": { 7 | "reset_password": { 8 | "label": "اعادة تعين كلمة السر", 9 | "username": "المستخدم", 10 | "password": "كلمة السر" 11 | } 12 | }, 13 | "clicked": { 14 | "zero": "لم تنقر بعد!", 15 | "one": "لقد نقرت مرة واحدة!", 16 | "two": "لقد قمت بالنقر مرتين!", 17 | "few": " لقد قمت بالنقر {} مرات!", 18 | "many": "لقد قمت بالنقر {} مرة!", 19 | "other": "{} نقرة!" 20 | }, 21 | "amount": { 22 | "zero": "المبلغ : {}", 23 | "one": " المبلغ : {}", 24 | "two": " المبلغ : {}", 25 | "few": " المبلغ : {}", 26 | "many": " المبلغ : {}", 27 | "other": " المبلغ : {}" 28 | }, 29 | "gender": { 30 | "male": " مرحبا يا رجل", 31 | "female": " مرحبا بك يا فتاة", 32 | "with_arg": { 33 | "male": "{} مرحبا يا رجل", 34 | "female": "{} مرحبا بك يا فتاة" 35 | } 36 | }, 37 | "reset_locale": "إعادة ضبط اللغة" 38 | } -------------------------------------------------------------------------------- /example/resources/langs/de-DE.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Hallo", 3 | "msg": "Hallo {} in der {} welt ", 4 | "msg_named": "{} ist in {lang} geschrieben", 5 | "clickMe": "Click mich", 6 | "profile": { 7 | "reset_password": { 8 | "label": "Password zurücksetzten", 9 | "username": "Name", 10 | "password": "Password" 11 | } 12 | }, 13 | "clicked": { 14 | "zero": "Du hast {} mal geklickt", 15 | "one": "Du hast {} mal geklickt", 16 | "two": "Du hast {} mal geklickt", 17 | "few": "Du hast {} mal geklickt", 18 | "many": "Du hast {} mal geklickt", 19 | "other": "Du hast {} mal geklickt" 20 | }, 21 | "amount": { 22 | "zero": "Deine Klicks: {}", 23 | "one": "Deine Klicks: {}", 24 | "two": "Deine Klicks: {}", 25 | "few": "Deine Klicks: {}", 26 | "many": "Deine Klicks: {}", 27 | "other": "Deine Klicks: {}" 28 | }, 29 | "gender": { 30 | "male": "Hi Mann ;) ", 31 | "female": "Hallo Frau :)", 32 | "with_arg": { 33 | "male": "Hi Mann ;) {}", 34 | "female": "Hallo Frau :) {}" 35 | } 36 | }, 37 | "reset_locale": "Sprache zurücksetzen" 38 | } -------------------------------------------------------------------------------- /example/resources/langs/de-DE.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Deine Klicks: {} 5 | Deine Klicks: {} 6 | Deine Klicks: {} 7 | Deine Klicks: {} 8 | Deine Klicks: {} 9 | Deine Klicks: {} 10 | 11 | Click mich 12 | 13 | Du hast {} mal geklickt 14 | Du hast {} mal geklickt 15 | Du hast {} mal geklickt 16 | Du hast {} mal geklickt 17 | Du hast {} mal geklickt 18 | Du hast {} mal geklickt 19 | 20 | 21 | Hallo Frau :) 22 | Hi Mann ;) 23 | 24 | Hallo Frau :) {} 25 | Hi Mann ;) {} 26 | 27 | 28 | Hallo {} in der {} welt 29 | {} ist in {lang} geschrieben 30 | 31 | 32 | 33 | Password 34 | Name 35 | 36 | 37 | Sprache zurücksetzen 38 | Hallo 39 | -------------------------------------------------------------------------------- /example/resources/langs/de-DE.yaml: -------------------------------------------------------------------------------- 1 | title: Hallo 2 | msg: 'Hallo {} in der {} welt ' 3 | msg_named: "{} ist in {lang} geschrieben" 4 | clickMe: Click mich 5 | profile: 6 | reset_password: 7 | label: Password zurücksetzten 8 | username: Name 9 | password: Password 10 | clicked: 11 | zero: Du hast {} mal geklickt 12 | one: Du hast {} mal geklickt 13 | two: Du hast {} mal geklickt 14 | few: Du hast {} mal geklickt 15 | many: Du hast {} mal geklickt 16 | other: Du hast {} mal geklickt 17 | amount: 18 | zero: 'Deine Klicks: {}' 19 | one: 'Deine Klicks: {}' 20 | two: 'Deine Klicks: {}' 21 | few: 'Deine Klicks: {}' 22 | many: 'Deine Klicks: {}' 23 | other: 'Deine Klicks: {}' 24 | gender: 25 | male: 'Hi Mann ;) ' 26 | female: Hallo Frau :) 27 | with_arg: 28 | male: Hi Mann ;) {} 29 | female: Hallo Frau :) {} 30 | reset_locale: Sprache zurücksetzen 31 | -------------------------------------------------------------------------------- /example/resources/langs/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Hallo", 3 | "msg": "Hallo {} in der {} welt ", 4 | "msg_named": "{} ist in {lang} geschrieben", 5 | "clickMe": "Click mich", 6 | "profile": { 7 | "reset_password": { 8 | "label": "Password zurücksetzten", 9 | "username": "Name", 10 | "password": "Password" 11 | } 12 | }, 13 | "clicked": { 14 | "zero": "Du hast {} mal geklickt", 15 | "one": "Du hast {} mal geklickt", 16 | "two": "Du hast {} mal geklickt", 17 | "few": "Du hast {} mal geklickt", 18 | "many": "Du hast {} mal geklickt", 19 | "other": "Du hast {} mal geklickt" 20 | }, 21 | "amount": { 22 | "zero": "Deine Klicks: {}", 23 | "one": "Deine Klicks: {}", 24 | "two": "Deine Klicks: {}", 25 | "few": "Deine Klicks: {}", 26 | "many": "Deine Klicks: {}", 27 | "other": "Deine Klicks: {}" 28 | }, 29 | "gender": { 30 | "male": "Hi Mann ;) ", 31 | "female": "Hallo Frau :)", 32 | "with_arg": { 33 | "male": "Hi Mann ;) {}", 34 | "female": "Hallo Frau :) {}" 35 | } 36 | }, 37 | "reset_locale": "Sprache zurücksetzen" 38 | } -------------------------------------------------------------------------------- /example/resources/langs/en-US.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Hello", 3 | "msg": "Hello {} in the {} world ", 4 | "msg_named": "{} are written in the {lang} language", 5 | "clickMe": "Click me", 6 | "profile": { 7 | "reset_password": { 8 | "label": "Reset Password", 9 | "username": "Username", 10 | "password": "password" 11 | } 12 | }, 13 | "clicked": { 14 | "zero": "You didn't click yet!", 15 | "few": "You clicked a few times ({})!", 16 | "many": "You clicked many times ({})!", 17 | "other": "You clicked {} time(s)!" 18 | }, 19 | "amount": { 20 | "zero": "Your amount : {} ", 21 | "one": "Your amount : {} ", 22 | "two": "Your amount : {} ", 23 | "few": "Your amount : {} ", 24 | "many": "Your amount : {} ", 25 | "other": "Your amount : {} " 26 | }, 27 | "gender": { 28 | "male": "Hi man ;) ", 29 | "female": "Hello girl :)", 30 | "with_arg": { 31 | "male": "Hi man ;) {}", 32 | "female": "Hello girl :) {}" 33 | } 34 | }, 35 | "reset_locale": "Reset Language" 36 | } -------------------------------------------------------------------------------- /example/resources/langs/en-US.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Your amount : {} 5 | Your amount : {} 6 | Your amount : {} 7 | Your amount : {} 8 | Your amount : {} 9 | Your amount : {} 10 | 11 | Click me 12 | 13 | You clicked {} times! 14 | You clicked {} times! 15 | You clicked {} time! 16 | You clicked {} times! 17 | You clicked {} times! 18 | You clicked {} times! 19 | 20 | 21 | Hello girl :) 22 | Hi man ;) 23 | 24 | Hello girl :) {} 25 | Hi man ;) {} 26 | 27 | 28 | Hello {} in the {} world 29 | {} are written in the {lang} language 30 | 31 | 32 | 33 | password 34 | Username 35 | 36 | 37 | Reset Language 38 | Hello 39 | -------------------------------------------------------------------------------- /example/resources/langs/en-US.yaml: -------------------------------------------------------------------------------- 1 | title: Hello 2 | msg: 'Hello {} in the {} world ' 3 | msg_named: "{} are written in the {lang} language" 4 | clickMe: Click me 5 | profile: 6 | reset_password: 7 | label: Reset Password 8 | username: Username 9 | password: password 10 | clicked: 11 | zero: You clicked {} times! 12 | one: You clicked {} time! 13 | two: You clicked {} times! 14 | few: You clicked {} times! 15 | many: You clicked {} times! 16 | other: You clicked {} times! 17 | amount: 18 | zero: 'Your amount : {} ' 19 | one: 'Your amount : {} ' 20 | two: 'Your amount : {} ' 21 | few: 'Your amount : {} ' 22 | many: 'Your amount : {} ' 23 | other: 'Your amount : {} ' 24 | gender: 25 | male: 'Hi man ;) ' 26 | female: Hello girl :) 27 | with_arg: 28 | male: Hi man ;) {} 29 | female: Hello girl :) {} 30 | reset_locale: Reset Language 31 | -------------------------------------------------------------------------------- /example/resources/langs/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Hello", 3 | "msg": "Hello {} in the {} world ", 4 | "msg_named": "{} are written in the {lang} language", 5 | "clickMe": "Click me", 6 | "profile": { 7 | "reset_password": { 8 | "label": "Reset Password", 9 | "username": "Username", 10 | "password": "password" 11 | } 12 | }, 13 | "clicked": { 14 | "zero": "You clicked {} times!", 15 | "one": "You clicked {} time!", 16 | "two": "You clicked {} times!", 17 | "few": "You clicked {} times!", 18 | "many": "You clicked {} times!", 19 | "other": "You clicked {} times!" 20 | }, 21 | "amount": { 22 | "zero": "Your amount : {} ", 23 | "one": "Your amount : {} ", 24 | "two": "Your amount : {} ", 25 | "few": "Your amount : {} ", 26 | "many": "Your amount : {} ", 27 | "other": "Your amount : {} " 28 | }, 29 | "gender": { 30 | "male": "Hi man ;) ", 31 | "female": "Hello girl :)", 32 | "with_arg": { 33 | "male": "Hi man ;) {}", 34 | "female": "Hello girl :) {}" 35 | } 36 | }, 37 | "reset_locale": "Reset Language" 38 | } -------------------------------------------------------------------------------- /example/resources/langs/langs.csv: -------------------------------------------------------------------------------- 1 | str,en_US,ar_DZ,de_DE,ru_RU 2 | title,Hello,السلام,Hallo,Привет! 3 | msg,Hello {} in the {} world ,السلام عليكم يا {} في عالم {},Hallo {} in der {} welt ,Привет! {} добро пожаловать {} мир! 4 | msg_named,{} are written in the {lang} language,{} مكتوبة باللغة {lang},{} ist in {lang} geschrieben,{} написан на языке {lang} 5 | clickMe,Click me,إضغط هنا,Click mich,Нажми на меня 6 | profile.reset_password.label,Reset Password,اعادة تعين كلمة السر,Password zurГјcksetzten,Сбросить пароль 7 | profile.reset_password.username,Username,المستخدم,Name,Логин 8 | profile.reset_password.password,password,كلمة السر,Password,Пароль 9 | clicked.zero,You clicked {} times!,{} نقرة!,Du hast {} mal geklickt,Ты кликнул {} раз! 10 | clicked.one,You clicked {} time!,{} نقرة!,Du hast {} mal geklickt,Ты кликнул {} раз! 11 | clicked.two,You clicked {} times!,{} نقرات!,Du hast {} mal geklickt,Ты кликнул {} раза! 12 | clicked.few,You clicked {} times!,{} نقرات!,Du hast {} mal geklickt,Ты кликнул {} раз! 13 | clicked.many,You clicked {} times!,{} نقرة!,Du hast {} mal geklickt,Ты кликнул {} раз! 14 | clicked.other,You clicked {} times!,{} نقرة!,Du hast {} mal geklickt,Ты кликнул {} раз! 15 | amount.zero,Your amount : {} ,المبلغ : {},Deine Klicks: {},Твой счет : {} 16 | amount.one,Your amount : {} , المبلغ : {},Deine Klicks: {},Твой счет : {} 17 | amount.two,Your amount : {} , المبلغ : {},Deine Klicks: {},Твой счет : {} 18 | amount.few,Your amount : {} , المبلغ : {},Deine Klicks: {},Твой счет : {} 19 | amount.many,Your amount : {} , المبلغ : {},Deine Klicks: {},Твой счет : {} 20 | amount.other,Your amount : {} , المبلغ : {},Deine Klicks: {},Твой счет : {} 21 | gender.male,Hi man ;) , مرحبا يا رجل,Hi Mann ;) ,Привет мужык ;) 22 | gender.female,Hello girl :), مرحبا بك يا فتاة,Hallo Frau :),Привет девчуля :) 23 | gender.with_arg.male,Hi man ;) {},{} مرحبا يا رجل,Hi Mann ;) {},Привет мужык ;) {} 24 | gender.with_arg.female,Hello girl :) {},{} مرحبا بك يا فتاة,Hallo Frau :) {},Привет девчуля :) {} 25 | reset_locale,Reset Language,إعادة ضبط اللغة,Gespeicherte Sprache zurГјcksettzen,Сбросить язык 26 | -------------------------------------------------------------------------------- /example/resources/langs/langs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | المبلغ : {} 6 | المبلغ : {} 7 | المبلغ : {} 8 | المبلغ : {} 9 | المبلغ : {} 10 | المبلغ : {} 11 | 12 | إضغط هنا 13 | 14 | لقد قمت بالنقر {} مرات! 15 | لقد قمت بالنقر {} مرة! 16 | لقد نقرت مرة واحدة! 17 | {} نقرة! 18 | لقد قمت بالنقر مرتين! 19 | لم تنقر بعد! 20 | 21 | 22 | مرحبا بك يا فتاة 23 | مرحبا يا رجل 24 | 25 | {} مرحبا بك يا فتاة 26 | {} مرحبا يا رجل 27 | 28 | 29 | السلام عليكم يا {} في عالم {} 30 | {} مكتوبة باللغة {lang} 31 | 32 | 33 | 34 | كلمة السر 35 | المستخدم 36 | 37 | 38 | إعادة ضبط اللغة 39 | السلام 40 | 41 | 42 | 43 | Deine Klicks: {} 44 | Deine Klicks: {} 45 | Deine Klicks: {} 46 | Deine Klicks: {} 47 | Deine Klicks: {} 48 | Deine Klicks: {} 49 | 50 | Click mich 51 | 52 | Du hast {} mal geklickt 53 | Du hast {} mal geklickt 54 | Du hast {} mal geklickt 55 | Du hast {} mal geklickt 56 | Du hast {} mal geklickt 57 | Du hast {} mal geklickt 58 | 59 | 60 | Hallo Frau :) 61 | Hi Mann ;) 62 | 63 | Hallo Frau :) {} 64 | Hi Mann ;) {} 65 | 66 | 67 | Hallo {} in der {} welt 68 | {} ist in {lang} geschrieben 69 | 70 | 71 | 72 | Password 73 | Name 74 | 75 | 76 | Sprache zurücksetzen 77 | Hallo 78 | 79 | 80 | 81 | Your amount : {} 82 | Your amount : {} 83 | Your amount : {} 84 | Your amount : {} 85 | Your amount : {} 86 | Your amount : {} 87 | 88 | Click me 89 | 90 | You clicked {} times! 91 | You clicked {} times! 92 | You clicked {} time! 93 | You clicked {} times! 94 | You clicked {} times! 95 | You clicked {} times! 96 | 97 | 98 | Hello girl :) 99 | Hi man ;) 100 | 101 | Hello girl :) {} 102 | Hi man ;) {} 103 | 104 | 105 | Hello {} in the {} world 106 | {} are written in the {lang} language 107 | 108 | 109 | 110 | password 111 | Username 112 | 113 | 114 | Reset Language 115 | Hello 116 | 117 | 118 | 119 | Твой счет : {} 120 | Твой счет : {} 121 | Твой счет : {} 122 | Твой счет : {} 123 | Твой счет : {} 124 | Твой счет : {} 125 | 126 | Нажми на меня 127 | 128 | Ты кликнул {} раз! 129 | Ты кликнул {} раз! 130 | Ты кликнул {} раз! 131 | Ты кликнул {} раз! 132 | Ты кликнул {} раза! 133 | Ты кликнул {} раз! 134 | 135 | 136 | Привет девчуля :) 137 | Привет мужык ;) 138 | 139 | Привет девчуля :) {} 140 | Привет мужык ;) {} 141 | 142 | 143 | Привет! {} добро пожаловать {} мир! 144 | {} написан на языке {lang} 145 | 146 | 147 | 148 | Пароль 149 | Логин 150 | 151 | 152 | Сбросить язык 153 | Привет! 154 | 155 | -------------------------------------------------------------------------------- /example/resources/langs/langs.yaml: -------------------------------------------------------------------------------- 1 | en_US: 2 | title: Hello 3 | msg: 'Hello {} in the {} world ' 4 | msg_named: "{} are written in the {lang} language" 5 | clickMe: Click me 6 | profile: 7 | reset_password: 8 | label: Reset Password 9 | username: Username 10 | password: password 11 | clicked: 12 | zero: You clicked {} times! 13 | one: You clicked {} time! 14 | two: You clicked {} times! 15 | few: You clicked {} times! 16 | many: You clicked {} times! 17 | other: You clicked {} times! 18 | amount: 19 | zero: 'Your amount : {} ' 20 | one: 'Your amount : {} ' 21 | two: 'Your amount : {} ' 22 | few: 'Your amount : {} ' 23 | many: 'Your amount : {} ' 24 | other: 'Your amount : {} ' 25 | gender: 26 | male: 'Hi man ;) ' 27 | female: Hello girl :) 28 | with_arg: 29 | male: Hi man ;) {} 30 | female: Hello girl :) {} 31 | reset_locale: Reset Language 32 | 33 | ar_DZ: 34 | title: السلام 35 | msg: السلام عليكم يا {} في عالم {} 36 | msg_named: "{} مكتوبة باللغة {lang}" 37 | clickMe: إضغط هنا 38 | profile: 39 | reset_password: 40 | label: اعادة تعين كلمة السر 41 | username: المستخدم 42 | password: كلمة السر 43 | clicked: 44 | zero: لم تنقر بعد! 45 | one: لقد نقرت مرة واحدة! 46 | two: لقد قمت بالنقر مرتين! 47 | few: " لقد قمت بالنقر {} مرات!" 48 | many: لقد قمت بالنقر {} مرة! 49 | other: "{} نقرة!" 50 | amount: 51 | zero: 'المبلغ : {}' 52 | one: " المبلغ : {}" 53 | two: " المبلغ : {}" 54 | few: " المبلغ : {}" 55 | many: " المبلغ : {}" 56 | other: " المبلغ : {}" 57 | gender: 58 | male: " مرحبا يا رجل" 59 | female: " مرحبا بك يا فتاة" 60 | with_arg: 61 | male: "{} مرحبا يا رجل" 62 | female: "{} مرحبا بك يا فتاة" 63 | reset_locale: إعادة ضبط اللغة 64 | 65 | de_DE: 66 | title: Hallo 67 | msg: 'Hallo {} in der {} welt ' 68 | msg_named: "{} ist in {lang} geschrieben" 69 | clickMe: Click mich 70 | profile: 71 | reset_password: 72 | label: Password zurücksetzten 73 | username: Name 74 | password: Password 75 | clicked: 76 | zero: Du hast {} mal geklickt 77 | one: Du hast {} mal geklickt 78 | two: Du hast {} mal geklickt 79 | few: Du hast {} mal geklickt 80 | many: Du hast {} mal geklickt 81 | other: Du hast {} mal geklickt 82 | amount: 83 | zero: 'Deine Klicks: {}' 84 | one: 'Deine Klicks: {}' 85 | two: 'Deine Klicks: {}' 86 | few: 'Deine Klicks: {}' 87 | many: 'Deine Klicks: {}' 88 | other: 'Deine Klicks: {}' 89 | gender: 90 | male: 'Hi Mann ;) ' 91 | female: Hallo Frau :) 92 | with_arg: 93 | male: Hi Mann ;) {} 94 | female: Hallo Frau :) {} 95 | reset_locale: Sprache zurücksetzen 96 | 97 | ru_RU: 98 | title: Привет! 99 | msg: 'Привет! {} добро пожаловать {} мир! ' 100 | msg_named: "{} написан на языке {lang}" 101 | clickMe: Нажми на меня 102 | profile: 103 | reset_password: 104 | label: Сбросить пароль 105 | username: Логин 106 | password: Пароль 107 | clicked: 108 | zero: Ты кликнул {} раз! 109 | one: Ты кликнул {} раз! 110 | two: Ты кликнул {} раза! 111 | few: Ты кликнул {} раз! 112 | many: Ты кликнул {} раз! 113 | other: Ты кликнул {} раз! 114 | amount: 115 | zero: 'Твой счет : {} ' 116 | one: 'Твой счет : {} ' 117 | two: 'Твой счет : {} ' 118 | few: 'Твой счет : {} ' 119 | many: 'Твой счет : {} ' 120 | other: 'Твой счет : {} ' 121 | gender: 122 | male: 'Привет мужык ;) ' 123 | female: Привет девчуля :) 124 | with_arg: 125 | male: Привет мужык ;) {} 126 | female: Привет девчуля :) {} 127 | reset_locale: Сбросить язык -------------------------------------------------------------------------------- /example/resources/langs/ru-RU.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Привет!", 3 | "msg": "Привет! {} добро пожаловать {} мир! ", 4 | "msg_named": "{} написан на языке {lang}", 5 | "clickMe": "Нажми на меня", 6 | "profile": { 7 | "reset_password": { 8 | "label": "Сбросить пароль", 9 | "username": "Логин", 10 | "password": "Пароль" 11 | } 12 | }, 13 | "clicked": { 14 | "zero": "Ты кликнул {} раз!", 15 | "one": "Ты кликнул {} раз!", 16 | "two": "Ты кликнул {} раза!", 17 | "few": "Ты кликнул {} раз!", 18 | "many": "Ты кликнул {} раз!", 19 | "other": "Ты кликнул {} раз!" 20 | }, 21 | "amount": { 22 | "zero": "Твой счет : {} ", 23 | "one": "Твой счет : {} ", 24 | "two": "Твой счет : {} ", 25 | "few": "Твой счет : {} ", 26 | "many": "Твой счет : {} ", 27 | "other": "Твой счет : {} " 28 | }, 29 | "gender": { 30 | "male": "Привет мужык ;) ", 31 | "female": "Привет девчуля :)", 32 | "with_arg": { 33 | "male": "Привет мужык ;) {}", 34 | "female": "Привет девчуля :) {}" 35 | } 36 | }, 37 | "reset_locale": "Сбросить язык" 38 | } -------------------------------------------------------------------------------- /example/resources/langs/ru-RU.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Твой счет : {} 5 | Твой счет : {} 6 | Твой счет : {} 7 | Твой счет : {} 8 | Твой счет : {} 9 | Твой счет : {} 10 | 11 | Нажми на меня 12 | 13 | Ты кликнул {} раз! 14 | Ты кликнул {} раз! 15 | Ты кликнул {} раз! 16 | Ты кликнул {} раз! 17 | Ты кликнул {} раза! 18 | Ты кликнул {} раз! 19 | 20 | 21 | Привет девчуля :) 22 | Привет мужык ;) 23 | 24 | Привет девчуля :) {} 25 | Привет мужык ;) {} 26 | 27 | 28 | Привет! {} добро пожаловать {} мир! 29 | {} написан на языке {lang} 30 | 31 | 32 | 33 | Пароль 34 | Логин 35 | 36 | 37 | Сбросить язык 38 | Привет! 39 | -------------------------------------------------------------------------------- /example/resources/langs/ru-RU.yaml: -------------------------------------------------------------------------------- 1 | title: Привет! 2 | msg: 'Привет! {} добро пожаловать {} мир! ' 3 | msg_named: "{} написан на языке {lang}" 4 | clickMe: Нажми на меня 5 | profile: 6 | reset_password: 7 | label: Сбросить пароль 8 | username: Логин 9 | password: Пароль 10 | clicked: 11 | zero: Ты кликнул {} раз! 12 | one: Ты кликнул {} раз! 13 | two: Ты кликнул {} раза! 14 | few: Ты кликнул {} раз! 15 | many: Ты кликнул {} раз! 16 | other: Ты кликнул {} раз! 17 | amount: 18 | zero: 'Твой счет : {} ' 19 | one: 'Твой счет : {} ' 20 | two: 'Твой счет : {} ' 21 | few: 'Твой счет : {} ' 22 | many: 'Твой счет : {} ' 23 | other: 'Твой счет : {} ' 24 | gender: 25 | male: 'Привет мужык ;) ' 26 | female: Привет девчуля :) 27 | with_arg: 28 | male: Привет мужык ;) {} 29 | female: Привет девчуля :) {} 30 | reset_locale: Сбросить язык 31 | -------------------------------------------------------------------------------- /example/resources/langs/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Привет!", 3 | "msg": "Привет! {} добро пожаловать {} мир! ", 4 | "msg_named": "{} написан на языке {lang}", 5 | "clickMe": "Нажми на меня", 6 | "profile": { 7 | "reset_password": { 8 | "label": "Сбросить пароль", 9 | "username": "Логин", 10 | "password": "Пароль" 11 | } 12 | }, 13 | "clicked": { 14 | "zero": "Ты кликнул {} раз!", 15 | "one": "Ты кликнул {} раз!", 16 | "two": "Ты кликнул {} раза!", 17 | "few": "Ты кликнул {} раз!", 18 | "many": "Ты кликнул {} раз!", 19 | "other": "Ты кликнул {} раз!" 20 | }, 21 | "amount": { 22 | "zero": "Твой счет : {} ", 23 | "one": "Твой счет : {} ", 24 | "two": "Твой счет : {} ", 25 | "few": "Твой счет : {} ", 26 | "many": "Твой счет : {} ", 27 | "other": "Твой счет : {} " 28 | }, 29 | "gender": { 30 | "male": "Привет мужык ;) ", 31 | "female": "Привет девчуля :)", 32 | "with_arg": { 33 | "male": "Привет мужык ;) {}", 34 | "female": "Привет девчуля :) {}" 35 | } 36 | }, 37 | "reset_locale": "Сбросить язык" 38 | } -------------------------------------------------------------------------------- /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:example/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /example/web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/web/favicon.png -------------------------------------------------------------------------------- /example/web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/web/icons/Icon-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/example/web/icons/Icon-512.png -------------------------------------------------------------------------------- /example/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | example 18 | 19 | 20 | 21 | 24 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /example/web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "short_name": "example", 4 | "start_url": ".", 5 | "display": "minimal-ui", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /i18n/ar-DZ.json: -------------------------------------------------------------------------------- 1 | { 2 | "test": "اختبار", 3 | "day": { 4 | "zero": "{} يوم", 5 | "one": "{} يوم", 6 | "two": "{} أيام", 7 | "few": "{} أيام", 8 | "many": "{} يوم", 9 | "other": "{} يوم" 10 | } 11 | } -------------------------------------------------------------------------------- /i18n/ar.json: -------------------------------------------------------------------------------- 1 | { 2 | "test": "اختبار", 3 | "day": { 4 | "zero": "{} يوم", 5 | "one": "{} يوم", 6 | "two": "{} أيام", 7 | "few": "{} أيام", 8 | "many": "{} يوم", 9 | "other": "{} يوم" 10 | } 11 | } -------------------------------------------------------------------------------- /i18n/en-US.json: -------------------------------------------------------------------------------- 1 | { 2 | "test": "test_en-US", 3 | "day": { 4 | "zero": "{} days", 5 | "one": "{} day", 6 | "two": "{} days", 7 | "few": "{} few days", 8 | "many": "{} many days", 9 | "other": "{} other days" 10 | }, 11 | "hat": { 12 | "zero": "no hats", 13 | "one": "one hat", 14 | "two": "two hats", 15 | "few": "few hats", 16 | "many": "many hats", 17 | "other": "other hats" 18 | }, 19 | "hat_other": { 20 | "other": "other hats" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /i18n/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "test": "test_en", 3 | "day": { 4 | "zero": "{} days", 5 | "one": "{} day", 6 | "two": "{} days", 7 | "few": "{} few days", 8 | "many": "{} many days", 9 | "other": "{} other days" 10 | }, 11 | "hat": { 12 | "zero": "no hats", 13 | "one": "one hat", 14 | "two": "two hats", 15 | "few": "few hats", 16 | "many": "many hats", 17 | "other": "other hats" 18 | }, 19 | "hat_other": { 20 | "other": "other hats" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/easy_localization.dart: -------------------------------------------------------------------------------- 1 | library easy_localization; 2 | 3 | export 'package:easy_localization/src/easy_localization_app.dart'; 4 | export 'package:easy_localization/src/asset_loader.dart'; 5 | export 'package:easy_localization/src/public.dart'; 6 | export 'package:easy_localization/src/public_ext.dart'; 7 | export 'package:intl/intl.dart'; 8 | -------------------------------------------------------------------------------- /lib/src/asset_loader.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:ui'; 3 | 4 | import 'package:easy_localization/easy_localization.dart'; 5 | import 'package:flutter/services.dart'; 6 | 7 | /// abstract class used to building your Custom AssetLoader 8 | /// Example: 9 | /// ``` 10 | ///class FileAssetLoader extends AssetLoader { 11 | /// @override 12 | /// Future> load(String path, Locale locale) async { 13 | /// final file = File(path); 14 | /// return json.decode(await file.readAsString()); 15 | /// } 16 | ///} 17 | /// ``` 18 | abstract class AssetLoader { 19 | const AssetLoader(); 20 | Future?> load(String path, Locale locale); 21 | } 22 | 23 | /// 24 | /// default used is RootBundleAssetLoader which uses flutter's assetloader 25 | /// 26 | class RootBundleAssetLoader extends AssetLoader { 27 | const RootBundleAssetLoader(); 28 | 29 | String getLocalePath(String basePath, Locale locale) { 30 | return '$basePath/${locale.toStringWithSeparator(separator: "-")}.json'; 31 | } 32 | 33 | @override 34 | Future?> load(String path, Locale locale) async { 35 | var localePath = getLocalePath(path, locale); 36 | EasyLocalization.logger.debug('Load asset from $path'); 37 | return json.decode(await rootBundle.loadString(localePath)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/src/easy_localization_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_localization/easy_localization.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:intl/intl_standalone.dart' 4 | if (dart.library.html) 'package:intl/intl_browser.dart'; 5 | import 'package:shared_preferences/shared_preferences.dart'; 6 | 7 | import 'translations.dart'; 8 | 9 | class EasyLocalizationController extends ChangeNotifier { 10 | static Locale? _savedLocale; 11 | static late Locale _deviceLocale; 12 | 13 | late Locale _locale; 14 | Locale? _fallbackLocale; 15 | List? _supportedLocales; 16 | 17 | final Function(FlutterError e) onLoadError; 18 | final AssetLoader assetLoader; 19 | final String path; 20 | final bool useFallbackTranslations; 21 | final bool saveLocale; 22 | final bool useOnlyLangCode; 23 | List? extraAssetLoaders; 24 | Translations? _translations, _fallbackTranslations; 25 | Translations? get translations => _translations; 26 | Translations? get fallbackTranslations => _fallbackTranslations; 27 | 28 | EasyLocalizationController({ 29 | required List supportedLocales, 30 | required this.useFallbackTranslations, 31 | required this.saveLocale, 32 | required this.assetLoader, 33 | required this.path, 34 | required this.useOnlyLangCode, 35 | required this.onLoadError, 36 | this.extraAssetLoaders, 37 | Locale? startLocale, 38 | Locale? fallbackLocale, 39 | Locale? forceLocale, // used for testing 40 | }) { 41 | _fallbackLocale = fallbackLocale; 42 | _supportedLocales = supportedLocales; 43 | if (forceLocale != null) { 44 | _locale = forceLocale; 45 | } else if (_savedLocale == null && startLocale != null) { 46 | _locale = _getFallbackLocale(supportedLocales, startLocale); 47 | EasyLocalization.logger('Start locale loaded ${_locale.toString()}'); 48 | } 49 | // If saved locale then get 50 | else if (saveLocale && _savedLocale != null) { 51 | EasyLocalization.logger('Saved locale loaded ${_savedLocale.toString()}'); 52 | _locale = selectLocaleFrom( 53 | supportedLocales, 54 | _savedLocale!, 55 | fallbackLocale: fallbackLocale, 56 | ); 57 | } else { 58 | // From Device Locale 59 | _locale = selectLocaleFrom( 60 | supportedLocales, 61 | _deviceLocale, 62 | fallbackLocale: fallbackLocale, 63 | ); 64 | } 65 | } 66 | 67 | @visibleForTesting 68 | static Locale selectLocaleFrom( 69 | List supportedLocales, 70 | Locale deviceLocale, { 71 | Locale? fallbackLocale, 72 | }) { 73 | final selectedLocale = supportedLocales.firstWhere( 74 | (locale) => locale.supports(deviceLocale), 75 | orElse: () => _getFallbackLocale( 76 | supportedLocales, 77 | fallbackLocale, 78 | deviceLocale: deviceLocale, 79 | ), 80 | ); 81 | return selectedLocale; 82 | } 83 | 84 | //Get fallback Locale 85 | static Locale _getFallbackLocale( 86 | List supportedLocales, Locale? fallbackLocale, 87 | {final Locale? deviceLocale}) { 88 | if (deviceLocale != null) { 89 | // a locale that matches the language code of the device locale is 90 | // preferred over the fallback locale 91 | final deviceLanguage = deviceLocale.languageCode; 92 | for (Locale locale in supportedLocales) { 93 | if (locale.languageCode == deviceLanguage) { 94 | return locale; 95 | } 96 | } 97 | } 98 | //If fallbackLocale not set then return first from supportedLocales 99 | if (fallbackLocale != null) { 100 | return fallbackLocale; 101 | } else { 102 | return supportedLocales.first; 103 | } 104 | } 105 | 106 | Future loadTranslations() async { 107 | Map data; 108 | try { 109 | data = Map.from(await loadTranslationData(_locale)); 110 | _translations = Translations(data); 111 | if (useFallbackTranslations && _fallbackLocale != null) { 112 | Map? baseLangData; 113 | if (_locale.countryCode != null && _locale.countryCode!.isNotEmpty) { 114 | baseLangData = 115 | await loadBaseLangTranslationData(Locale(locale.languageCode)); 116 | } 117 | data = Map.from(await loadTranslationData(_fallbackLocale!)); 118 | if (baseLangData != null) { 119 | try { 120 | data.addAll(baseLangData); 121 | } on UnsupportedError { 122 | data = Map.of(data)..addAll(baseLangData); 123 | } 124 | } 125 | _fallbackTranslations = Translations(data); 126 | } 127 | notifyListeners(); 128 | } on FlutterError catch (e) { 129 | onLoadError(e); 130 | } catch (e) { 131 | onLoadError(FlutterError(e.toString())); 132 | } 133 | } 134 | 135 | Future?> loadBaseLangTranslationData( 136 | Locale locale) async { 137 | try { 138 | return await loadTranslationData(Locale(locale.languageCode)); 139 | } on FlutterError catch (e) { 140 | // Disregard asset not found FlutterError when attempting to load base language fallback 141 | EasyLocalization.logger.warning(e.message); 142 | } 143 | return null; 144 | } 145 | 146 | Future> loadTranslationData(Locale locale) async => 147 | _combineAssetLoaders( 148 | path: path, 149 | locale: locale, 150 | assetLoader: assetLoader, 151 | useOnlyLangCode: useOnlyLangCode, 152 | extraAssetLoaders: extraAssetLoaders, 153 | ); 154 | 155 | Future> _combineAssetLoaders({ 156 | required String path, 157 | required Locale locale, 158 | required AssetLoader assetLoader, 159 | required bool useOnlyLangCode, 160 | List? extraAssetLoaders, 161 | }) async { 162 | final result = {}; 163 | final loaderFutures = ?>>[]; 164 | 165 | // need scriptCode, it might be better to use ignoreCountryCode as the variable name of useOnlyLangCode 166 | final Locale desiredLocale = 167 | useOnlyLangCode ? Locale.fromSubtags(languageCode: locale.languageCode, scriptCode: locale.scriptCode) : locale; 168 | 169 | List loaders = [ 170 | assetLoader, 171 | if (extraAssetLoaders != null) ...extraAssetLoaders 172 | ]; 173 | 174 | for (final loader in loaders) { 175 | loaderFutures.add(loader.load(path, desiredLocale)); 176 | } 177 | 178 | await Future.wait(loaderFutures).then((List?> value) { 179 | for (final Map? map in value) { 180 | if (map != null) { 181 | result.addAllRecursive(map); 182 | } 183 | } 184 | }); 185 | 186 | return result; 187 | } 188 | 189 | Locale get locale => _locale; 190 | 191 | Future setLocale(Locale l) async { 192 | _locale = l; 193 | await loadTranslations(); 194 | notifyListeners(); 195 | EasyLocalization.logger('Locale $locale changed'); 196 | await _saveLocale(_locale); 197 | } 198 | 199 | Future _saveLocale(Locale? locale) async { 200 | if (!saveLocale) return; 201 | final preferences = await SharedPreferences.getInstance(); 202 | await preferences.setString('locale', locale.toString()); 203 | EasyLocalization.logger('Locale $locale saved'); 204 | } 205 | 206 | static Future initEasyLocation() async { 207 | final preferences = await SharedPreferences.getInstance(); 208 | final strLocale = preferences.getString('locale'); 209 | _savedLocale = strLocale?.toLocale(); 210 | final foundPlatformLocale = await findSystemLocale(); 211 | _deviceLocale = foundPlatformLocale.toLocale(); 212 | EasyLocalization.logger.debug('Localization initialized'); 213 | } 214 | 215 | Future deleteSaveLocale() async { 216 | _savedLocale = null; 217 | final preferences = await SharedPreferences.getInstance(); 218 | await preferences.remove('locale'); 219 | EasyLocalization.logger('Saved locale deleted'); 220 | } 221 | 222 | Locale get deviceLocale => _deviceLocale; 223 | Locale? get savedLocale => _savedLocale; 224 | 225 | Future resetLocale() async { 226 | final locale = selectLocaleFrom(_supportedLocales!, deviceLocale, fallbackLocale: _fallbackLocale); 227 | 228 | EasyLocalization.logger('Reset locale to $locale while the platform locale is $_deviceLocale and the fallback locale is $_fallbackLocale'); 229 | await setLocale(locale); 230 | } 231 | } 232 | 233 | @visibleForTesting 234 | extension LocaleExtension on Locale { 235 | bool supports(Locale locale) { 236 | if (this == locale) { 237 | return true; 238 | } 239 | if (languageCode != locale.languageCode) { 240 | return false; 241 | } 242 | if (countryCode != null && 243 | countryCode!.isNotEmpty && 244 | countryCode != locale.countryCode) { 245 | return false; 246 | } 247 | if (scriptCode != null && scriptCode != locale.scriptCode) { 248 | return false; 249 | } 250 | 251 | return true; 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /lib/src/exceptions.dart: -------------------------------------------------------------------------------- 1 | class LocalizationNotFoundException implements Exception { 2 | const LocalizationNotFoundException(); 3 | 4 | @override 5 | String toString() => 'Localization not found for current context'; 6 | } 7 | -------------------------------------------------------------------------------- /lib/src/localization.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_localization/easy_localization.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | 4 | import 'plural_rules.dart'; 5 | import 'translations.dart'; 6 | 7 | class Localization { 8 | Translations? _translations, _fallbackTranslations; 9 | late Locale _locale; 10 | 11 | final RegExp _replaceArgRegex = RegExp('{}'); 12 | final RegExp _linkKeyMatcher = 13 | RegExp(r'(?:@(?:\.[a-z]+)?:(?:[\w\-_|.]+|\([\w\-_|.]+\)))'); 14 | final RegExp _linkKeyPrefixMatcher = RegExp(r'^@(?:\.([a-z]+))?:'); 15 | final RegExp _bracketsMatcher = RegExp('[()]'); 16 | final _modifiers = { 17 | 'upper': (String? val) => val!.toUpperCase(), 18 | 'lower': (String? val) => val!.toLowerCase(), 19 | 'capitalize': (String? val) => '${val![0].toUpperCase()}${val.substring(1)}' 20 | }; 21 | 22 | bool _useFallbackTranslationsForEmptyResources = false; 23 | bool _ignorePluralRules = false; 24 | 25 | Localization(); 26 | 27 | static Localization? _instance; 28 | 29 | static Localization get instance => _instance ?? (_instance = Localization()); 30 | 31 | static Localization? of(BuildContext context) => 32 | Localizations.of(context, Localization); 33 | 34 | static bool load( 35 | Locale locale, { 36 | Translations? translations, 37 | Translations? fallbackTranslations, 38 | bool useFallbackTranslationsForEmptyResources = false, 39 | bool ignorePluralRules = true, 40 | }) { 41 | instance._locale = locale; 42 | instance._translations = translations; 43 | instance._fallbackTranslations = fallbackTranslations; 44 | instance._useFallbackTranslationsForEmptyResources = 45 | useFallbackTranslationsForEmptyResources; 46 | instance._ignorePluralRules = ignorePluralRules; 47 | return translations == null ? false : true; 48 | } 49 | 50 | String tr( 51 | String key, { 52 | List? args, 53 | Map? namedArgs, 54 | String? gender, 55 | }) { 56 | late String res; 57 | 58 | if (gender != null) { 59 | res = _gender(key, gender: gender); 60 | } else { 61 | res = _resolve(key); 62 | } 63 | 64 | res = _replaceLinks(res); 65 | 66 | res = _replaceNamedArgs(res, namedArgs); 67 | 68 | return _replaceArgs(res, args); 69 | } 70 | 71 | String _replaceLinks(String res, {bool logging = true}) { 72 | // TODO: add recursion detection and a resolve stack. 73 | final matches = _linkKeyMatcher.allMatches(res); 74 | var result = res; 75 | 76 | for (final match in matches) { 77 | final link = match[0]!; 78 | final linkPrefixMatches = _linkKeyPrefixMatcher.allMatches(link); 79 | final linkPrefix = linkPrefixMatches.first[0]!; 80 | final formatterName = linkPrefixMatches.first[1]; 81 | 82 | // Remove the leading @:, @.case: and the brackets 83 | final linkPlaceholder = 84 | link.replaceAll(linkPrefix, '').replaceAll(_bracketsMatcher, ''); 85 | 86 | var translated = _resolve(linkPlaceholder); 87 | 88 | if (formatterName != null) { 89 | if (_modifiers.containsKey(formatterName)) { 90 | translated = _modifiers[formatterName]!(translated); 91 | } else { 92 | if (logging) { 93 | EasyLocalization.logger.warning( 94 | 'Undefined modifier $formatterName, available modifiers: ${_modifiers.keys.toString()}'); 95 | } 96 | } 97 | } 98 | 99 | result = 100 | translated.isEmpty ? result : result.replaceAll(link, translated); 101 | } 102 | 103 | return result; 104 | } 105 | 106 | String _replaceArgs(String res, List? args) { 107 | if (args == null || args.isEmpty) return res; 108 | for (var str in args) { 109 | res = res.replaceFirst(_replaceArgRegex, str); 110 | } 111 | return res; 112 | } 113 | 114 | String _replaceNamedArgs(String res, Map? args) { 115 | if (args == null || args.isEmpty) return res; 116 | args.forEach((String key, String value) => 117 | res = res.replaceAll(RegExp('{$key}'), value)); 118 | return res; 119 | } 120 | 121 | static PluralRule? _pluralRule(String? locale, num howMany) { 122 | if (instance._ignorePluralRules) { 123 | return () => _pluralCaseFallback(howMany); 124 | } 125 | startRuleEvaluation(howMany); 126 | return pluralRules[locale]; 127 | } 128 | 129 | static PluralCase _pluralCaseFallback(num value) { 130 | switch (value) { 131 | case 0: 132 | return PluralCase.ZERO; 133 | case 1: 134 | return PluralCase.ONE; 135 | case 2: 136 | return PluralCase.TWO; 137 | default: 138 | return PluralCase.OTHER; 139 | } 140 | } 141 | 142 | String plural( 143 | String key, 144 | num value, { 145 | List? args, 146 | Map? namedArgs, 147 | String? name, 148 | NumberFormat? format, 149 | }) { 150 | late String res; 151 | 152 | final pluralRule = _pluralRule(_locale.languageCode, value); 153 | final pluralCase = pluralRule != null ? pluralRule() : _pluralCaseFallback(value); 154 | 155 | switch (pluralCase) { 156 | case PluralCase.ZERO: 157 | res = _resolvePlural(key, 'zero'); 158 | break; 159 | case PluralCase.ONE: 160 | res = _resolvePlural(key, 'one'); 161 | break; 162 | case PluralCase.TWO: 163 | res = _resolvePlural(key, 'two'); 164 | break; 165 | case PluralCase.FEW: 166 | res = _resolvePlural(key, 'few'); 167 | break; 168 | case PluralCase.MANY: 169 | res = _resolvePlural(key, 'many'); 170 | break; 171 | case PluralCase.OTHER: 172 | res = _resolvePlural(key, 'other'); 173 | break; 174 | default: 175 | throw ArgumentError.value(value, 'howMany', 'Invalid plural argument'); 176 | } 177 | 178 | final formattedValue = format == null ? '$value' : format.format(value); 179 | 180 | if (name != null) { 181 | namedArgs = {...?namedArgs, name: formattedValue}; 182 | } 183 | res = _replaceNamedArgs(res, namedArgs); 184 | 185 | return _replaceArgs(res, args ?? [formattedValue]); 186 | } 187 | 188 | String _gender(String key, {required String gender}) { 189 | return _resolve('$key.$gender'); 190 | } 191 | 192 | String _resolvePlural(String key, String subKey) { 193 | if (subKey == 'other') return _resolve('$key.other'); 194 | 195 | final tag = '$key.$subKey'; 196 | var resource = _resolve(tag, logging: false, fallback: _fallbackTranslations != null); 197 | if (resource == tag) { 198 | resource = _resolve('$key.other'); 199 | } 200 | return resource; 201 | } 202 | 203 | String _resolve(String key, {bool logging = true, bool fallback = true}) { 204 | var resource = _translations?.get(key); 205 | if (resource == null || 206 | (_useFallbackTranslationsForEmptyResources && resource.isEmpty)) { 207 | if (logging) { 208 | EasyLocalization.logger.warning('Localization key [$key] not found'); 209 | } 210 | if (_fallbackTranslations == null || !fallback) { 211 | return key; 212 | } else { 213 | resource = _fallbackTranslations?.get(key); 214 | if (resource == null || 215 | (_useFallbackTranslationsForEmptyResources && resource.isEmpty)) { 216 | if (logging) { 217 | EasyLocalization.logger 218 | .warning('Fallback localization key [$key] not found'); 219 | } 220 | return key; 221 | } 222 | } 223 | } 224 | return resource; 225 | } 226 | 227 | bool exists(String key) { 228 | return _translations?.get(key) != null; 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /lib/src/public.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'package:intl/intl.dart'; 3 | import 'localization.dart'; 4 | 5 | /// {@template tr} 6 | /// Main function for translate your language keys 7 | /// [key] Localization key 8 | /// [BuildContext] The location in the tree where this widget builds 9 | /// [args] List of localized strings. Replaces {} left to right 10 | /// [namedArgs] Map of localized strings. Replaces the name keys {key_name} according to its name 11 | /// [gender] Gender switcher. Changes the localized string based on gender string 12 | /// 13 | /// Example: 14 | /// 15 | /// ```json 16 | /// { 17 | /// "msg":"{} are written in the {} language", 18 | /// "msg_named":"Easy localization is written in the {lang} language", 19 | /// "msg_mixed":"{} are written in the {lang} language", 20 | /// "gender":{ 21 | /// "male":"Hi man ;) {}", 22 | /// "female":"Hello girl :) {}", 23 | /// "other":"Hello {}" 24 | /// } 25 | /// } 26 | /// ``` 27 | /// ```dart 28 | /// Text('msg').tr(args: ['Easy localization', 'Dart']), // args 29 | /// Text('msg_named').tr(namedArgs: {'lang': 'Dart'}), // namedArgs 30 | /// Text('msg_mixed').tr(args: ['Easy localization'], namedArgs: {'lang': 'Dart'}), // args and namedArgs 31 | /// Text('gender').tr(gender: _gender ? "female" : "male"), // gender 32 | /// ``` 33 | /// {@endtemplate} 34 | String tr( 35 | String key, { 36 | BuildContext? context, 37 | List? args, 38 | Map? namedArgs, 39 | String? gender, 40 | }) { 41 | return context != null 42 | ? Localization.of(context)! 43 | .tr(key, args: args, namedArgs: namedArgs, gender: gender) 44 | : Localization.instance 45 | .tr(key, args: args, namedArgs: namedArgs, gender: gender); 46 | } 47 | 48 | bool trExists(String key, {BuildContext? context}) { 49 | return context != null 50 | ? Localization.of(context)! 51 | .exists(key) 52 | : Localization.instance 53 | .exists(key); 54 | } 55 | 56 | /// {@template plural} 57 | /// function translate with pluralization 58 | /// [key] Localization key 59 | /// [value] Number value for pluralization 60 | /// [BuildContext] The location in the tree where this widget builds 61 | /// [args] List of localized strings. Replaces {} left to right 62 | /// [namedArgs] Map of localized strings. Replaces the name keys {key_name} according to its name 63 | /// [name] Name of number value. Replaces {$name} to value 64 | /// [format] Formats a numeric value using a [NumberFormat](https://pub.dev/documentation/intl/latest/intl/NumberFormat-class.html) class 65 | /// 66 | /// Example: 67 | ///```json 68 | /// { 69 | /// "day": { 70 | /// "zero":"{} дней", 71 | /// "one": "{} день", 72 | /// "two": "{} дня", 73 | /// "few": "{} дня", 74 | /// "many": "{} дней", 75 | /// "other": "{} дней" 76 | /// }, 77 | /// "money": { 78 | /// "zero": "You not have money", 79 | /// "one": "You have {} dollar", 80 | /// "many": "You have {} dollars", 81 | /// "other": "You have {} dollars" 82 | /// }, 83 | /// "money_args": { 84 | /// "zero": "{} has no money", 85 | /// "one": "{} has {} dollar", 86 | /// "many": "{} has {} dollars", 87 | /// "other": "{} has {} dollars" 88 | /// }, 89 | /// "money_named_args": { 90 | /// "zero": "{name} has no money", 91 | /// "one": "{name} has {money} dollar", 92 | /// "many": "{name} has {money} dollars", 93 | /// "other": "{name} has {money} dollars" 94 | /// } 95 | /// } 96 | /// ``` 97 | /// 98 | ///```dart 99 | /// Text('money').plural(1000000, format: NumberFormat.compact(locale: context.locale.toString())) // output: You have 1M dollars 100 | /// print('day'.plural(21)); // output: 21 день 101 | /// var money = plural('money', 10.23) // output: You have 10.23 dollars 102 | /// var money = plural('money_args', 10.23, args: ['John', '10.23']) // output: John has 10.23 dollars 103 | /// var money = plural('money_named_args', 10.23, namedArgs: {'name': 'Jane'}, name: 'money') // output: Jane has 10.23 dollars 104 | /// ``` 105 | /// {@endtemplate} 106 | String plural( 107 | String key, 108 | num value, { 109 | List? args, 110 | BuildContext? context, 111 | Map? namedArgs, 112 | String? name, 113 | NumberFormat? format, 114 | }) { 115 | return context != null 116 | ? Localization.of(context)!.plural(key, value, 117 | args: args, namedArgs: namedArgs, name: name, format: format) 118 | : Localization.instance.plural(key, value, 119 | args: args, namedArgs: namedArgs, name: name, format: format); 120 | } 121 | -------------------------------------------------------------------------------- /lib/src/public_ext.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_localization/src/exceptions.dart'; 2 | import 'package:easy_localization/src/localization.dart'; 3 | import 'package:flutter/widgets.dart'; 4 | import 'package:intl/intl.dart'; 5 | 6 | import 'easy_localization_app.dart'; 7 | import 'public.dart' as ez; 8 | 9 | /// Text widget extension method for access to [tr()] and [plural()] 10 | /// Example : 11 | /// ```dart 12 | /// Text('title').tr() 13 | /// Text('day').plural(21) 14 | /// ``` 15 | extension TextTranslateExtension on Text { 16 | /// {@macro tr} 17 | Text tr( 18 | {List? args, 19 | BuildContext? context, 20 | Map? namedArgs, 21 | String? gender}) => 22 | Text( 23 | ez.tr( 24 | data ?? '', 25 | context: context, 26 | args: args, 27 | namedArgs: namedArgs, 28 | gender: gender, 29 | ), 30 | key: key, 31 | style: style, 32 | strutStyle: strutStyle, 33 | textAlign: textAlign, 34 | textDirection: textDirection, 35 | locale: locale, 36 | softWrap: softWrap, 37 | overflow: overflow, 38 | textScaleFactor: textScaleFactor, 39 | maxLines: maxLines, 40 | semanticsLabel: semanticsLabel, 41 | textWidthBasis: textWidthBasis); 42 | 43 | /// {@macro plural} 44 | Text plural( 45 | num value, { 46 | BuildContext? context, 47 | List? args, 48 | Map? namedArgs, 49 | String? name, 50 | NumberFormat? format, 51 | }) => 52 | Text( 53 | ez.plural( 54 | data ?? '', 55 | value, 56 | context: context, 57 | args: args, 58 | namedArgs: namedArgs, 59 | name: name, 60 | format: format, 61 | ), 62 | key: key, 63 | style: style, 64 | strutStyle: strutStyle, 65 | textAlign: textAlign, 66 | textDirection: textDirection, 67 | locale: locale, 68 | softWrap: softWrap, 69 | overflow: overflow, 70 | textScaleFactor: textScaleFactor, 71 | maxLines: maxLines, 72 | semanticsLabel: semanticsLabel, 73 | textWidthBasis: textWidthBasis); 74 | } 75 | 76 | /// Strings extension method for access to [tr()] and [plural()] 77 | /// Example : 78 | /// ```dart 79 | /// 'title'.tr() 80 | /// 'day'.plural(21) 81 | /// ``` 82 | extension StringTranslateExtension on String { 83 | /// {@macro tr} 84 | String tr({ 85 | List? args, 86 | Map? namedArgs, 87 | String? gender, 88 | BuildContext? context, 89 | }) => 90 | ez.tr(this, 91 | context: context, args: args, namedArgs: namedArgs, gender: gender); 92 | 93 | bool trExists({BuildContext? context}) => ez.trExists(this, context: context); 94 | 95 | /// {@macro plural} 96 | String plural( 97 | num value, { 98 | List? args, 99 | BuildContext? context, 100 | Map? namedArgs, 101 | String? name, 102 | NumberFormat? format, 103 | }) => 104 | ez.plural( 105 | this, 106 | value, 107 | context: context, 108 | args: args, 109 | namedArgs: namedArgs, 110 | name: name, 111 | format: format, 112 | ); 113 | } 114 | 115 | /// BuildContext extension method for access to [locale], [supportedLocales], [fallbackLocale], [delegates] and [deleteSaveLocale()] 116 | /// 117 | /// Example : 118 | /// 119 | /// ```dart 120 | /// context.locale = Locale('en', 'US'); 121 | /// print(context.locale.toString()); 122 | /// 123 | /// context.deleteSaveLocale(); 124 | /// 125 | /// print(context.supportedLocales); // output: [en_US, ar_DZ, de_DE, ru_RU] 126 | /// print(context.fallbackLocale); // output: en_US 127 | /// ``` 128 | extension BuildContextEasyLocalizationExtension on BuildContext { 129 | /// Get current locale 130 | Locale get locale => EasyLocalization.of(this)!.locale; 131 | 132 | /// Change app locale 133 | Future setLocale(Locale val) async => 134 | EasyLocalization.of(this)!.setLocale(val); 135 | 136 | /// Old Change app locale 137 | @Deprecated( 138 | 'This is the func used in the old version of EasyLocalization. The modern func is `setLocale(val)` . ' 139 | 'This feature was deprecated after v3.0.0') 140 | set locale(Locale val) => EasyLocalization.of(this)!.setLocale(val); 141 | 142 | /// Get List of supported locales. 143 | List get supportedLocales => 144 | EasyLocalization.of(this)!.supportedLocales; 145 | 146 | /// Get fallback locale 147 | Locale? get fallbackLocale => EasyLocalization.of(this)!.fallbackLocale; 148 | 149 | /// {@macro flutter.widgets.widgetsApp.localizationsDelegates} 150 | /// return 151 | /// ```dart 152 | /// delegates = [ 153 | /// delegate 154 | /// GlobalMaterialLocalizations.delegate, 155 | /// GlobalWidgetsLocalizations.delegate, 156 | /// GlobalCupertinoLocalizations.delegate 157 | /// ], 158 | /// ``` 159 | List get localizationDelegates => 160 | EasyLocalization.of(this)!.delegates; 161 | 162 | /// Clears a saved locale from device storage 163 | Future deleteSaveLocale() => 164 | EasyLocalization.of(this)!.deleteSaveLocale(); 165 | 166 | /// Getting device locale from platform 167 | Locale get deviceLocale => EasyLocalization.of(this)!.deviceLocale; 168 | Locale? get savedLocale => EasyLocalization.of(this)!.savedLocale; 169 | 170 | /// Reset locale to platform locale 171 | Future resetLocale() => EasyLocalization.of(this)!.resetLocale(); 172 | 173 | /// An extension method for translating your language keys. 174 | /// Subscribes the widget on current [Localization] that provided from context. 175 | /// Throws exception if [Localization] was not found. 176 | /// 177 | /// [key] Localization key 178 | /// [args] List of localized strings. Replaces {} left to right 179 | /// [namedArgs] Map of localized strings. Replaces the name keys {key_name} according to its name 180 | /// [gender] Gender switcher. Changes the localized string based on gender string 181 | /// 182 | /// Example: 183 | /// 184 | /// ```json 185 | /// { 186 | /// "msg":"{} are written in the {} language", 187 | /// "msg_named":"Easy localization is written in the {lang} language", 188 | /// "msg_mixed":"{} are written in the {lang} language", 189 | /// "gender":{ 190 | /// "male":"Hi man ;) {}", 191 | /// "female":"Hello girl :) {}", 192 | /// "other":"Hello {}" 193 | /// } 194 | /// } 195 | /// ``` 196 | /// ```dart 197 | /// Text(context.tr('msg', args: ['Easy localization', 'Dart']), // args 198 | /// Text(context.tr('msg_named', namedArgs: {'lang': 'Dart'}), // namedArgs 199 | /// Text(context.tr('msg_mixed', args: ['Easy localization'], namedArgs: {'lang': 'Dart'}), // args and namedArgs 200 | /// Text(context.tr('gender', gender: _gender ? "female" : "male"), // gender 201 | /// ``` 202 | String tr( 203 | String key, { 204 | List? args, 205 | Map? namedArgs, 206 | String? gender, 207 | }) { 208 | final localization = Localization.of(this); 209 | 210 | if (localization == null) { 211 | throw const LocalizationNotFoundException(); 212 | } 213 | 214 | return localization.tr( 215 | key, 216 | args: args, 217 | namedArgs: namedArgs, 218 | gender: gender, 219 | ); 220 | } 221 | 222 | bool trExists(String key) { 223 | final localization = Localization.of(this); 224 | 225 | if (localization == null) { 226 | throw const LocalizationNotFoundException(); 227 | } 228 | 229 | return localization.exists(key); 230 | } 231 | 232 | String plural( 233 | String key, 234 | num number, { 235 | List? args, 236 | Map? namedArgs, 237 | String? name, 238 | NumberFormat? format, 239 | }) { 240 | final localization = Localization.of(this); 241 | 242 | if (localization == null) { 243 | throw const LocalizationNotFoundException(); 244 | } 245 | 246 | return localization.plural( 247 | key, 248 | number, 249 | args: args, 250 | namedArgs: namedArgs, 251 | name: name, 252 | format: format, 253 | ); 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /lib/src/translations.dart: -------------------------------------------------------------------------------- 1 | class Translations { 2 | final Map? _translations; 3 | final Map _nestedKeysCache; 4 | 5 | Translations(this._translations) : _nestedKeysCache = {}; 6 | String? get(String key) { 7 | String? returnValue; 8 | 9 | /// Try to look it up as a nested key 10 | if (isNestedKey(key)) { 11 | returnValue = getNested(key); 12 | } 13 | 14 | /// If we failed to find the key as a nested key, then fall back 15 | /// to looking it up like normal. 16 | returnValue ??= _translations?[key]; 17 | 18 | return returnValue; 19 | } 20 | 21 | String? getNested(String key) { 22 | if (isNestedCached(key)) return _nestedKeysCache[key]; 23 | 24 | final keys = key.split('.'); 25 | final kHead = keys.first; 26 | 27 | var value = _translations![kHead]; 28 | 29 | // print(value); 30 | 31 | for (var i = 1; i < keys.length; i++) { 32 | if (value is Map) value = value[keys[i]]; 33 | } 34 | 35 | /// If we found the value, cache it. If the value is null then 36 | /// we're not going to cache it, and returning null instead. 37 | if (value != null) { 38 | cacheNestedKey(key, value); 39 | } 40 | 41 | return value; 42 | } 43 | 44 | // bool has(String key) => isNestedKey(key) 45 | // ? getNested(key) != null 46 | // : _translations.containsKey(key); 47 | 48 | bool isNestedCached(String key) => _nestedKeysCache.containsKey(key); 49 | 50 | void cacheNestedKey(String key, String value) { 51 | if (!isNestedKey(key)) { 52 | throw Exception('Cannot cache a key that is not nested.'); 53 | } 54 | 55 | _nestedKeysCache[key] = value; 56 | } 57 | 58 | bool isNestedKey(String key) => 59 | !_translations!.containsKey(key) && key.contains('.'); 60 | } 61 | -------------------------------------------------------------------------------- /lib/src/utils.dart: -------------------------------------------------------------------------------- 1 | part of 'easy_localization_app.dart'; 2 | 3 | /// Convert string locale [localeString] to [Locale] 4 | @Deprecated('Deprecated on Easy Localization 3.0') 5 | Locale localeFromString(String localeString) { 6 | final localeList = localeString.split('_'); 7 | switch (localeList.length) { 8 | case 2: 9 | return localeList.last.length == 4 // scriptCode length is 4 10 | ? Locale.fromSubtags( 11 | languageCode: localeList.first, 12 | scriptCode: localeList.last, 13 | ) 14 | : Locale(localeList.first, localeList.last); 15 | case 3: 16 | return Locale.fromSubtags( 17 | languageCode: localeList.first, 18 | scriptCode: localeList[1], 19 | countryCode: localeList.last, 20 | ); 21 | default: 22 | return Locale(localeList.first); 23 | } 24 | } 25 | 26 | /// Convert [locale] to Srting with custom [separator] 27 | @Deprecated('Deprecated on Easy Localization 3.0') 28 | String localeToString(Locale locale, {String separator = '_'}) { 29 | return locale.toString().split('_').join(separator); 30 | } 31 | 32 | /// [Easy Localization] locale helper 33 | extension LocaleToStringHelper on Locale { 34 | /// Convert [locale] to String with custom separator 35 | String toStringWithSeparator({String separator = '_'}) { 36 | return toString().split('_').join(separator); 37 | } 38 | } 39 | 40 | /// [Easy Localization] string locale helper 41 | extension StringToLocaleHelper on String { 42 | /// Convert string to [Locale] object 43 | Locale toLocale({String separator = '_'}) { 44 | final localeList = split(separator); 45 | switch (localeList.length) { 46 | case 2: 47 | return localeList.last.length == 4 // scriptCode length is 4 48 | ? Locale.fromSubtags( 49 | languageCode: localeList.first, 50 | scriptCode: localeList.last, 51 | ) 52 | : Locale(localeList.first, localeList.last); 53 | case 3: 54 | return Locale.fromSubtags( 55 | languageCode: localeList.first, 56 | scriptCode: localeList[1], 57 | countryCode: localeList.last, 58 | ); 59 | default: 60 | return Locale(localeList.first); 61 | } 62 | } 63 | } 64 | 65 | extension MapExtension on Map { 66 | void addAllRecursive(Map other) { 67 | for (final entry in other.entries) { 68 | final oldValue = this[entry.key]; 69 | final newValue = entry.value; 70 | 71 | if (oldValue is Map && newValue is Map) { 72 | oldValue.addAllRecursive(newValue); 73 | 74 | continue; 75 | } 76 | 77 | this[entry.key] = newValue; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/src/widgets.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class FutureErrorWidget extends StatelessWidget { 4 | final String msg; 5 | const FutureErrorWidget({Key? key, this.msg = 'Loading ...'}) 6 | : super(key: key); 7 | @override 8 | Widget build(BuildContext context) { 9 | return Container( 10 | color: Colors.white, 11 | child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ 12 | const Icon( 13 | Icons.error_outline, 14 | color: Colors.red, 15 | size: 64, 16 | textDirection: TextDirection.ltr, 17 | ), 18 | const SizedBox(height: 20), 19 | const Text( 20 | 'Easy Localization:', 21 | textAlign: TextAlign.center, 22 | textDirection: TextDirection.ltr, 23 | style: TextStyle( 24 | fontWeight: FontWeight.w700, color: Colors.red, fontSize: 25.0), 25 | ), 26 | const SizedBox(height: 10), 27 | Text( 28 | '"$msg"', 29 | textAlign: TextAlign.center, 30 | textDirection: TextDirection.ltr, 31 | style: const TextStyle( 32 | fontWeight: FontWeight.w500, color: Colors.red, fontSize: 14.0), 33 | ), 34 | const SizedBox(height: 30), 35 | // Center( 36 | // child: CircularProgressIndicator() 37 | // ), 38 | ]), 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /logo/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | Easy 42 | 43 | 44 | Localization 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /packages/easy_logger/.idea/libraries/Dart_SDK.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /packages/easy_logger/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /packages/easy_logger/.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /packages/easy_logger/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: b0a22998593fc605c723dee8ff4d9315c32cfe2c 8 | channel: beta 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /packages/easy_logger/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### [0.0.1] 2 | 3 | - initial release. 4 | -------------------------------------------------------------------------------- /packages/easy_logger/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /packages/easy_logger/README.md: -------------------------------------------------------------------------------- 1 |

2 | EasyLogger 3 |

4 | 5 | ## Getting Started 6 | 7 | ### 🔩 Installation 8 | 9 | Add to your `pubspec.yaml`: 10 | 11 | ```yaml 12 | dependencies: 13 | easy_logger: 14 | ``` 15 | 16 | ### ⚙️ Configuration logger 17 | 18 | Create global logger value 19 | 20 | ```dart 21 | import 'package:easy_logger/easy_logger.dart'; 22 | 23 | final EasyLogger logger = EasyLogger( 24 | name: 'NamePrefix', 25 | defaultLevel: LevelMessages.debug, 26 | enableBuildModes: [BuildMode.debug, BuildMode.profile, BuildMode.release], 27 | enableLevels: [LevelMessages.debug, LevelMessages.info, LevelMessages.error, LevelMessages.warning], 28 | ); 29 | 30 | void main() async { 31 | ... 32 | } 33 | ``` 34 | 35 | ### 📜 EasyLogger properties 36 | 37 | | Properties | Required | Default | Description | 38 | | ---------------- | -------- | ------------------------- | ----------- | 39 | | name | false | '' | Name prefix in the logging line. | 40 | | defaultLevel | false | LevelMessages.info | Default message level if no level is set when call [EasyLogger]. | 41 | | enableBuildModes | false | [BuildMode.debug, BuildMode.profile] | List of available build modes in which logging is enabled. | 42 | | enableLevels | false | [LevelMessages.debug, LevelMessages.info, LevelMessages.error, LevelMessages.warning] | List of available levels messages in which logging is enabled. | 43 | | printer | false | easyLogDefaultPrinter() | Default function printing. | 44 | 45 | ## Usage 46 | 47 | Simle usage: 48 | 49 | ```dart 50 | logger('Your log text'); 51 | ``` 52 | 53 | Or you can set the message level 54 | 55 | ```dart 56 | logger('Your log text', level: LevelMessages.info); 57 | ``` 58 | 59 | 60 | ### 🐛 StackTrace 61 | 62 | [EasyLogger] support [StackTrace] dump sending: 63 | 64 | ```dart 65 | try { 66 | //same code 67 | } on Exception catch (e, stackTrace) { 68 | logger('same error', stackTrace: stackTrace); 69 | } 70 | ``` 71 | 72 | ### 🖨️ Customise message or build levels 73 | 74 | [EasyLogger] supported Flutter build modes. Read more about [Build modes](https://flutter.dev/docs/testing/build-modes) 75 | 76 | ```dart 77 | // only debug and profile modes 78 | logger.enableBuildModes = [BuildMode.debug, BuildMode.profile] 79 | 80 | // logger off 81 | logger.enableLevels = [] 82 | ``` 83 | 84 | You can customize what levels of messages you need 85 | 86 | ```dart 87 | // show only errors 88 | logger.enableBuildModes = [LevelMessages.error] 89 | 90 | ``` 91 | 92 | ### 🖨️ Customise printer function 93 | 94 | [EasyLogger] have easiest way to change default printer function. 95 | Create your custom printer and past like parameter in class. 96 | 97 | ```dart 98 | EasyLogPrinter customLogPrinter = ( 99 | Object object, { 100 | String name, 101 | StackTrace stackTrace, 102 | LevelMessages level, 103 | }) { 104 | print('$name: ${object.toString()}'); 105 | }; 106 | 107 | final EasyLogger logger = EasyLogger( 108 | printer: customLogPrinter, 109 | ); 110 | ``` 111 | Or insert into class object 112 | 113 | ```dart 114 | logger.printer = customLogPrinter; 115 | ``` 116 | 117 | ## 🖨️ Helpers 118 | 119 | For easest using logger you can send messages without `level` parameter. 120 | 121 | ```dart 122 | logger.debug('your log text'); 123 | logger.info('your log text'); 124 | logger.warning('your log text'); 125 | logger.error('your log text'); 126 | ``` -------------------------------------------------------------------------------- /packages/easy_logger/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | #https://dart.dev/guides/language/analysis-options 2 | include: package:flutter_lints/flutter.yaml 3 | 4 | analyzer: 5 | errors: 6 | missing_required_param: warning 7 | missing_return: warning 8 | todo: ignore 9 | 10 | linter: 11 | rules: 12 | avoid_classes_with_only_static_members: false 13 | sort_constructors_first: true 14 | prefer_single_quotes: true 15 | public_member_api_docs: true 16 | always_specify_types: true -------------------------------------------------------------------------------- /packages/easy_logger/lib/easy_logger.dart: -------------------------------------------------------------------------------- 1 | export 'src/logger.dart'; 2 | export 'src/enums.dart'; 3 | export 'src/logger_printer.dart'; 4 | -------------------------------------------------------------------------------- /packages/easy_logger/lib/src/enums.dart: -------------------------------------------------------------------------------- 1 | /// Build mode enum used in [EasyLogger]. 2 | enum BuildMode { 3 | /// Release build mode 4 | release, 5 | 6 | /// Profile build mode 7 | profile, 8 | 9 | /// Debug build mode 10 | debug 11 | } 12 | 13 | /// Levels messages enum used in [EasyLogger]. 14 | enum LevelMessages { 15 | ///Debug level message 16 | debug, 17 | 18 | ///Information level message 19 | info, 20 | 21 | ///Warning level message 22 | warning, 23 | 24 | ///Error level message 25 | error, 26 | } 27 | -------------------------------------------------------------------------------- /packages/easy_logger/lib/src/logger.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | 3 | import 'enums.dart'; 4 | import 'logger_printer.dart'; 5 | 6 | /// Easy Logger callable class 7 | class EasyLogger { 8 | /// Customized logger, part of [EasyLocalization](https://github.com/aissat/easy_localization) ecosystem. 9 | /// Callable class, [more info](https://dart.dev/guides/language/language-tour#callable-classes) 10 | EasyLogger({ 11 | this.name = '', 12 | this.enableBuildModes = const [ 13 | BuildMode.profile, 14 | BuildMode.debug, 15 | ], 16 | this.enableLevels = const [ 17 | LevelMessages.debug, 18 | LevelMessages.info, 19 | LevelMessages.error, 20 | LevelMessages.warning, 21 | ], 22 | EasyLogPrinter? printer, 23 | this.defaultLevel = LevelMessages.info, 24 | }) { 25 | this.printer = printer ?? easyLogDefaultPrinter; 26 | _currentBuildMode = _getCurrentBuildMode(); 27 | } 28 | 29 | BuildMode? _currentBuildMode; 30 | 31 | /// Name prefix in the logging line. 32 | /// @Default value `''` empty string. 33 | /// Example: 34 | /// ``` 35 | /// [YourName] same log text 36 | /// ``` 37 | String name; 38 | 39 | /// List of available build modes in which logging is enabled. 40 | /// @Default value `const [BuildMode.profile, BuildMode.debug]` 41 | List enableBuildModes; 42 | 43 | /// List of available levels messages in which logging is enabled. 44 | /// @Default value `const [LevelMessages.debug, LevelMessages.info, LevelMessages.error, LevelMessages.warning]` 45 | List enableLevels; 46 | 47 | /// Default message level if no level is set when call [EasyLogger]. 48 | /// @Default value `LevelMessages.info` 49 | LevelMessages defaultLevel; 50 | 51 | /// Printer function instance. 52 | /// You can change the standard print function to a custom one. 53 | /// Example: 54 | /// ```dart 55 | /// EasyLogPrinter customLogPrinter = ( 56 | /// Object object, { 57 | /// String name, 58 | /// StackTrace stackTrace, 59 | /// LevelMessages level, 60 | /// }) { 61 | /// print('$name: ${object.toString()}'); 62 | /// }; 63 | /// 64 | /// logger.printer = customLogPrinter; 65 | /// ``` 66 | EasyLogPrinter? printer; 67 | 68 | BuildMode _getCurrentBuildMode() { 69 | if (kReleaseMode) { 70 | return BuildMode.release; 71 | } else if (kProfileMode) { 72 | return BuildMode.profile; 73 | } 74 | return BuildMode.debug; 75 | } 76 | 77 | /// Check [enableBuildModes] and [enableLevels] 78 | bool isEnabled(LevelMessages level) { 79 | if (!enableBuildModes.contains(_currentBuildMode)) { 80 | return false; 81 | } 82 | if (!enableLevels.contains(level)) { 83 | return false; 84 | } 85 | return true; 86 | } 87 | 88 | /// The main callable function for handling log messages. 89 | void call(Object object, {StackTrace? stackTrace, LevelMessages? level}) { 90 | level ??= defaultLevel; 91 | if (isEnabled(level)) { 92 | printer!(object, stackTrace: stackTrace, level: level, name: name); 93 | } 94 | } 95 | 96 | /// Helper for main callable function. 97 | /// Call logger function with level [LevelMessages.debug] 98 | void debug(Object object, {StackTrace? stackTrace}) => 99 | call(object, stackTrace: stackTrace, level: LevelMessages.debug); 100 | 101 | /// Helper for main callable function. 102 | /// Call logger function with level [LevelMessages.info] 103 | void info(Object object, {StackTrace? stackTrace}) => 104 | call(object, stackTrace: stackTrace, level: LevelMessages.info); 105 | 106 | /// Helper for main callable function. 107 | /// Call logger function with level [LevelMessages.warning] 108 | void warning(Object object, {StackTrace? stackTrace}) => 109 | call(object, stackTrace: stackTrace, level: LevelMessages.warning); 110 | 111 | /// Helper for main callable function. 112 | /// Call logger function with level [LevelMessages.error] 113 | void error(Object object, {StackTrace? stackTrace}) => 114 | call(object, stackTrace: stackTrace, level: LevelMessages.error); 115 | } 116 | -------------------------------------------------------------------------------- /packages/easy_logger/lib/src/logger_printer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | 3 | import '../easy_logger.dart'; 4 | 5 | /// Type for function printing/logging in [EasyLogger]. 6 | typedef EasyLogPrinter = Function(Object object, 7 | {String? name, LevelMessages? level, StackTrace? stackTrace}); 8 | 9 | /// Default debug-mode function printing. 10 | EasyLogPrinter easyLogDefaultPrinter = (Object object, 11 | {String? name, StackTrace? stackTrace, LevelMessages? level}) { 12 | final String levelName = level?.name != null ? '[${level?.name}] ' : ''; 13 | final String tag = name != null ? '[$name] ' : ''; 14 | 15 | if (kDebugMode) { 16 | print(_getColoredString(level, '$tag$levelName${object.toString()}')); 17 | 18 | if (stackTrace != null) { 19 | print(_getColoredString(level, '__________________________________')); 20 | print(_getColoredString(level, stackTrace.toString())); 21 | } 22 | } 23 | }; 24 | 25 | String _getColoredString(LevelMessages? level, String string) { 26 | switch (level) { 27 | case LevelMessages.debug: 28 | // gray 29 | return '\u001b[90m$string\u001b[0m'; 30 | case LevelMessages.info: 31 | // green 32 | return '\u001b[32m$string\u001b[0m'; 33 | case LevelMessages.warning: 34 | // blue 35 | return '\u001B[34m$string\u001b[0m'; 36 | case LevelMessages.error: 37 | // red 38 | return '\u001b[31m$string\u001b[0m'; 39 | default: 40 | // gray 41 | return '\u001b[90m$string\u001b[0m'; 42 | } 43 | } 44 | 45 | extension _LevelMessagesExtension on LevelMessages { 46 | String get name { 47 | switch (this) { 48 | case LevelMessages.debug: 49 | return 'DEBUG'; 50 | case LevelMessages.info: 51 | return 'INFO'; 52 | case LevelMessages.warning: 53 | return 'WARNING'; 54 | case LevelMessages.error: 55 | return 'ERROR'; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /packages/easy_logger/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: easy_logger 2 | description: Easy logger 3 | version: 0.0.2 4 | 5 | environment: 6 | sdk: '>=2.12.0-0 <3.0.0' 7 | 8 | dependencies: 9 | flutter: 10 | sdk: flutter 11 | 12 | dev_dependencies: 13 | flutter_test: 14 | sdk: flutter 15 | flutter_lints: ^2.0.1 16 | 17 | flutter: -------------------------------------------------------------------------------- /packages/easy_logger/test/easy_logger_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:easy_logger/easy_logger.dart'; 4 | import 'package:flutter_test/flutter_test.dart'; 5 | 6 | List printLog = []; 7 | ZoneSpecification? spec; 8 | dynamic Function() overridePrint(Function() testFn) => () { 9 | spec = ZoneSpecification( 10 | print: (_, __, ___, String msg) { 11 | // Add to log instead of printing to stdout 12 | printLog.add(msg); 13 | }, 14 | ); 15 | return Zone.current.fork(specification: spec).run(testFn); 16 | }; 17 | 18 | late EasyLogger logger; 19 | void main() { 20 | group('Logger testing print', () { 21 | logger = EasyLogger(name: 'test logger'); 22 | 23 | test('Logger print', overridePrint(() { 24 | printLog = []; 25 | logger('Same print'); 26 | expect(printLog.first, contains('Same print')); 27 | expect(printLog.first, contains(logger.name)); 28 | })); 29 | 30 | test('Logger print info', overridePrint(() { 31 | printLog = []; 32 | logger('print info', level: LevelMessages.info); 33 | expect(printLog.first, contains('print info')); 34 | expect(printLog.first, contains('[INFO]')); 35 | })); 36 | 37 | test('Logger print debug', overridePrint(() { 38 | printLog = []; 39 | logger('print debug', level: LevelMessages.debug); 40 | expect(printLog.first, contains('print debug')); 41 | expect(printLog.first, contains('[DEBUG]')); 42 | })); 43 | 44 | test('Logger print warning', overridePrint(() { 45 | printLog = []; 46 | logger('print warning', level: LevelMessages.warning); 47 | expect(printLog.first, contains('print warning')); 48 | expect(printLog.first, contains('[WARNING]')); 49 | })); 50 | 51 | test('Logger print error', overridePrint(() { 52 | printLog = []; 53 | logger('print error', level: LevelMessages.error); 54 | expect(printLog.first, contains('print error')); 55 | expect(printLog.first, contains('[ERROR]')); 56 | })); 57 | 58 | test('Logger print error with StackTrace', overridePrint(() { 59 | printLog = []; 60 | 61 | StackTrace testStackTrace; 62 | testStackTrace = StackTrace.fromString('test stack'); 63 | 64 | logger('print error', 65 | level: LevelMessages.error, stackTrace: testStackTrace); 66 | expect(printLog.first, contains('print error')); 67 | expect(printLog.first, contains('[ERROR]')); 68 | expect(printLog.last, contains('test stack')); 69 | })); 70 | }); 71 | 72 | group('Logger BuildModes', () { 73 | logger = EasyLogger(name: 'test logger'); 74 | 75 | test('Logger BuildModes is not null', () { 76 | expect(logger.enableBuildModes, isNotNull); 77 | expect(logger.enableBuildModes, isNotEmpty); 78 | }); 79 | 80 | test('Logger default enable', () { 81 | expect(logger.isEnabled(LevelMessages.debug), true); 82 | }); 83 | }); 84 | } 85 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: easy_localization 2 | description: Easy and Fast internationalizing and localization your Flutter Apps, this package simplify the internationalizing process . 3 | # author: AISSAT abdelwahab 4 | homepage: https://github.com/aissat/easy_localization 5 | issue_tracker: https://github.com/aissat/easy_localization/issues 6 | # publish_to: none 7 | 8 | version: 3.0.7+1 9 | 10 | environment: 11 | sdk: '>=2.12.0 <4.0.0' 12 | 13 | dependencies: 14 | flutter: 15 | sdk: flutter 16 | shared_preferences: '>=2.0.0 <3.0.0' 17 | intl: '>=0.17.0-0 <0.21.0' 18 | args: ^2.3.1 19 | path: ^1.8.1 20 | easy_logger: ^0.0.2 21 | flutter_localizations: 22 | sdk: flutter 23 | 24 | dev_dependencies: 25 | flutter_test: 26 | sdk: flutter 27 | flutter_lints: ^2.0.1 28 | -------------------------------------------------------------------------------- /screenshots/Screenshot_ar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/screenshots/Screenshot_ar.png -------------------------------------------------------------------------------- /screenshots/Screenshot_en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/screenshots/Screenshot_en.png -------------------------------------------------------------------------------- /screenshots/Screenshot_err.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aissat/easy_localization/bed8c0178c22ed300636b91aed056e037ff88959/screenshots/Screenshot_err.png -------------------------------------------------------------------------------- /test/easy_localization_extra_asset_loaders_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | import 'package:easy_localization/src/easy_localization_controller.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_test/flutter_test.dart'; 6 | 7 | import 'utils/test_asset_loaders.dart'; 8 | 9 | void main() { 10 | group('ExtraAssetLoaders', () { 11 | test('should work normal if no extraAssetLoaders is provided', () async { 12 | final EasyLocalizationController controller = EasyLocalizationController( 13 | forceLocale: const Locale('en'), 14 | path: 'path/en.json', 15 | supportedLocales: const [Locale('en')], 16 | useOnlyLangCode: true, 17 | useFallbackTranslations: false, 18 | saveLocale: false, 19 | onLoadError: (FlutterError e) { 20 | log(e.toString()); 21 | }, 22 | assetLoader: const ImmutableJsonAssetLoader(), 23 | extraAssetLoaders: null, 24 | ); 25 | 26 | var result = await controller.loadTranslationData(const Locale('en')); 27 | 28 | expect(result, {'test': 'test'}); 29 | expect(result.entries.length, 1); 30 | }); 31 | 32 | test('load assets from external loader and merge with asset loader', 33 | () async { 34 | final EasyLocalizationController controller = EasyLocalizationController( 35 | forceLocale: const Locale('en'), 36 | path: 'path/en.json', 37 | supportedLocales: const [Locale('en')], 38 | useOnlyLangCode: true, 39 | useFallbackTranslations: false, 40 | saveLocale: false, 41 | onLoadError: (FlutterError e) { 42 | log(e.toString()); 43 | }, 44 | assetLoader: const ImmutableJsonAssetLoader(), 45 | extraAssetLoaders: [const ExternalAssetLoader()], 46 | ); 47 | 48 | final Map result = 49 | await controller.loadTranslationData(const Locale('en')); 50 | 51 | expect(result, { 52 | 'test': 'test', 53 | 'package_value_01': 'package_value_01', 54 | 'package_value_02': 'package_value_02', 55 | 'package_value_03': 'package_value_03', 56 | }); 57 | expect(result.entries.length, 4); 58 | }); 59 | 60 | test( 61 | 'load assets from external loader with nested translations and merge with asset loader', 62 | () async { 63 | final EasyLocalizationController controller = EasyLocalizationController( 64 | forceLocale: const Locale('en'), 65 | path: 'path/en.json', 66 | supportedLocales: const [Locale('en')], 67 | useOnlyLangCode: true, 68 | useFallbackTranslations: false, 69 | saveLocale: false, 70 | onLoadError: (FlutterError e) { 71 | log(e.toString()); 72 | }, 73 | assetLoader: const ImmutableJsonAssetLoader(), 74 | extraAssetLoaders: [const NestedAssetLoader()], 75 | ); 76 | 77 | final Map result = 78 | await controller.loadTranslationData(const Locale('en')); 79 | 80 | expect(result, { 81 | 'test': 'test', 82 | 'nested': { 83 | 'super': { 84 | 'duper': { 85 | 'nested': 'nested.super.duper.nested', 86 | } 87 | } 88 | }, 89 | }); 90 | expect(result.entries.length, 2); 91 | }); 92 | 93 | test( 94 | 'load assets from external loader and merge duplicates with asset loader', 95 | () async { 96 | final EasyLocalizationController controller = EasyLocalizationController( 97 | forceLocale: const Locale('en'), 98 | path: 'path/en.json', 99 | supportedLocales: const [Locale('en')], 100 | useOnlyLangCode: true, 101 | useFallbackTranslations: false, 102 | saveLocale: false, 103 | onLoadError: (FlutterError e) { 104 | log(e.toString()); 105 | }, 106 | assetLoader: const ImmutableJsonAssetLoader(), 107 | extraAssetLoaders: [const ImmutableJsonAssetLoader()], 108 | ); 109 | 110 | final Map result = 111 | await controller.loadTranslationData(const Locale('en')); 112 | 113 | expect(result, {'test': 'test'}); 114 | expect(result.entries.length, 1); 115 | }); 116 | }); 117 | } 118 | -------------------------------------------------------------------------------- /test/easy_localization_init_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_localization/easy_localization.dart'; 2 | import 'package:easy_logger/easy_logger.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_test/flutter_test.dart'; 5 | import 'package:shared_preferences/shared_preferences.dart'; 6 | 7 | import 'easy_localization_context_test.dart'; 8 | 9 | Future main() async { 10 | EasyLocalization.logger.enableLevels = [ 11 | LevelMessages.error, 12 | LevelMessages.warning, 13 | ]; 14 | 15 | SharedPreferences.setMockInitialValues({}); 16 | EasyLocalization.logger.enableLevels = [ 17 | LevelMessages.error, 18 | LevelMessages.warning, 19 | ]; 20 | 21 | await EasyLocalization.ensureInitialized(); 22 | 23 | testWidgets( 24 | 'Ensure that loading the translations will update its depending widgets', 25 | (WidgetTester tester) async { 26 | await tester.runAsync(() async { 27 | await tester.pumpWidget(EasyLocalization( 28 | supportedLocales: const [Locale('en'), Locale('de')], 29 | path: '../../i18n', 30 | fallbackLocale: const Locale('en'), 31 | child: const I18nObserver(child: MyApp()), 32 | )); 33 | await tester.pump(); 34 | }); 35 | }, 36 | ); 37 | } 38 | 39 | class I18nObserver extends StatefulWidget { 40 | final Widget child; 41 | 42 | const I18nObserver({Key? key, required this.child}) : super(key: key); 43 | 44 | @override 45 | State createState() => _I18nObserverState(); 46 | } 47 | 48 | class _I18nObserverState extends State { 49 | var _firstUpdate = true; 50 | 51 | @override 52 | Widget build(BuildContext context) => widget.child; 53 | 54 | @override 55 | void didChangeDependencies() { 56 | // use the dependOnInheritedWidgetOfExactType pattern 57 | EasyLocalization.of(context); 58 | 59 | super.didChangeDependencies(); 60 | 61 | if (_firstUpdate) { 62 | _firstUpdate = false; 63 | expect( 64 | 'test'.tr(), 65 | 'test', 66 | reason: 'The translation cannot be found yet', 67 | ); 68 | } else { 69 | expect( 70 | 'test'.tr(), 71 | 'test_en', 72 | reason: 'The translation should be loaded on the second call ' 73 | 'of didChangeDependencies()', 74 | ); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /test/easy_localization_language_specific_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | import 'package:easy_localization/src/easy_localization_controller.dart'; 4 | import 'package:easy_localization/src/localization.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter_test/flutter_test.dart'; 7 | 8 | import 'utils/test_asset_loaders.dart'; 9 | 10 | void main() { 11 | group('language-specific-plurals', () { 12 | var r = EasyLocalizationController( 13 | forceLocale: const Locale('fb'), 14 | supportedLocales: [ 15 | const Locale('en'), 16 | const Locale('ru'), 17 | const Locale('fb') 18 | ], 19 | fallbackLocale: const Locale('fb'), 20 | path: 'path', 21 | useOnlyLangCode: true, 22 | useFallbackTranslations: true, 23 | onLoadError: (FlutterError e) { 24 | log(e.toString()); 25 | }, 26 | saveLocale: false, 27 | assetLoader: const JsonAssetLoader()); 28 | 29 | setUpAll(() async { 30 | await r.loadTranslations(); 31 | }); 32 | 33 | test('english one', () async { 34 | Localization.load(const Locale('en'), 35 | translations: r.translations, 36 | fallbackTranslations: r.fallbackTranslations); 37 | expect(Localization.instance.plural('hat', 1), 'one hat'); 38 | }); 39 | test('english other (default)', () async { 40 | Localization.load(const Locale('en'), 41 | translations: r.translations, 42 | fallbackTranslations: r.fallbackTranslations); 43 | expect(Localization.instance.plural('hat', 2), 'two hats'); 44 | expect(Localization.instance.plural('hat', 0), 'no hats'); 45 | expect(Localization.instance.plural('hat', 3), 'other hats'); 46 | }); 47 | test('english other (with ignorePluralRules)', () async { 48 | Localization.load(const Locale('en'), 49 | translations: r.translations, 50 | fallbackTranslations: r.fallbackTranslations, 51 | ignorePluralRules: false); 52 | expect(Localization.instance.plural('hat', 2), 'other hats'); 53 | expect(Localization.instance.plural('hat', 0), 'other hats'); 54 | expect(Localization.instance.plural('hat', 3), 'other hats'); 55 | }); 56 | test('russian one', () async { 57 | Localization.load(const Locale('ru'), 58 | translations: r.translations, 59 | fallbackTranslations: r.fallbackTranslations); 60 | expect(Localization.instance.plural('hat', 1), 'one hat'); 61 | }); 62 | test('russian few (default)', () async { 63 | Localization.load(const Locale('ru'), 64 | translations: r.translations, 65 | fallbackTranslations: r.fallbackTranslations); 66 | expect(Localization.instance.plural('hat', 2), 'two hats'); 67 | expect(Localization.instance.plural('hat', 3), 'other hats'); 68 | }); 69 | test('russian few (with ignorePluralRules)', () async { 70 | Localization.load(const Locale('ru'), 71 | translations: r.translations, 72 | fallbackTranslations: r.fallbackTranslations, 73 | ignorePluralRules: false); 74 | expect(Localization.instance.plural('hat', 2), 'few hats'); 75 | expect(Localization.instance.plural('hat', 3), 'few hats'); 76 | }); 77 | test('russian many (default)', () async { 78 | Localization.load(const Locale('ru'), 79 | translations: r.translations, 80 | fallbackTranslations: r.fallbackTranslations); 81 | expect(Localization.instance.plural('hat', 0), 'no hats'); 82 | expect(Localization.instance.plural('hat', 5), 'other hats'); 83 | }); 84 | test('russian many (with ignorePluralRules)', () async { 85 | Localization.load(const Locale('ru'), 86 | translations: r.translations, 87 | fallbackTranslations: r.fallbackTranslations, 88 | ignorePluralRules: false); 89 | expect(Localization.instance.plural('hat', 0), 'many hats'); 90 | expect(Localization.instance.plural('hat', 5), 'many hats'); 91 | }); 92 | }); 93 | } 94 | -------------------------------------------------------------------------------- /test/easy_localization_logger_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_localization/easy_localization.dart'; 2 | import 'package:easy_logger/easy_logger.dart'; 3 | import 'package:flutter_test/flutter_test.dart'; 4 | 5 | import 'easy_localization_utils_test.dart'; 6 | 7 | void main() async { 8 | group('Logger testing', () { 9 | test('Logger enable', () { 10 | expect(EasyLocalization.logger, equals(EasyLocalization.logger)); 11 | expect(EasyLocalization.logger, isNotNull); 12 | }); 13 | 14 | test('Logger print', overridePrint(() { 15 | printLog = []; 16 | EasyLocalization.logger('Same print'); 17 | expect(printLog.first, contains('Same print')); 18 | expect(printLog.first, contains(EasyLocalization.logger.name)); 19 | })); 20 | 21 | test('Logger print info', overridePrint(() { 22 | printLog = []; 23 | EasyLocalization.logger('print info', level: LevelMessages.info); 24 | expect(printLog.first, contains('print info')); 25 | expect(printLog.first, contains('[INFO]')); 26 | })); 27 | 28 | test('Logger print debug', overridePrint(() { 29 | printLog = []; 30 | EasyLocalization.logger('print debug', level: LevelMessages.debug); 31 | expect(printLog.first, contains('print debug')); 32 | expect(printLog.first, contains('[DEBUG]')); 33 | })); 34 | 35 | test('Logger print warning', overridePrint(() { 36 | printLog = []; 37 | EasyLocalization.logger('print warning', level: LevelMessages.warning); 38 | expect(printLog.first, contains('print warning')); 39 | expect(printLog.first, contains('[WARNING]')); 40 | })); 41 | 42 | test('Logger print error', overridePrint(() { 43 | printLog = []; 44 | EasyLocalization.logger('print error', level: LevelMessages.error); 45 | expect(printLog.first, contains('print error')); 46 | expect(printLog.first, contains('[ERROR]')); 47 | })); 48 | 49 | test('Logger print error with StackTrace', overridePrint(() { 50 | printLog = []; 51 | 52 | StackTrace testStackTrace; 53 | testStackTrace = StackTrace.fromString('test stack'); 54 | 55 | EasyLocalization.logger('print error', 56 | level: LevelMessages.error, stackTrace: testStackTrace); 57 | expect(printLog.first, contains('print error')); 58 | expect(printLog.first, contains('[ERROR]')); 59 | expect(printLog.last, contains('test stack')); 60 | })); 61 | }); 62 | } 63 | -------------------------------------------------------------------------------- /test/easy_localization_utils_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:easy_localization/easy_localization.dart'; 4 | import 'package:flutter/widgets.dart'; 5 | import 'package:flutter_test/flutter_test.dart'; 6 | 7 | var printLog = []; 8 | dynamic overridePrint(Function() testFn) => () { 9 | var spec = ZoneSpecification(print: (_, __, ___, String msg) { 10 | // Add to log instead of printing to stdout 11 | printLog.add(msg); 12 | }); 13 | return Zone.current.fork(specification: spec).run(testFn); 14 | }; 15 | 16 | void main() { 17 | group('Utils', () { 18 | group('Locales', () { 19 | test('localeFromString only language code', () { 20 | var locale = 'en'.toLocale(); 21 | expect(locale, const Locale('en')); 22 | }); 23 | 24 | test('localeFromString language code and country code', () { 25 | var locale = 'en_US'.toLocale(); 26 | expect(locale, const Locale('en', 'US')); 27 | }); 28 | 29 | test('localeFromString language code and script code', () { 30 | var locale = 'zh_Hant'.toLocale(); 31 | expect(locale, 32 | const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant')); 33 | }); 34 | 35 | test('localeFromString language, country, script code', () { 36 | var locale = 'zh_Hant_HK'.toLocale(); 37 | expect( 38 | locale, 39 | const Locale.fromSubtags( 40 | languageCode: 'zh', scriptCode: 'Hant', countryCode: 'HK')); 41 | }); 42 | 43 | test('localeToString', () { 44 | var locale = const Locale.fromSubtags( 45 | languageCode: 'zh', scriptCode: 'Hant', countryCode: 'HK'); 46 | var string = locale.toStringWithSeparator(); 47 | expect(string, 'zh_Hant_HK'); 48 | }); 49 | 50 | test('localeToString custom separator', () { 51 | var locale = const Locale.fromSubtags( 52 | languageCode: 'zh', scriptCode: 'Hant', countryCode: 'HK'); 53 | var string = locale.toStringWithSeparator(separator: '|'); 54 | expect(string, 'zh|Hant|HK'); 55 | }); 56 | }); 57 | 58 | group('MapExtension', () { 59 | test('should add all key value pairs recursively', () { 60 | final Map map1 = { 61 | 'key1': 'value1', 62 | 'key2': { 63 | 'key3': 'value3', 64 | 'key4': 'value4', 65 | }, 66 | }; 67 | 68 | final Map map2 = { 69 | 'key2': { 70 | 'key4': 'new_value4', 71 | 'key5': 'value5', 72 | }, 73 | 'key6': 'value6', 74 | }; 75 | 76 | map1.addAllRecursive(map2); 77 | 78 | expect(map1, { 79 | 'key1': 'value1', 80 | 'key2': { 81 | 'key3': 'value3', 82 | 'key4': 'new_value4', 83 | 'key5': 'value5', 84 | }, 85 | 'key6': 'value6', 86 | }); 87 | }); 88 | 89 | test('should work with empty maps', () { 90 | final Map map1 = {}; 91 | final Map map2 = { 92 | 'key1': 'value1', 93 | 'key2': { 94 | 'key3': 'value3', 95 | 'key4': 'value4', 96 | }, 97 | }; 98 | 99 | map1.addAllRecursive(map2); 100 | 101 | expect(map1, { 102 | 'key1': 'value1', 103 | 'key2': { 104 | 'key3': 'value3', 105 | 'key4': 'value4', 106 | }, 107 | }); 108 | }); 109 | }); 110 | }); 111 | } 112 | -------------------------------------------------------------------------------- /test/utils/test_asset_loaders.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:easy_localization/src/asset_loader.dart'; 4 | 5 | class ImmutableJsonAssetLoader extends AssetLoader { 6 | const ImmutableJsonAssetLoader(); 7 | 8 | @override 9 | Future> load(String fullPath, Locale locale) { 10 | return Future.value(const { 11 | 'test': 'test', 12 | }); 13 | } 14 | } 15 | 16 | class JsonAssetLoader extends AssetLoader { 17 | const JsonAssetLoader(); 18 | 19 | @override 20 | Future> load(String fullPath, Locale locale) { 21 | return Future.value({ 22 | 'test': 'test', 23 | 'test_empty': '', 24 | 'test_replace_one': 'test replace {}', 25 | 'test_replace_two': 'test replace {} {}', 26 | 'test_replace_named': 'test named replace {arg1} {arg2}', 27 | 'gender': {'male': 'Hi man ;)', 'female': 'Hello girl :)'}, 28 | 'gender_and_replace': { 29 | 'male': 'Hi {} man ;)', 30 | 'female': 'Hello {} girl :)' 31 | }, 32 | 'day': { 33 | 'zero': '{} days', 34 | 'one': '{} day', 35 | 'two': '{} days', 36 | 'few': '{} few days', 37 | 'many': '{} many days', 38 | 'other': '{} other days' 39 | }, 40 | 'hat': { 41 | 'zero': 'no hats', 42 | 'one': 'one hat', 43 | 'two': 'two hats', 44 | 'few': 'few hats', 45 | 'many': 'many hats', 46 | 'other': 'other hats' 47 | }, 48 | 'hat_other': {'other': 'other hats'}, 49 | 'money': { 50 | 'zero': '{} has no money', 51 | 'one': '{} has {} dollar', 52 | 'other': '{} has {} dollars', 53 | }, 54 | 'money_named_args': { 55 | 'zero': '{name} has no money', 56 | 'one': '{name} has {money} dollar', 57 | 'other': '{name} has {money} dollars', 58 | }, 59 | 'nested_periods': { 60 | 'Processing': 'Processing', 61 | 'Processing.': 'Processing.', 62 | }, 63 | 'nested.but.not.nested': 'nested but not nested', 64 | 'linked': 'this @:isLinked', 65 | 'isLinked': 'is linked', 66 | 'linkAndModify': 'this is linked and @.upper:modified', 67 | 'modified': 'modified', 68 | 'linkMany': '@:many @.capitalize:locale @:messages', 69 | 'many': 'many', 70 | 'locale': 'locale', 71 | 'messages': 'messages', 72 | 'linkedWithBrackets': 'linked with @.lower:(brackets).', 73 | 'brackets': 'Brackets', 74 | 'nestedArguments': 'this is {} @.undefiend:nestedArg', 75 | 'nestedArg': 'nested {}{}', 76 | 'nestedNamedArguments': '{firstArg} is a @:nestedNamedArg', 77 | 'nestedNamedArg': 'nested {secondArg}{thirdArg}', 78 | 'nested': { 79 | 'super': { 80 | 'duper': { 81 | 'nested': 'nested.super.duper.nested', 82 | 'nested_with_arg': 'nested.super.duper.nested_with_arg {}', 83 | 'nested_with_named_arg': 84 | 'nested.super.duper.nested_with_named_arg {arg}' 85 | } 86 | } 87 | }, 88 | 'path': fullPath, 89 | 'test_missing_fallback': 90 | (locale.languageCode == 'fb' ? 'fallback!' : null), 91 | 'test_empty_fallback': (locale.languageCode == 'fb' ? 'fallback!' : ''), 92 | 'test_fallback_plurals': (locale.languageCode == 'fb' 93 | ? { 94 | 'zero': 'fallback zero', 95 | 'one': 'fallback one', 96 | 'two': 'fallback two', 97 | 'few': 'fallback few', 98 | 'many': 'fallback many', 99 | 'other': 'fallback other', 100 | } 101 | : { 102 | 'one': '{} second', 103 | 'other': '{} seconds', 104 | }), 105 | 'test_empty_fallback_plurals': (locale.languageCode == 'fb' 106 | ? { 107 | 'zero': 'fallback zero', 108 | 'one': 'fallback one', 109 | 'two': 'fallback two', 110 | 'few': 'fallback few', 111 | 'many': 'fallback many', 112 | 'other': 'fallback other', 113 | } 114 | : { 115 | 'zero': '', 116 | 'one': '', 117 | 'two': '', 118 | 'few': '', 119 | 'many': '', 120 | 'other': '', 121 | }), 122 | 'test_empty_plurals': (locale.languageCode == 'fb' 123 | ? { 124 | 'zero': '', 125 | 'one': '', 126 | 'two': '', 127 | 'few': '', 128 | 'many': '', 129 | 'other': '', 130 | } 131 | : { 132 | 'zero': '', 133 | 'one': '', 134 | 'two': '', 135 | 'few': '', 136 | 'many': '', 137 | 'other': '', 138 | }) 139 | }); 140 | } 141 | } 142 | 143 | class ExternalAssetLoader extends AssetLoader { 144 | const ExternalAssetLoader(); 145 | 146 | @override 147 | Future> load(String fullPath, Locale locale) => 148 | Future.value(const { 149 | 'package_value_01': 'package_value_01', 150 | 'package_value_02': 'package_value_02', 151 | 'package_value_03': 'package_value_03', 152 | }); 153 | } 154 | 155 | class NestedAssetLoader extends AssetLoader { 156 | const NestedAssetLoader(); 157 | 158 | @override 159 | Future> load(String fullPath, Locale locale) => 160 | Future.value({ 161 | 'nested': { 162 | 'super': { 163 | 'duper': {'nested': 'nested.super.duper.nested'} 164 | } 165 | }, 166 | }); 167 | } 168 | --------------------------------------------------------------------------------