16 Commits

Author SHA1 Message Date
f553ecdab3 fix: notifications icon 2025-05-31 12:02:01 +01:00
b7764d82c9 fix: zap comments missing 2025-05-30 17:08:00 +01:00
1575c7dd09 fix: ios 2025-05-30 14:56:18 +01:00
3914768eeb chore: update translations 2025-05-30 14:17:39 +01:00
edfb5bb80d chore: bump version 2025-05-30 14:06:58 +01:00
33ad784e87 fix: missing stream hides live page 2025-05-30 14:03:28 +01:00
0422341bf8 chore: bump flutter version 2025-05-30 13:59:46 +01:00
8af62e0b32 chore: bump ndk 2025-05-30 13:53:40 +01:00
cd6c50f9bd feat: add notifications button to profile page 2025-05-30 13:51:07 +01:00
428771462d feat: live streaming
closes #43
2025-05-30 13:48:37 +01:00
917147605b feat: default ln address to zap.stream for new accounts 2025-05-29 14:31:13 +01:00
8e3a4cbd41 feat: wallet balance
closes #42
2025-05-29 12:32:10 +01:00
c66127cac2 feat: improve notifications 2025-05-29 11:03:15 +01:00
ab9fdd6b71 chore: bump version 2025-05-29 09:51:00 +01:00
99c163a51a fix: allow background player option 2025-05-28 16:40:34 +01:00
fb4821ffdd feat: background playback (wip)
https://github.com/ryanheise/audio_service/issues/1124
2025-05-28 16:28:24 +01:00
106 changed files with 3410 additions and 344 deletions

View File

@ -12,7 +12,7 @@ jobs:
uses: subosito/flutter-action@v2
with:
channel: stable
flutter-version: 3.29.3
flutter-version: 3.32.0
- run: flutter pub get
- run: flutter build appbundle
env:
@ -53,6 +53,6 @@ jobs:
uses: subosito/flutter-action@v2
with:
channel: stable
flutter-version: 3.29.3
flutter-version: 3.32.0
- run: flutter pub get
- run: flutter build ios --no-codesign

View File

@ -16,7 +16,7 @@ jobs:
uses: subosito/flutter-action@v2
with:
channel: stable
flutter-version: 3.29.3
flutter-version: 3.32.0
- run: flutter pub get
- name: Build apk
env:

View File

@ -1,5 +1,4 @@
import com.android.build.api.dsl.ApkSigningConfig
import com.android.build.api.dsl.SigningConfig
import org.jetbrains.kotlin.gradle.targets.js.toHex
import java.io.FileInputStream
import java.util.Base64
@ -8,11 +7,9 @@ import java.util.Properties
plugins {
id("com.android.application")
// START: FlutterFire Configuration
id("com.google.gms.google-services")
// END: FlutterFire Configuration
id("kotlin-android")
id("dev.flutter.flutter-gradle-plugin")
id("com.google.gms.google-services")
}
fun getKeystoreFile(base64String: String?, hash: String, fileName: String): File {

View File

@ -3,6 +3,5 @@
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INTERNET" />
</manifest>

View File

@ -1,52 +1,79 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<application
android:label="zap.stream"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
android:icon="@mipmap/ic_launcher"
android:label="zap.stream">
<activity
android:name=".MainActivity"
android:name="com.ryanheise.audioservice.AudioServiceActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:exported="true"
android:hardwareAccelerated="true"
android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" />
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" android:host="zap.stream" />
<data android:host="zap.stream" />
<data android:scheme="http" />
<data android:scheme="https" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="zswc" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<service
android:name="com.ryanheise.audioservice.AudioService"
android:exported="true"
android:foregroundServiceType="mediaPlayback"
android:permission="android.permission.FOREGROUND_SERVICE"
tools:ignore="Instantiatable">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
<receiver
android:name="com.ryanheise.audioservice.MediaButtonReceiver"
android:exported="true"
tools:ignore="Instantiatable">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and
@ -55,10 +82,11 @@
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
<action android:name="android.intent.action.PROCESS_TEXT" />
<data android:mimeType="text/plain" />
</intent>
</queries>
<meta-data
android:name="firebase_messaging_auto_init_enabled"
android:value="false" />

View File

@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="33"
android:viewportHeight="23"
android:tint="#FFFFFF">
<group android:scaleX="0.777027"
android:scaleY="0.5415643"
android:translateX="3.679054"
android:translateY="5.272011">
<path
android:pathData="M32.788,1.721C32.356,0.677 31.344,0 30.216,0H10.68C10.674,0 10.667,0 10.661,0C10.655,0 10.648,0 10.642,0C6.542,0 3.21,3.332 3.21,7.425C3.21,9.578 4.138,11.582 5.704,12.975L0.812,17.867C0.013,18.666 -0.225,19.858 0.207,20.896C0.638,21.94 1.65,22.617 2.778,22.617H22.314C22.32,22.617 22.327,22.617 22.34,22.617C22.346,22.617 22.353,22.617 22.366,22.617C26.458,22.617 29.791,19.285 29.791,15.192C29.791,13.039 28.862,11.035 27.296,9.642L32.188,4.75C32.981,3.951 33.22,2.759 32.788,1.721ZM2.714,19.858C2.694,19.813 2.707,19.8 2.733,19.775L8.109,14.399L22.391,19.452C22.404,19.459 22.424,19.465 22.437,19.465C22.456,19.472 22.475,19.478 22.494,19.491C22.527,19.51 22.552,19.542 22.572,19.581C22.578,19.588 22.578,19.594 22.585,19.601C22.591,19.613 22.591,19.626 22.591,19.639C22.591,19.652 22.598,19.665 22.598,19.678C22.598,19.794 22.469,19.897 22.327,19.897H2.785C2.753,19.91 2.733,19.91 2.714,19.858ZM25.208,18.956C25.208,18.95 25.201,18.937 25.201,18.93C25.176,18.827 25.143,18.73 25.105,18.634C25.098,18.621 25.098,18.608 25.092,18.595C25.053,18.492 25.002,18.395 24.95,18.299C24.944,18.286 24.931,18.266 24.924,18.253C24.815,18.06 24.686,17.88 24.538,17.712C24.525,17.699 24.512,17.686 24.499,17.673C24.422,17.596 24.344,17.519 24.26,17.448C24.248,17.441 24.235,17.428 24.228,17.422C24.151,17.358 24.067,17.299 23.977,17.242C23.964,17.235 23.951,17.222 23.938,17.216C23.848,17.158 23.751,17.106 23.655,17.055C23.635,17.042 23.61,17.035 23.59,17.022C23.493,16.977 23.39,16.932 23.281,16.9L16.674,14.56L9.037,11.86L9.011,11.853C9.004,11.853 9.004,11.853 8.998,11.847C8.811,11.776 8.624,11.692 8.437,11.595C6.884,10.777 5.924,9.178 5.924,7.425C5.924,5.891 6.658,4.525 7.799,3.661C7.799,3.667 7.806,3.68 7.806,3.687C7.831,3.783 7.864,3.88 7.902,3.977C7.915,4.009 7.928,4.041 7.941,4.074C7.973,4.144 8.005,4.209 8.038,4.28C8.051,4.312 8.07,4.344 8.083,4.37C8.128,4.454 8.179,4.531 8.237,4.608C8.263,4.641 8.283,4.673 8.308,4.705C8.353,4.763 8.399,4.821 8.45,4.873C8.469,4.899 8.489,4.924 8.515,4.944C8.579,5.015 8.656,5.079 8.727,5.143C8.753,5.169 8.779,5.189 8.811,5.208C8.882,5.266 8.953,5.317 9.03,5.369C9.043,5.382 9.056,5.388 9.075,5.401C9.166,5.459 9.262,5.511 9.359,5.556C9.385,5.569 9.411,5.582 9.436,5.595C9.539,5.64 9.643,5.685 9.746,5.717L24.009,10.764C24.016,10.764 24.022,10.77 24.022,10.77C24.209,10.841 24.389,10.919 24.563,11.015C26.117,11.834 27.077,13.432 27.077,15.185C27.084,16.726 26.342,18.092 25.208,18.956ZM30.267,2.836L24.892,8.211C24.879,8.205 24.86,8.199 24.847,8.192L10.622,3.165C10.603,3.158 10.584,3.152 10.571,3.145H10.564C10.545,3.139 10.532,3.132 10.513,3.12C10.506,3.113 10.5,3.107 10.493,3.1C10.48,3.094 10.474,3.081 10.467,3.074C10.461,3.068 10.455,3.055 10.455,3.049C10.448,3.036 10.442,3.023 10.435,3.01C10.435,3.004 10.429,2.991 10.429,2.978C10.429,2.965 10.422,2.946 10.422,2.926C10.422,2.913 10.429,2.907 10.429,2.894C10.448,2.797 10.564,2.714 10.687,2.714H30.229C30.261,2.714 30.28,2.714 30.3,2.759C30.306,2.797 30.293,2.817 30.267,2.836Z"
android:fillColor="#ffffff"/>
</group>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 531 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 702 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
assets/logo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

@ -1,4 +1,14 @@
PODS:
- apivideo_live_stream (0.0.1):
- ApiVideoLiveStream (= 1.4.1)
- Flutter
- ApiVideoLiveStream (1.4.1):
- HaishinKit (= 1.7.3)
- audio_service (0.0.1):
- Flutter
- FlutterMacOS
- audio_session (0.0.1):
- Flutter
- emoji_picker_flutter (0.0.1):
- Flutter
- Firebase/CoreOnly (11.10.0):
@ -65,17 +75,22 @@ PODS:
- GoogleUtilities/UserDefaults (8.1.0):
- GoogleUtilities/Logger
- GoogleUtilities/Privacy
- HaishinKit (1.7.3):
- Logboard (~> 2.4.1)
- image_picker_ios (0.0.1):
- Flutter
- Logboard (2.4.2)
- nanopb (3.30910.0):
- nanopb/decode (= 3.30910.0)
- nanopb/encode (= 3.30910.0)
- nanopb/decode (3.30910.0)
- nanopb/encode (3.30910.0)
- ObjectBox (4.2.0)
- native_device_orientation (0.0.1):
- Flutter
- ObjectBox (4.3.0)
- objectbox_flutter_libs (0.0.1):
- Flutter
- ObjectBox (= 4.2.0)
- ObjectBox (= 4.3.0)
- package_info_plus (0.4.5):
- Flutter
- path_provider_foundation (0.0.1):
@ -103,6 +118,9 @@ PODS:
- Flutter
DEPENDENCIES:
- apivideo_live_stream (from `.symlinks/plugins/apivideo_live_stream/ios`)
- audio_service (from `.symlinks/plugins/audio_service/darwin`)
- audio_session (from `.symlinks/plugins/audio_session/ios`)
- emoji_picker_flutter (from `.symlinks/plugins/emoji_picker_flutter/ios`)
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
@ -110,6 +128,7 @@ DEPENDENCIES:
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- native_device_orientation (from `.symlinks/plugins/native_device_orientation/ios`)
- objectbox_flutter_libs (from `.symlinks/plugins/objectbox_flutter_libs/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
@ -124,6 +143,7 @@ DEPENDENCIES:
SPEC REPOS:
trunk:
- ApiVideoLiveStream
- Firebase
- FirebaseCore
- FirebaseCoreInternal
@ -131,11 +151,19 @@ SPEC REPOS:
- FirebaseMessaging
- GoogleDataTransport
- GoogleUtilities
- HaishinKit
- Logboard
- nanopb
- ObjectBox
- PromisesObjC
EXTERNAL SOURCES:
apivideo_live_stream:
:path: ".symlinks/plugins/apivideo_live_stream/ios"
audio_service:
:path: ".symlinks/plugins/audio_service/darwin"
audio_session:
:path: ".symlinks/plugins/audio_session/ios"
emoji_picker_flutter:
:path: ".symlinks/plugins/emoji_picker_flutter/ios"
firebase_core:
@ -150,6 +178,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_secure_storage/ios"
image_picker_ios:
:path: ".symlinks/plugins/image_picker_ios/ios"
native_device_orientation:
:path: ".symlinks/plugins/native_device_orientation/ios"
objectbox_flutter_libs:
:path: ".symlinks/plugins/objectbox_flutter_libs/ios"
package_info_plus:
@ -174,6 +204,10 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/wakelock_plus/ios"
SPEC CHECKSUMS:
apivideo_live_stream: caab45dd35fb3b140d423c099d0f6672378e7abe
ApiVideoLiveStream: 8f9dce7f6d15d5e4bb3c7a25e406bf2a36337a5a
audio_service: aa99a6ba2ae7565996015322b0bb024e1d25c6fd
audio_session: 9bb7f6c970f21241b19f5a3658097ae459681ba0
emoji_picker_flutter: ece213fc274bdddefb77d502d33080dc54e616cc
Firebase: 1fe1c0a7d9aaea32efe01fbea5f0ebd8d70e53a2
firebase_core: ba71b44041571da878cb624ce0d80250bcbe58ad
@ -187,10 +221,13 @@ SPEC CHECKSUMS:
flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
HaishinKit: 326e27c4d06427ba53bffc68e516a92033293051
image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
Logboard: 759d82599c439945d430d5a0958455b5a1974a0c
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
ObjectBox: 2b4c925852ea99f070492f21328bf4002165cbd9
objectbox_flutter_libs: 50402e45f47f385d47fc041aba4607b89c4a3a1f
native_device_orientation: d6a4dc6887cd8a5ce1049962367aec60139ea0f1
ObjectBox: 7760fa9072adcffe102a6adf4646ca84ba70ab6b
objectbox_flutter_libs: 4014e38f57250b074b99893ae84579aba8f02876
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47

View File

@ -322,14 +322,10 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
@ -380,14 +376,10 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
inputPaths = (
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";

View File

@ -26,6 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
@ -54,6 +55,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 771 B

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 784 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -72,6 +72,7 @@
<array>
<string>fetch</string>
<string>remote-notification</string>
<string>audio</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
@ -90,5 +91,9 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>NSCameraUsageDescription</key>
<string>Live streaming</string>
<key>NSMicrophoneUsageDescription</key>
<string>Live streaming</string>
</dict>
</plist>

260
lib/api.dart Normal file
View File

@ -0,0 +1,260 @@
import 'dart:convert';
import 'dart:developer' as developer;
import 'package:convert/convert.dart';
import 'package:crypto/crypto.dart';
import 'package:http/http.dart' as http;
import 'package:ndk/ndk.dart';
import 'package:zap_stream_flutter/const.dart';
class IngestEndpoint {
final String name;
final String url;
final String key;
final IngestCost cost;
final List<String> capabilities;
const IngestEndpoint({
required this.name,
required this.url,
required this.key,
required this.cost,
required this.capabilities,
});
static IngestEndpoint fromJson(Map<String, dynamic> json) {
return IngestEndpoint(
name: json["name"],
url: json["url"],
key: json["key"],
cost: IngestCost.fromJson(json["cost"]),
capabilities: List<String>.from(json["capabilities"]),
);
}
@override
int get hashCode => name.hashCode;
@override
bool operator ==(Object other) {
if (other is IngestEndpoint) {
return other.name == name;
}
return false;
}
}
class IngestCost {
final String unit;
final double rate;
const IngestCost({required this.unit, required this.rate});
static IngestCost fromJson(Map<String, dynamic> json) {
return IngestCost(unit: json["unit"], rate: json["rate"]);
}
}
class TosAccepted {
final bool accepted;
final String? link;
const TosAccepted({required this.accepted, required this.link});
static TosAccepted fromJson(Map<String, dynamic> json) {
return TosAccepted(accepted: json["accepted"], link: json["link"]);
}
}
class AccountInfo {
final double balance;
final List<IngestEndpoint> endpoints;
final TosAccepted tos;
final EventInfo? details;
const AccountInfo({
required this.balance,
required this.endpoints,
required this.tos,
this.details,
});
static AccountInfo fromJson(Map<String, dynamic> json) {
final balance = json["balance"] as int;
final endpoints = json["endpoints"] as Iterable<dynamic>;
return AccountInfo(
balance: balance.toDouble(),
endpoints: endpoints.map((e) => IngestEndpoint.fromJson(e)).toList(),
tos: TosAccepted.fromJson(json["tos"]),
details:
json.containsKey("details")
? EventInfo.fromJson(json["details"])
: null,
);
}
}
class EventInfo {
final String? id;
final String? title;
final String? summary;
final String? image;
final String? contentWarning;
final String? goal;
final List<String>? tags;
EventInfo({
required this.id,
required this.title,
required this.summary,
required this.image,
required this.contentWarning,
required this.goal,
required this.tags,
});
static EventInfo fromJson(Map<String, dynamic> json) {
return EventInfo(
id: json["id"],
title: json["title"],
summary: json["summary"],
image: json["image"],
contentWarning: json["content_warning"],
goal: json["goal"],
tags: json.containsKey("tags") ? List<String>.from(json["tags"]) : null,
);
}
}
class ZapStreamApi {
final String base;
final EventSigner signer;
ZapStreamApi(this.base, this.signer);
static ZapStreamApi instance() {
return ZapStreamApi(apiUrl, ndk.accounts.getLoggedAccount()!.signer);
}
Future<AccountInfo> getAccountInfo() async {
final url = "$base/account";
final rsp = await _sendGetRequest(url);
return AccountInfo.fromJson(JsonCodec().decode(rsp.body));
}
Future<void> updateDefaultStreamInfo({
String? title,
String? summary,
String? image,
String? contentWarning,
String? goal,
List<String>? tags,
}) async {
final url = "$base/event";
await _sendPatchRequest(
url,
body: {
"title": title,
"summary": summary,
"image": image,
"content_warning": contentWarning,
"goal": goal,
"tags": tags,
},
);
}
Future<void> acceptTos() async {
await _sendPatchRequest("$base/account", body: {"accept_tos": true});
}
Future<http.Response> _sendPatchRequest(String url, {Object? body}) async {
final jsonBody = body != null ? JsonCodec().encode(body) : null;
final auth = await _makeAuth("PATCH", url, body: jsonBody);
final rsp = await http
.patch(
Uri.parse(url),
body: jsonBody,
headers: {
"authorization": "Nostr $auth",
"accept": "application/json",
"content-type": "application/json",
},
)
.timeout(Duration(seconds: 10));
developer.log(rsp.body);
return rsp;
}
Future<http.Response> _sendPutRequest(String url, {Object? body}) async {
final jsonBody = body != null ? JsonCodec().encode(body) : null;
final auth = await _makeAuth("PUT", url, body: jsonBody);
final rsp = await http
.put(
Uri.parse(url),
body: jsonBody,
headers: {
"authorization": "Nostr $auth",
"accept": "application/json",
"content-type": "application/json",
},
)
.timeout(Duration(seconds: 10));
developer.log(rsp.body);
return rsp;
}
Future<http.Response> _sendGetRequest(String url, {Object? body}) async {
final jsonBody = body != null ? JsonCodec().encode(body) : null;
final auth = await _makeAuth("GET", url, body: jsonBody);
final rsp = await http
.get(
Uri.parse(url),
headers: {
"authorization": "Nostr $auth",
"accept": "application/json",
"content-type": "application/json",
},
)
.timeout(Duration(seconds: 10));
developer.log(rsp.body);
return rsp;
}
Future<http.Response> _sendDeleteRequest(String url, {Object? body}) async {
final jsonBody = body != null ? JsonCodec().encode(body) : null;
final auth = await _makeAuth("DELETE", url, body: jsonBody);
final rsp = await http
.delete(
Uri.parse(url),
headers: {
"authorization": "Nostr $auth",
"accept": "application/json",
"content-type": "application/json",
},
)
.timeout(Duration(seconds: 10));
developer.log(rsp.body);
return rsp;
}
Future<String> _makeAuth(String method, String url, {String? body}) async {
final pubkey = signer.getPublicKey();
var tags = [
["u", url],
["method", method],
];
if (body != null) {
final hash = hex.encode(sha256.convert(utf8.encode(body)).bytes);
tags.add(["payload", hash]);
}
final authEvent = Nip01Event(
pubKey: pubkey,
kind: 27235,
tags: tags,
content: "",
);
await signer.sign(authEvent);
return authEvent.toBase64();
}
}

View File

@ -6,6 +6,7 @@ import 'package:zap_stream_flutter/i18n/strings.g.dart';
import 'package:zap_stream_flutter/pages/category.dart';
import 'package:zap_stream_flutter/pages/hashtag.dart';
import 'package:zap_stream_flutter/pages/home.dart';
import 'package:zap_stream_flutter/pages/live.dart';
import 'package:zap_stream_flutter/pages/login.dart';
import 'package:zap_stream_flutter/pages/login_input.dart';
import 'package:zap_stream_flutter/pages/new_account.dart';
@ -25,7 +26,11 @@ void runZapStream() {
supportedLocales: AppLocaleUtils.supportedLocales,
localizationsDelegates: GlobalMaterialLocalizations.delegates,
theme: ThemeData.localize(
ThemeData(colorScheme: ColorScheme.dark(), highlightColor: PRIMARY_1),
ThemeData(
colorScheme: ColorScheme.dark(),
highlightColor: PRIMARY_1,
useMaterial3: true,
),
TextTheme(),
),
routerConfig: GoRouter(
@ -131,6 +136,10 @@ void runZapStream() {
),
],
),
GoRoute(
path: "/live",
builder: (context, state) => LivePage(),
),
GoRoute(
path: "/:id",
redirect: (context, state) {

View File

@ -1,6 +1,7 @@
import 'package:amberflutter/amberflutter.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:ndk/ndk.dart';
import 'package:ndk_amber/ndk_amber.dart';
@ -17,7 +18,7 @@ class NoVerify extends EventVerifier {
final ndkCache = DbObjectBox();
final eventVerifier = kDebugMode ? NoVerify() : RustEventVerifier();
var ndk = Ndk(
final ndk = Ndk(
NdkConfig(
eventVerifier: eventVerifier,
cache: ndkCache,
@ -36,6 +37,7 @@ const defaultRelays = [
];
const searchRelays = ["wss://relay.nostr.band", "wss://search.nos.today"];
const nwcRelays = ["wss://relay.getalby.com/v1"];
final apiUrl = dotenv.env["API_URL"] ?? "https://api.zap.stream/api/nostr";
final loginData = LoginData();
final RouteObserver<ModalRoute<void>> routeObserver =

View File

@ -4,9 +4,9 @@
/// To regenerate, run: `dart run slang`
///
/// Locales: 22
/// Strings: 1628 (74 per locale)
/// Strings: 2010 (91 per locale)
///
/// Built on 2025-05-28 at 12:41 UTC
/// Built on 2025-05-30 at 13:17 UTC
// coverage:ignore-file
// ignore_for_file: type=lint, unused_import

View File

@ -51,10 +51,12 @@ class TranslationsAr extends Translations {
/// مستخدم مجهول
@override String get anon => 'هوية مخفية';
@override String full_amount_sats({required num n}) => '${NumberFormat.decimalPattern('ar').format(n)} دولار ساتس';
/// عدد مشاهدي البث
@override String viewers({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ar'))(n,
one: '1 مشاهد',
other: '${NumberFormat.decimalPattern('ar').format(n)} المشاهدين',
other: '{n:decimalPattern} المشاهدين',
);
@override late final _TranslationsStreamAr stream = _TranslationsStreamAr._(_root);
@ -69,6 +71,7 @@ class TranslationsAr extends Translations {
@override late final _TranslationsProfileAr profile = _TranslationsProfileAr._(_root);
@override late final _TranslationsSettingsAr settings = _TranslationsSettingsAr._(_root);
@override late final _TranslationsLoginAr login = _TranslationsLoginAr._(_root);
@override late final _TranslationsLiveAr live = _TranslationsLiveAr._(_root);
}
// Path: stream
@ -80,6 +83,7 @@ class _TranslationsStreamAr extends TranslationsStreamEn {
// Translations
@override late final _TranslationsStreamStatusAr status = _TranslationsStreamStatusAr._(_root);
@override String started({required Object timestamp}) => 'بدأ ${timestamp}';
@override String notification({required Object name}) => '${name} بدأ البث المباشر!';
@override late final _TranslationsStreamChatAr chat = _TranslationsStreamChatAr._(_root);
}
@ -204,6 +208,30 @@ class _TranslationsLoginAr extends TranslationsLoginEn {
@override late final _TranslationsLoginErrorAr error = _TranslationsLoginErrorAr._(_root);
}
// Path: live
class _TranslationsLiveAr extends TranslationsLiveEn {
_TranslationsLiveAr._(TranslationsAr root) : this._root = root, super.internal(root);
final TranslationsAr _root; // ignore: unused_field
// Translations
@override String get start => 'ابدأ البث المباشر';
@override String get configure_stream => 'تكوين الدفق';
@override String get endpoint => 'نقطة النهاية';
@override String get accept_tos => 'قبول شروط الخدمة';
@override String balance_left({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ar'))(n,
zero: '',
other: '~${time}',
);
@override String get title => 'العنوان';
@override String get summary => 'الملخص';
@override String get image => 'صورة الغلاف';
@override String get tags => 'الوسوم';
@override String get nsfw => 'محتوى غير لائق جنسيًا';
@override String get nsfw_description => 'تحقق هنا إذا كان هذا البث يحتوي على محتوى إباحي أو عري.';
@override late final _TranslationsLiveErrorAr error = _TranslationsLiveErrorAr._(_root);
}
// Path: stream.status
class _TranslationsStreamStatusAr extends TranslationsStreamStatusEn {
_TranslationsStreamStatusAr._(TranslationsAr root) : this._root = root, super.internal(root);
@ -288,6 +316,8 @@ class _TranslationsSettingsWalletAr extends TranslationsSettingsWalletEn {
@override String get disconnect_wallet => 'محفظة قطع الاتصال';
@override String get connect_1tap => '1-التوصيل بنقرة 1';
@override String get paste => 'لصق عنوان URL';
@override String get balance => 'الرصيد';
@override String get name => 'المحفظة';
@override late final _TranslationsSettingsWalletErrorAr error = _TranslationsSettingsWalletErrorAr._(_root);
}
@ -301,6 +331,18 @@ class _TranslationsLoginErrorAr extends TranslationsLoginErrorEn {
@override String get invalid_key => 'مفتاح غير صالح';
}
// Path: live.error
class _TranslationsLiveErrorAr extends TranslationsLiveErrorEn {
_TranslationsLiveErrorAr._(TranslationsAr root) : this._root = root, super.internal(root);
final TranslationsAr _root; // ignore: unused_field
// Translations
@override String get failed => 'فشل البث';
@override String get connection_error => 'خطأ في الاتصال';
@override String get start_failed => 'فشل بدء البث فشل، يرجى التحقق من رصيدك';
}
// Path: stream.chat.write
class _TranslationsStreamChatWriteAr extends TranslationsStreamChatWriteEn {
_TranslationsStreamChatWriteAr._(TranslationsAr root) : this._root = root, super.internal(root);
@ -379,14 +421,16 @@ extension on TranslationsAr {
case 'most_zapped_streamers': return 'معظم اللافتات التي يتم بثها';
case 'no_user_found': return 'لم يتم العثور على مستخدم';
case 'anon': return 'هوية مخفية';
case 'full_amount_sats': return ({required num n}) => '${NumberFormat.decimalPattern('ar').format(n)} دولار ساتس';
case 'viewers': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ar'))(n,
one: '1 مشاهد',
other: '${NumberFormat.decimalPattern('ar').format(n)} المشاهدين',
other: '{n:decimalPattern} المشاهدين',
);
case 'stream.status.live': return 'بث مباشر';
case 'stream.status.ended': return 'انتهى';
case 'stream.status.planned': return 'مخطط';
case 'stream.started': return ({required Object timestamp}) => 'بدأ ${timestamp}';
case 'stream.notification': return ({required Object name}) => '${name} بدأ البث المباشر!';
case 'stream.chat.disabled': return 'تم تعطيل الدردشة';
case 'stream.chat.disabled_timeout': return ({required Object time}) => 'تنتهي المهلة: ${time}';
case 'stream.chat.timeout': return ({required InlineSpan mod, required InlineSpan user, required InlineSpan time}) => TextSpan(children: [
@ -455,6 +499,8 @@ extension on TranslationsAr {
case 'settings.wallet.disconnect_wallet': return 'محفظة قطع الاتصال';
case 'settings.wallet.connect_1tap': return '1-التوصيل بنقرة 1';
case 'settings.wallet.paste': return 'لصق عنوان URL';
case 'settings.wallet.balance': return 'الرصيد';
case 'settings.wallet.name': return 'المحفظة';
case 'settings.wallet.error.logged_out': return 'لا يمكن الاتصال بالمحفظة عند تسجيل الخروج';
case 'settings.wallet.error.nwc_auth_event_not_found': return 'لم يتم العثور على حدث مصادقة المحفظة';
case 'login.username': return 'اسم المستخدم';
@ -462,6 +508,23 @@ extension on TranslationsAr {
case 'login.key': return 'تسجيل الدخول بالمفتاح';
case 'login.create': return 'إنشاء حساب';
case 'login.error.invalid_key': return 'مفتاح غير صالح';
case 'live.start': return 'ابدأ البث المباشر';
case 'live.configure_stream': return 'تكوين الدفق';
case 'live.endpoint': return 'نقطة النهاية';
case 'live.accept_tos': return 'قبول شروط الخدمة';
case 'live.balance_left': return ({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ar'))(n,
zero: '',
other: '~${time}',
);
case 'live.title': return 'العنوان';
case 'live.summary': return 'الملخص';
case 'live.image': return 'صورة الغلاف';
case 'live.tags': return 'الوسوم';
case 'live.nsfw': return 'محتوى غير لائق جنسيًا';
case 'live.nsfw_description': return 'تحقق هنا إذا كان هذا البث يحتوي على محتوى إباحي أو عري.';
case 'live.error.failed': return 'فشل البث';
case 'live.error.connection_error': return 'خطأ في الاتصال';
case 'live.error.start_failed': return 'فشل بدء البث فشل، يرجى التحقق من رصيدك';
default: return null;
}
}

View File

@ -51,6 +51,8 @@ class TranslationsCs extends Translations {
/// Anonymní uživatel
@override String get anon => 'Anon';
@override String full_amount_sats({required num n}) => '${NumberFormat.decimalPattern('cs').format(n)} sats';
/// Počet diváků streamu
@override String viewers({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('cs'))(n,
one: '1 divák',
@ -69,6 +71,7 @@ class TranslationsCs extends Translations {
@override late final _TranslationsProfileCs profile = _TranslationsProfileCs._(_root);
@override late final _TranslationsSettingsCs settings = _TranslationsSettingsCs._(_root);
@override late final _TranslationsLoginCs login = _TranslationsLoginCs._(_root);
@override late final _TranslationsLiveCs live = _TranslationsLiveCs._(_root);
}
// Path: stream
@ -80,6 +83,7 @@ class _TranslationsStreamCs extends TranslationsStreamEn {
// Translations
@override late final _TranslationsStreamStatusCs status = _TranslationsStreamStatusCs._(_root);
@override String started({required Object timestamp}) => 'Založeno ${timestamp}';
@override String notification({required Object name}) => '${name} byl spuštěn!';
@override late final _TranslationsStreamChatCs chat = _TranslationsStreamChatCs._(_root);
}
@ -204,6 +208,30 @@ class _TranslationsLoginCs extends TranslationsLoginEn {
@override late final _TranslationsLoginErrorCs error = _TranslationsLoginErrorCs._(_root);
}
// Path: live
class _TranslationsLiveCs extends TranslationsLiveEn {
_TranslationsLiveCs._(TranslationsCs root) : this._root = root, super.internal(root);
final TranslationsCs _root; // ignore: unused_field
// Translations
@override String get start => 'PŘEJÍT NA ŽIVOT';
@override String get configure_stream => 'Konfigurace streamu';
@override String get endpoint => 'Koncový bod';
@override String get accept_tos => 'Přijmout TOS';
@override String balance_left({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('cs'))(n,
zero: '',
other: '~${time}',
);
@override String get title => 'Název';
@override String get summary => 'Souhrn';
@override String get image => 'Obrázek na obálce';
@override String get tags => 'Štítky';
@override String get nsfw => 'Obsah NSFW';
@override String get nsfw_description => 'Zde zkontrolujte, zda tento stream obsahuje nahotu nebo pornografický obsah.';
@override late final _TranslationsLiveErrorCs error = _TranslationsLiveErrorCs._(_root);
}
// Path: stream.status
class _TranslationsStreamStatusCs extends TranslationsStreamStatusEn {
_TranslationsStreamStatusCs._(TranslationsCs root) : this._root = root, super.internal(root);
@ -288,6 +316,8 @@ class _TranslationsSettingsWalletCs extends TranslationsSettingsWalletEn {
@override String get disconnect_wallet => 'Odpojení peněženky';
@override String get connect_1tap => 'Připojení 1 kohoutku';
@override String get paste => 'Vložit adresu URL';
@override String get balance => 'Bilance';
@override String get name => 'Peněženka';
@override late final _TranslationsSettingsWalletErrorCs error = _TranslationsSettingsWalletErrorCs._(_root);
}
@ -301,6 +331,18 @@ class _TranslationsLoginErrorCs extends TranslationsLoginErrorEn {
@override String get invalid_key => 'Neplatný klíč';
}
// Path: live.error
class _TranslationsLiveErrorCs extends TranslationsLiveErrorEn {
_TranslationsLiveErrorCs._(TranslationsCs root) : this._root = root, super.internal(root);
final TranslationsCs _root; // ignore: unused_field
// Translations
@override String get failed => 'Stream se nezdařil';
@override String get connection_error => 'Chyba připojení';
@override String get start_failed => 'Spuštění streamu se nezdařilo, zkontrolujte prosím zůstatek';
}
// Path: stream.chat.write
class _TranslationsStreamChatWriteCs extends TranslationsStreamChatWriteEn {
_TranslationsStreamChatWriteCs._(TranslationsCs root) : this._root = root, super.internal(root);
@ -379,6 +421,7 @@ extension on TranslationsCs {
case 'most_zapped_streamers': return 'Nejvíce zapnutých streamerů';
case 'no_user_found': return 'Nebyl nalezen žádný uživatel';
case 'anon': return 'Anon';
case 'full_amount_sats': return ({required num n}) => '${NumberFormat.decimalPattern('cs').format(n)} sats';
case 'viewers': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('cs'))(n,
one: '1 divák',
other: '${NumberFormat.decimalPattern('cs').format(n)} diváků',
@ -387,6 +430,7 @@ extension on TranslationsCs {
case 'stream.status.ended': return 'KONEC';
case 'stream.status.planned': return 'PLÁNOVANÉ';
case 'stream.started': return ({required Object timestamp}) => 'Založeno ${timestamp}';
case 'stream.notification': return ({required Object name}) => '${name} byl spuštěn!';
case 'stream.chat.disabled': return 'CHAT ZRUŠEN';
case 'stream.chat.disabled_timeout': return ({required Object time}) => 'Časový limit vyprší: ${time}';
case 'stream.chat.timeout': return ({required InlineSpan mod, required InlineSpan user, required InlineSpan time}) => TextSpan(children: [
@ -455,6 +499,8 @@ extension on TranslationsCs {
case 'settings.wallet.disconnect_wallet': return 'Odpojení peněženky';
case 'settings.wallet.connect_1tap': return 'Připojení 1 kohoutku';
case 'settings.wallet.paste': return 'Vložit adresu URL';
case 'settings.wallet.balance': return 'Bilance';
case 'settings.wallet.name': return 'Peněženka';
case 'settings.wallet.error.logged_out': return 'Nelze se připojit k peněžence, když jste odhlášeni';
case 'settings.wallet.error.nwc_auth_event_not_found': return 'Nebyla nalezena žádná událost autentizace peněženky';
case 'login.username': return 'Uživatelské jméno';
@ -462,6 +508,23 @@ extension on TranslationsCs {
case 'login.key': return 'Přihlášení pomocí klíče';
case 'login.create': return 'Vytvořit účet';
case 'login.error.invalid_key': return 'Neplatný klíč';
case 'live.start': return 'PŘEJÍT NA ŽIVOT';
case 'live.configure_stream': return 'Konfigurace streamu';
case 'live.endpoint': return 'Koncový bod';
case 'live.accept_tos': return 'Přijmout TOS';
case 'live.balance_left': return ({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('cs'))(n,
zero: '',
other: '~${time}',
);
case 'live.title': return 'Název';
case 'live.summary': return 'Souhrn';
case 'live.image': return 'Obrázek na obálce';
case 'live.tags': return 'Štítky';
case 'live.nsfw': return 'Obsah NSFW';
case 'live.nsfw_description': return 'Zde zkontrolujte, zda tento stream obsahuje nahotu nebo pornografický obsah.';
case 'live.error.failed': return 'Stream se nezdařil';
case 'live.error.connection_error': return 'Chyba připojení';
case 'live.error.start_failed': return 'Spuštění streamu se nezdařilo, zkontrolujte prosím zůstatek';
default: return null;
}
}

View File

@ -51,6 +51,8 @@ class TranslationsDa extends Translations {
/// En anonym bruger
@override String get anon => 'Anon';
@override String full_amount_sats({required num n}) => '${NumberFormat.decimalPattern('da').format(n)} sats';
/// Antal seere af streamingen
@override String viewers({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('da'))(n,
one: '1 seer',
@ -69,6 +71,7 @@ class TranslationsDa extends Translations {
@override late final _TranslationsProfileDa profile = _TranslationsProfileDa._(_root);
@override late final _TranslationsSettingsDa settings = _TranslationsSettingsDa._(_root);
@override late final _TranslationsLoginDa login = _TranslationsLoginDa._(_root);
@override late final _TranslationsLiveDa live = _TranslationsLiveDa._(_root);
}
// Path: stream
@ -80,6 +83,7 @@ class _TranslationsStreamDa extends TranslationsStreamEn {
// Translations
@override late final _TranslationsStreamStatusDa status = _TranslationsStreamStatusDa._(_root);
@override String started({required Object timestamp}) => 'Startet ${timestamp}';
@override String notification({required Object name}) => '${name} gik live!';
@override late final _TranslationsStreamChatDa chat = _TranslationsStreamChatDa._(_root);
}
@ -204,6 +208,30 @@ class _TranslationsLoginDa extends TranslationsLoginEn {
@override late final _TranslationsLoginErrorDa error = _TranslationsLoginErrorDa._(_root);
}
// Path: live
class _TranslationsLiveDa extends TranslationsLiveEn {
_TranslationsLiveDa._(TranslationsDa root) : this._root = root, super.internal(root);
final TranslationsDa _root; // ignore: unused_field
// Translations
@override String get start => 'GO LIVE';
@override String get configure_stream => 'Konfigurer stream';
@override String get endpoint => 'Slutpunkt';
@override String get accept_tos => 'Accepter TOS';
@override String balance_left({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('da'))(n,
zero: '',
other: '~${time}',
);
@override String get title => 'Titel';
@override String get summary => 'Sammenfatning';
@override String get image => 'Forsidebillede';
@override String get tags => 'Tags';
@override String get nsfw => 'NSFW-indhold';
@override String get nsfw_description => 'Tjek her, om denne stream indeholder nøgenhed eller pornografisk indhold.';
@override late final _TranslationsLiveErrorDa error = _TranslationsLiveErrorDa._(_root);
}
// Path: stream.status
class _TranslationsStreamStatusDa extends TranslationsStreamStatusEn {
_TranslationsStreamStatusDa._(TranslationsDa root) : this._root = root, super.internal(root);
@ -288,6 +316,8 @@ class _TranslationsSettingsWalletDa extends TranslationsSettingsWalletEn {
@override String get disconnect_wallet => 'Afbryd forbindelsen til tegnebogen';
@override String get connect_1tap => '1-Tap-forbindelse';
@override String get paste => 'Indsæt URL';
@override String get balance => 'Balance';
@override String get name => 'Tegnebog';
@override late final _TranslationsSettingsWalletErrorDa error = _TranslationsSettingsWalletErrorDa._(_root);
}
@ -301,6 +331,18 @@ class _TranslationsLoginErrorDa extends TranslationsLoginErrorEn {
@override String get invalid_key => 'Ugyldig nøgle';
}
// Path: live.error
class _TranslationsLiveErrorDa extends TranslationsLiveErrorEn {
_TranslationsLiveErrorDa._(TranslationsDa root) : this._root = root, super.internal(root);
final TranslationsDa _root; // ignore: unused_field
// Translations
@override String get failed => 'Strømmen mislykkedes';
@override String get connection_error => 'Forbindelsesfejl';
@override String get start_failed => 'Stream-start mislykkedes, tjek venligst din saldo';
}
// Path: stream.chat.write
class _TranslationsStreamChatWriteDa extends TranslationsStreamChatWriteEn {
_TranslationsStreamChatWriteDa._(TranslationsDa root) : this._root = root, super.internal(root);
@ -379,6 +421,7 @@ extension on TranslationsDa {
case 'most_zapped_streamers': return 'De fleste zappede streamere';
case 'no_user_found': return 'Ingen bruger fundet';
case 'anon': return 'Anon';
case 'full_amount_sats': return ({required num n}) => '${NumberFormat.decimalPattern('da').format(n)} sats';
case 'viewers': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('da'))(n,
one: '1 seer',
other: '${NumberFormat.decimalPattern('da').format(n)} seere',
@ -387,6 +430,7 @@ extension on TranslationsDa {
case 'stream.status.ended': return 'AFSLUTTET';
case 'stream.status.planned': return 'PLANLAGT';
case 'stream.started': return ({required Object timestamp}) => 'Startet ${timestamp}';
case 'stream.notification': return ({required Object name}) => '${name} gik live!';
case 'stream.chat.disabled': return 'CHAT DEAKTIVERET';
case 'stream.chat.disabled_timeout': return ({required Object time}) => 'Timeout udløber: ${time}';
case 'stream.chat.timeout': return ({required InlineSpan mod, required InlineSpan user, required InlineSpan time}) => TextSpan(children: [
@ -455,6 +499,8 @@ extension on TranslationsDa {
case 'settings.wallet.disconnect_wallet': return 'Afbryd forbindelsen til tegnebogen';
case 'settings.wallet.connect_1tap': return '1-Tap-forbindelse';
case 'settings.wallet.paste': return 'Indsæt URL';
case 'settings.wallet.balance': return 'Balance';
case 'settings.wallet.name': return 'Tegnebog';
case 'settings.wallet.error.logged_out': return 'Kan ikke oprette forbindelse til wallet, når jeg er logget ud';
case 'settings.wallet.error.nwc_auth_event_not_found': return 'Ingen wallet-auth-begivenhed fundet';
case 'login.username': return 'Brugernavn';
@ -462,6 +508,23 @@ extension on TranslationsDa {
case 'login.key': return 'Login med nøgle';
case 'login.create': return 'Opret konto';
case 'login.error.invalid_key': return 'Ugyldig nøgle';
case 'live.start': return 'GO LIVE';
case 'live.configure_stream': return 'Konfigurer stream';
case 'live.endpoint': return 'Slutpunkt';
case 'live.accept_tos': return 'Accepter TOS';
case 'live.balance_left': return ({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('da'))(n,
zero: '',
other: '~${time}',
);
case 'live.title': return 'Titel';
case 'live.summary': return 'Sammenfatning';
case 'live.image': return 'Forsidebillede';
case 'live.tags': return 'Tags';
case 'live.nsfw': return 'NSFW-indhold';
case 'live.nsfw_description': return 'Tjek her, om denne stream indeholder nøgenhed eller pornografisk indhold.';
case 'live.error.failed': return 'Strømmen mislykkedes';
case 'live.error.connection_error': return 'Forbindelsesfejl';
case 'live.error.start_failed': return 'Stream-start mislykkedes, tjek venligst din saldo';
default: return null;
}
}

View File

@ -42,7 +42,7 @@ class TranslationsDe extends Translations {
/// Text, der den Benutzer auffordert, auf den Avatar-Platzhalter zu klicken, um den Upload zu starten
@override String get upload_avatar => 'Avatar hochladen';
/// Überschrift über gelistete Top-Streamer von zaps
/// Überschrift über gelistete Top-Streamer nach Zaps
@override String get most_zapped_streamers => 'Meistgezappte Streamer';
/// Kein Benutzer bei der Suche gefunden
@ -51,7 +51,9 @@ class TranslationsDe extends Translations {
/// Ein anonymer Benutzer
@override String get anon => 'Anon';
/// Anzahl der Betrachter des Streams
@override String full_amount_sats({required num n}) => '${NumberFormat.decimalPattern('de').format(n)} sats';
/// Anzahl der Zuschauer des Streams
@override String viewers({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('de'))(n,
one: '1 Zuschauer',
other: '${NumberFormat.decimalPattern('de').format(n)} Zuschauer',
@ -69,6 +71,7 @@ class TranslationsDe extends Translations {
@override late final _TranslationsProfileDe profile = _TranslationsProfileDe._(_root);
@override late final _TranslationsSettingsDe settings = _TranslationsSettingsDe._(_root);
@override late final _TranslationsLoginDe login = _TranslationsLoginDe._(_root);
@override late final _TranslationsLiveDe live = _TranslationsLiveDe._(_root);
}
// Path: stream
@ -80,6 +83,7 @@ class _TranslationsStreamDe extends TranslationsStreamEn {
// Translations
@override late final _TranslationsStreamStatusDe status = _TranslationsStreamStatusDe._(_root);
@override String started({required Object timestamp}) => 'Gestartet ${timestamp}';
@override String notification({required Object name}) => '${name} ging live!';
@override late final _TranslationsStreamChatDe chat = _TranslationsStreamChatDe._(_root);
}
@ -204,6 +208,30 @@ class _TranslationsLoginDe extends TranslationsLoginEn {
@override late final _TranslationsLoginErrorDe error = _TranslationsLoginErrorDe._(_root);
}
// Path: live
class _TranslationsLiveDe extends TranslationsLiveEn {
_TranslationsLiveDe._(TranslationsDe root) : this._root = root, super.internal(root);
final TranslationsDe _root; // ignore: unused_field
// Translations
@override String get start => 'LIVE GEHEN';
@override String get configure_stream => 'Stream konfigurieren';
@override String get endpoint => 'Endpunkt';
@override String get accept_tos => 'TOS akzeptieren';
@override String balance_left({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('de'))(n,
zero: '',
other: '~${time}',
);
@override String get title => 'Titel';
@override String get summary => 'Zusammenfassung';
@override String get image => 'Titelbild';
@override String get tags => 'Tags';
@override String get nsfw => 'NSFW-Inhalt';
@override String get nsfw_description => 'Prüfen Sie hier, ob dieser Stream Nacktheit oder pornografische Inhalte enthält.';
@override late final _TranslationsLiveErrorDe error = _TranslationsLiveErrorDe._(_root);
}
// Path: stream.status
class _TranslationsStreamStatusDe extends TranslationsStreamStatusEn {
_TranslationsStreamStatusDe._(TranslationsDe root) : this._root = root, super.internal(root);
@ -212,7 +240,7 @@ class _TranslationsStreamStatusDe extends TranslationsStreamStatusEn {
// Translations
@override String get live => 'LIVE';
@override String get ended => 'ENDED';
@override String get ended => 'BEENDET';
@override String get planned => 'GEPLANT';
}
@ -224,21 +252,21 @@ class _TranslationsStreamChatDe extends TranslationsStreamChatEn {
// Translations
@override String get disabled => 'CHAT DEAKTIVIERT';
@override String disabled_timeout({required Object time}) => 'Die Zeitüberschreitung läuft ab: ${time}';
@override String disabled_timeout({required Object time}) => 'Timeout läuft ab: ${time}';
/// Chat-Nachricht mit Zeitüberschreitungsereignissen
/// Chat-Nachricht mit Timeout-Ereignissen
@override TextSpan timeout({required InlineSpan mod, required InlineSpan user, required InlineSpan time}) => TextSpan(children: [
mod,
const TextSpan(text: ' Zeitüberschreitung '),
const TextSpan(text: ' gibt '),
user,
const TextSpan(text: ' für '),
const TextSpan(text: ' einen Timeout für '),
time,
]);
/// Stream beendet Fußzeile am Ende des Chats
@override String get ended => 'STREAM BEENDET';
/// Chatnachricht mit Stream Zaps
/// Chat-Nachricht mit Stream-Zaps
@override TextSpan zap({required InlineSpan user, required InlineSpan amount}) => TextSpan(children: [
user,
const TextSpan(text: ' hat '),
@ -288,6 +316,8 @@ class _TranslationsSettingsWalletDe extends TranslationsSettingsWalletEn {
@override String get disconnect_wallet => 'Brieftasche abtrennen';
@override String get connect_1tap => '1-Tap-Verbindung';
@override String get paste => 'URL einfügen';
@override String get balance => 'Bilanz';
@override String get name => 'Brieftasche';
@override late final _TranslationsSettingsWalletErrorDe error = _TranslationsSettingsWalletErrorDe._(_root);
}
@ -301,6 +331,18 @@ class _TranslationsLoginErrorDe extends TranslationsLoginErrorEn {
@override String get invalid_key => 'Ungültiger Schlüssel';
}
// Path: live.error
class _TranslationsLiveErrorDe extends TranslationsLiveErrorEn {
_TranslationsLiveErrorDe._(TranslationsDe root) : this._root = root, super.internal(root);
final TranslationsDe _root; // ignore: unused_field
// Translations
@override String get failed => 'Stream fehlgeschlagen';
@override String get connection_error => 'Verbindungsfehler';
@override String get start_failed => 'Streamstart fehlgeschlagen, bitte überprüfen Sie Ihr Guthaben';
}
// Path: stream.chat.write
class _TranslationsStreamChatWriteDe extends TranslationsStreamChatWriteEn {
_TranslationsStreamChatWriteDe._(TranslationsDe root) : this._root = root, super.internal(root);
@ -379,21 +421,23 @@ extension on TranslationsDe {
case 'most_zapped_streamers': return 'Meistgezappte Streamer';
case 'no_user_found': return 'Kein Benutzer gefunden';
case 'anon': return 'Anon';
case 'full_amount_sats': return ({required num n}) => '${NumberFormat.decimalPattern('de').format(n)} sats';
case 'viewers': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('de'))(n,
one: '1 Zuschauer',
other: '${NumberFormat.decimalPattern('de').format(n)} Zuschauer',
);
case 'stream.status.live': return 'LIVE';
case 'stream.status.ended': return 'ENDED';
case 'stream.status.ended': return 'BEENDET';
case 'stream.status.planned': return 'GEPLANT';
case 'stream.started': return ({required Object timestamp}) => 'Gestartet ${timestamp}';
case 'stream.notification': return ({required Object name}) => '${name} ging live!';
case 'stream.chat.disabled': return 'CHAT DEAKTIVIERT';
case 'stream.chat.disabled_timeout': return ({required Object time}) => 'Die Zeitüberschreitung läuft ab: ${time}';
case 'stream.chat.disabled_timeout': return ({required Object time}) => 'Timeout läuft ab: ${time}';
case 'stream.chat.timeout': return ({required InlineSpan mod, required InlineSpan user, required InlineSpan time}) => TextSpan(children: [
mod,
const TextSpan(text: ' Zeitüberschreitung '),
const TextSpan(text: ' gibt '),
user,
const TextSpan(text: ' für '),
const TextSpan(text: ' einen Timeout für '),
time,
]);
case 'stream.chat.ended': return 'STREAM BEENDET';
@ -455,6 +499,8 @@ extension on TranslationsDe {
case 'settings.wallet.disconnect_wallet': return 'Brieftasche abtrennen';
case 'settings.wallet.connect_1tap': return '1-Tap-Verbindung';
case 'settings.wallet.paste': return 'URL einfügen';
case 'settings.wallet.balance': return 'Bilanz';
case 'settings.wallet.name': return 'Brieftasche';
case 'settings.wallet.error.logged_out': return 'Kann keine Verbindung zur Brieftasche herstellen, wenn ich abgemeldet bin';
case 'settings.wallet.error.nwc_auth_event_not_found': return 'Kein Wallet-Authentifizierungsereignis gefunden';
case 'login.username': return 'Benutzername';
@ -462,6 +508,23 @@ extension on TranslationsDe {
case 'login.key': return 'Anmeldung mit Schlüssel';
case 'login.create': return 'Konto erstellen';
case 'login.error.invalid_key': return 'Ungültiger Schlüssel';
case 'live.start': return 'LIVE GEHEN';
case 'live.configure_stream': return 'Stream konfigurieren';
case 'live.endpoint': return 'Endpunkt';
case 'live.accept_tos': return 'TOS akzeptieren';
case 'live.balance_left': return ({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('de'))(n,
zero: '',
other: '~${time}',
);
case 'live.title': return 'Titel';
case 'live.summary': return 'Zusammenfassung';
case 'live.image': return 'Titelbild';
case 'live.tags': return 'Tags';
case 'live.nsfw': return 'NSFW-Inhalt';
case 'live.nsfw_description': return 'Prüfen Sie hier, ob dieser Stream Nacktheit oder pornografische Inhalte enthält.';
case 'live.error.failed': return 'Stream fehlgeschlagen';
case 'live.error.connection_error': return 'Verbindungsfehler';
case 'live.error.start_failed': return 'Streamstart fehlgeschlagen, bitte überprüfen Sie Ihr Guthaben';
default: return null;
}
}

View File

@ -51,6 +51,8 @@ class TranslationsEl extends Translations {
/// Ένας ανώνυμος χρήστης
@override String get anon => 'Anon';
@override String full_amount_sats({required num n}) => '${NumberFormat.decimalPattern('el').format(n)} sats';
/// Αριθμός θεατών της ροής
@override String viewers({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('el'))(n,
one: '1 θεατής',
@ -69,6 +71,7 @@ class TranslationsEl extends Translations {
@override late final _TranslationsProfileEl profile = _TranslationsProfileEl._(_root);
@override late final _TranslationsSettingsEl settings = _TranslationsSettingsEl._(_root);
@override late final _TranslationsLoginEl login = _TranslationsLoginEl._(_root);
@override late final _TranslationsLiveEl live = _TranslationsLiveEl._(_root);
}
// Path: stream
@ -80,6 +83,7 @@ class _TranslationsStreamEl extends TranslationsStreamEn {
// Translations
@override late final _TranslationsStreamStatusEl status = _TranslationsStreamStatusEl._(_root);
@override String started({required Object timestamp}) => 'Ξεκίνησε ${timestamp}';
@override String notification({required Object name}) => '${name} βγήκε ζωντανά!';
@override late final _TranslationsStreamChatEl chat = _TranslationsStreamChatEl._(_root);
}
@ -204,6 +208,30 @@ class _TranslationsLoginEl extends TranslationsLoginEn {
@override late final _TranslationsLoginErrorEl error = _TranslationsLoginErrorEl._(_root);
}
// Path: live
class _TranslationsLiveEl extends TranslationsLiveEn {
_TranslationsLiveEl._(TranslationsEl root) : this._root = root, super.internal(root);
final TranslationsEl _root; // ignore: unused_field
// Translations
@override String get start => 'GO LIVE';
@override String get configure_stream => 'Διαμόρφωση ροής';
@override String get endpoint => 'Τελικό σημείο';
@override String get accept_tos => 'Αποδοχή TOS';
@override String balance_left({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('el'))(n,
zero: '',
other: '~${time}',
);
@override String get title => 'Τίτλος';
@override String get summary => 'Περίληψη';
@override String get image => 'Εικόνα εξωφύλλου';
@override String get tags => 'Ετικέτες';
@override String get nsfw => 'Περιεχόμενο NSFW';
@override String get nsfw_description => 'Ελέγξτε εδώ αν αυτή η ροή περιέχει γυμνό ή πορνογραφικό περιεχόμενο.';
@override late final _TranslationsLiveErrorEl error = _TranslationsLiveErrorEl._(_root);
}
// Path: stream.status
class _TranslationsStreamStatusEl extends TranslationsStreamStatusEn {
_TranslationsStreamStatusEl._(TranslationsEl root) : this._root = root, super.internal(root);
@ -288,6 +316,8 @@ class _TranslationsSettingsWalletEl extends TranslationsSettingsWalletEn {
@override String get disconnect_wallet => 'Αποσύνδεση πορτοφολιού';
@override String get connect_1tap => 'Σύνδεση 1 βρύσης';
@override String get paste => 'Επικόλληση URL';
@override String get balance => 'Υπόλοιπο';
@override String get name => 'Πορτοφόλι';
@override late final _TranslationsSettingsWalletErrorEl error = _TranslationsSettingsWalletErrorEl._(_root);
}
@ -301,6 +331,18 @@ class _TranslationsLoginErrorEl extends TranslationsLoginErrorEn {
@override String get invalid_key => 'Μη έγκυρο κλειδί';
}
// Path: live.error
class _TranslationsLiveErrorEl extends TranslationsLiveErrorEn {
_TranslationsLiveErrorEl._(TranslationsEl root) : this._root = root, super.internal(root);
final TranslationsEl _root; // ignore: unused_field
// Translations
@override String get failed => 'Το ρεύμα απέτυχε';
@override String get connection_error => 'Σφάλμα σύνδεσης';
@override String get start_failed => 'Η εκκίνηση της ροής απέτυχε, παρακαλούμε ελέγξτε το υπόλοιπό σας';
}
// Path: stream.chat.write
class _TranslationsStreamChatWriteEl extends TranslationsStreamChatWriteEn {
_TranslationsStreamChatWriteEl._(TranslationsEl root) : this._root = root, super.internal(root);
@ -379,6 +421,7 @@ extension on TranslationsEl {
case 'most_zapped_streamers': return 'Τα περισσότερα Zapped Streamers';
case 'no_user_found': return 'Δεν βρέθηκε χρήστης';
case 'anon': return 'Anon';
case 'full_amount_sats': return ({required num n}) => '${NumberFormat.decimalPattern('el').format(n)} sats';
case 'viewers': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('el'))(n,
one: '1 θεατής',
other: '${NumberFormat.decimalPattern('el').format(n)} θεατές',
@ -387,6 +430,7 @@ extension on TranslationsEl {
case 'stream.status.ended': return 'ENDED';
case 'stream.status.planned': return 'ΣΧΕΔΙΑΣΜΟΣ';
case 'stream.started': return ({required Object timestamp}) => 'Ξεκίνησε ${timestamp}';
case 'stream.notification': return ({required Object name}) => '${name} βγήκε ζωντανά!';
case 'stream.chat.disabled': return 'ΑΠΕΝΕΡΓΟΠΟΙΗΜΈΝΗ ΣΥΝΟΜΙΛΊΑ';
case 'stream.chat.disabled_timeout': return ({required Object time}) => 'Το χρονικό όριο λήγει: ${time}';
case 'stream.chat.timeout': return ({required InlineSpan mod, required InlineSpan user, required InlineSpan time}) => TextSpan(children: [
@ -455,6 +499,8 @@ extension on TranslationsEl {
case 'settings.wallet.disconnect_wallet': return 'Αποσύνδεση πορτοφολιού';
case 'settings.wallet.connect_1tap': return 'Σύνδεση 1 βρύσης';
case 'settings.wallet.paste': return 'Επικόλληση URL';
case 'settings.wallet.balance': return 'Υπόλοιπο';
case 'settings.wallet.name': return 'Πορτοφόλι';
case 'settings.wallet.error.logged_out': return 'Δεν μπορώ να συνδεθώ με πορτοφόλι όταν έχω αποσυνδεθεί';
case 'settings.wallet.error.nwc_auth_event_not_found': return 'Δεν βρέθηκε συμβάν εξουσιοδότησης πορτοφολιού';
case 'login.username': return 'Όνομα χρήστη';
@ -462,6 +508,23 @@ extension on TranslationsEl {
case 'login.key': return 'Σύνδεση με κλειδί';
case 'login.create': return 'Δημιουργία λογαριασμού';
case 'login.error.invalid_key': return 'Μη έγκυρο κλειδί';
case 'live.start': return 'GO LIVE';
case 'live.configure_stream': return 'Διαμόρφωση ροής';
case 'live.endpoint': return 'Τελικό σημείο';
case 'live.accept_tos': return 'Αποδοχή TOS';
case 'live.balance_left': return ({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('el'))(n,
zero: '',
other: '~${time}',
);
case 'live.title': return 'Τίτλος';
case 'live.summary': return 'Περίληψη';
case 'live.image': return 'Εικόνα εξωφύλλου';
case 'live.tags': return 'Ετικέτες';
case 'live.nsfw': return 'Περιεχόμενο NSFW';
case 'live.nsfw_description': return 'Ελέγξτε εδώ αν αυτή η ροή περιέχει γυμνό ή πορνογραφικό περιεχόμενο.';
case 'live.error.failed': return 'Το ρεύμα απέτυχε';
case 'live.error.connection_error': return 'Σφάλμα σύνδεσης';
case 'live.error.start_failed': return 'Η εκκίνηση της ροής απέτυχε, παρακαλούμε ελέγξτε το υπόλοιπό σας';
default: return null;
}
}

View File

@ -52,6 +52,8 @@ class Translations implements BaseTranslations<AppLocale, Translations> {
/// An anonymous user
String get anon => 'Anon';
String full_amount_sats({required num n}) => '${NumberFormat.decimalPattern('en').format(n)} sats';
/// Number of viewers of the stream
String viewers({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('en'))(n,
one: '1 viewer',
@ -70,6 +72,7 @@ class Translations implements BaseTranslations<AppLocale, Translations> {
late final TranslationsProfileEn profile = TranslationsProfileEn.internal(_root);
late final TranslationsSettingsEn settings = TranslationsSettingsEn.internal(_root);
late final TranslationsLoginEn login = TranslationsLoginEn.internal(_root);
late final TranslationsLiveEn live = TranslationsLiveEn.internal(_root);
}
// Path: stream
@ -81,6 +84,7 @@ class TranslationsStreamEn {
// Translations
late final TranslationsStreamStatusEn status = TranslationsStreamStatusEn.internal(_root);
String started({required Object timestamp}) => 'Started ${timestamp}';
String notification({required Object name}) => '${name} went live!';
late final TranslationsStreamChatEn chat = TranslationsStreamChatEn.internal(_root);
}
@ -205,6 +209,30 @@ class TranslationsLoginEn {
late final TranslationsLoginErrorEn error = TranslationsLoginErrorEn.internal(_root);
}
// Path: live
class TranslationsLiveEn {
TranslationsLiveEn.internal(this._root);
final Translations _root; // ignore: unused_field
// Translations
String get start => 'GO LIVE';
String get configure_stream => 'Configure Stream';
String get endpoint => 'Endpoint';
String get accept_tos => 'Accept TOS';
String balance_left({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('en'))(n,
zero: '',
other: '~${time}',
);
String get title => 'Title';
String get summary => 'Summary';
String get image => 'Cover Image';
String get tags => 'Tags';
String get nsfw => 'NSFW Content';
String get nsfw_description => 'Check here if this stream contains nudity or pornographic content.';
late final TranslationsLiveErrorEn error = TranslationsLiveErrorEn.internal(_root);
}
// Path: stream.status
class TranslationsStreamStatusEn {
TranslationsStreamStatusEn.internal(this._root);
@ -289,6 +317,8 @@ class TranslationsSettingsWalletEn {
String get disconnect_wallet => 'Disconnect Wallet';
String get connect_1tap => '1-Tap Connection';
String get paste => 'Paste URL';
String get balance => 'Balance';
String get name => 'Wallet';
late final TranslationsSettingsWalletErrorEn error = TranslationsSettingsWalletErrorEn.internal(_root);
}
@ -302,6 +332,18 @@ class TranslationsLoginErrorEn {
String get invalid_key => 'Invalid key';
}
// Path: live.error
class TranslationsLiveErrorEn {
TranslationsLiveErrorEn.internal(this._root);
final Translations _root; // ignore: unused_field
// Translations
String get failed => 'Stream failed';
String get connection_error => 'Connection Error';
String get start_failed => 'Stream start failed, please check your balance';
}
// Path: stream.chat.write
class TranslationsStreamChatWriteEn {
TranslationsStreamChatWriteEn.internal(this._root);
@ -380,6 +422,7 @@ extension on Translations {
case 'most_zapped_streamers': return 'Most Zapped Streamers';
case 'no_user_found': return 'No user found';
case 'anon': return 'Anon';
case 'full_amount_sats': return ({required num n}) => '${NumberFormat.decimalPattern('en').format(n)} sats';
case 'viewers': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('en'))(n,
one: '1 viewer',
other: '${NumberFormat.decimalPattern('en').format(n)} viewers',
@ -388,6 +431,7 @@ extension on Translations {
case 'stream.status.ended': return 'ENDED';
case 'stream.status.planned': return 'PLANNED';
case 'stream.started': return ({required Object timestamp}) => 'Started ${timestamp}';
case 'stream.notification': return ({required Object name}) => '${name} went live!';
case 'stream.chat.disabled': return 'CHAT DISABLED';
case 'stream.chat.disabled_timeout': return ({required Object time}) => 'Timeout expires: ${time}';
case 'stream.chat.timeout': return ({required InlineSpan mod, required InlineSpan user, required InlineSpan time}) => TextSpan(children: [
@ -456,6 +500,8 @@ extension on Translations {
case 'settings.wallet.disconnect_wallet': return 'Disconnect Wallet';
case 'settings.wallet.connect_1tap': return '1-Tap Connection';
case 'settings.wallet.paste': return 'Paste URL';
case 'settings.wallet.balance': return 'Balance';
case 'settings.wallet.name': return 'Wallet';
case 'settings.wallet.error.logged_out': return 'Cant connect wallet when logged out';
case 'settings.wallet.error.nwc_auth_event_not_found': return 'No wallet auth event found';
case 'login.username': return 'Username';
@ -463,6 +509,23 @@ extension on Translations {
case 'login.key': return 'Login with Key';
case 'login.create': return 'Create Account';
case 'login.error.invalid_key': return 'Invalid key';
case 'live.start': return 'GO LIVE';
case 'live.configure_stream': return 'Configure Stream';
case 'live.endpoint': return 'Endpoint';
case 'live.accept_tos': return 'Accept TOS';
case 'live.balance_left': return ({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('en'))(n,
zero: '',
other: '~${time}',
);
case 'live.title': return 'Title';
case 'live.summary': return 'Summary';
case 'live.image': return 'Cover Image';
case 'live.tags': return 'Tags';
case 'live.nsfw': return 'NSFW Content';
case 'live.nsfw_description': return 'Check here if this stream contains nudity or pornographic content.';
case 'live.error.failed': return 'Stream failed';
case 'live.error.connection_error': return 'Connection Error';
case 'live.error.start_failed': return 'Stream start failed, please check your balance';
default: return null;
}
}

View File

@ -51,6 +51,8 @@ class TranslationsEs extends Translations {
/// Un usuario anónimo
@override String get anon => 'Anónimo';
@override String full_amount_sats({required num n}) => '${NumberFormat.decimalPattern('es').format(n)} sats';
/// Número de espectadores del flujo
@override String viewers({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('es'))(n,
one: '1 espectador',
@ -69,6 +71,7 @@ class TranslationsEs extends Translations {
@override late final _TranslationsProfileEs profile = _TranslationsProfileEs._(_root);
@override late final _TranslationsSettingsEs settings = _TranslationsSettingsEs._(_root);
@override late final _TranslationsLoginEs login = _TranslationsLoginEs._(_root);
@override late final _TranslationsLiveEs live = _TranslationsLiveEs._(_root);
}
// Path: stream
@ -80,6 +83,7 @@ class _TranslationsStreamEs extends TranslationsStreamEn {
// Translations
@override late final _TranslationsStreamStatusEs status = _TranslationsStreamStatusEs._(_root);
@override String started({required Object timestamp}) => 'Comenzó ${timestamp}';
@override String notification({required Object name}) => '${name} ¡se ha puesto en marcha!';
@override late final _TranslationsStreamChatEs chat = _TranslationsStreamChatEs._(_root);
}
@ -204,6 +208,30 @@ class _TranslationsLoginEs extends TranslationsLoginEn {
@override late final _TranslationsLoginErrorEs error = _TranslationsLoginErrorEs._(_root);
}
// Path: live
class _TranslationsLiveEs extends TranslationsLiveEn {
_TranslationsLiveEs._(TranslationsEs root) : this._root = root, super.internal(root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override String get start => 'EN VIVO';
@override String get configure_stream => 'Configurar Stream';
@override String get endpoint => 'Punto final';
@override String get accept_tos => 'Aceptar TOS';
@override String balance_left({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('es'))(n,
zero: '',
other: '~${time}',
);
@override String get title => 'Título';
@override String get summary => 'Resumen';
@override String get image => 'Imagen de portada';
@override String get tags => 'Etiquetas';
@override String get nsfw => 'Contenido NSFW';
@override String get nsfw_description => 'Compruebe aquí si este flujo contiene desnudos o contenido pornográfico.';
@override late final _TranslationsLiveErrorEs error = _TranslationsLiveErrorEs._(_root);
}
// Path: stream.status
class _TranslationsStreamStatusEs extends TranslationsStreamStatusEn {
_TranslationsStreamStatusEs._(TranslationsEs root) : this._root = root, super.internal(root);
@ -288,6 +316,8 @@ class _TranslationsSettingsWalletEs extends TranslationsSettingsWalletEn {
@override String get disconnect_wallet => 'Desconectar Cartera';
@override String get connect_1tap => 'Conexión de 1 toma';
@override String get paste => 'Pegar URL';
@override String get balance => 'Saldo';
@override String get name => 'Cartera';
@override late final _TranslationsSettingsWalletErrorEs error = _TranslationsSettingsWalletErrorEs._(_root);
}
@ -301,6 +331,18 @@ class _TranslationsLoginErrorEs extends TranslationsLoginErrorEn {
@override String get invalid_key => 'Clave no válida';
}
// Path: live.error
class _TranslationsLiveErrorEs extends TranslationsLiveErrorEn {
_TranslationsLiveErrorEs._(TranslationsEs root) : this._root = root, super.internal(root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override String get failed => 'Corriente fallida';
@override String get connection_error => 'Error de conexión';
@override String get start_failed => 'Error en el inicio de la transmisión, compruebe su saldo';
}
// Path: stream.chat.write
class _TranslationsStreamChatWriteEs extends TranslationsStreamChatWriteEn {
_TranslationsStreamChatWriteEs._(TranslationsEs root) : this._root = root, super.internal(root);
@ -379,6 +421,7 @@ extension on TranslationsEs {
case 'most_zapped_streamers': return 'Serpentinas más derribadas';
case 'no_user_found': return 'No se ha encontrado ningún usuario';
case 'anon': return 'Anónimo';
case 'full_amount_sats': return ({required num n}) => '${NumberFormat.decimalPattern('es').format(n)} sats';
case 'viewers': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('es'))(n,
one: '1 espectador',
other: '${NumberFormat.decimalPattern('es').format(n)} espectadores',
@ -387,6 +430,7 @@ extension on TranslationsEs {
case 'stream.status.ended': return 'FIN';
case 'stream.status.planned': return 'PLANIFICADO';
case 'stream.started': return ({required Object timestamp}) => 'Comenzó ${timestamp}';
case 'stream.notification': return ({required Object name}) => '${name} ¡se ha puesto en marcha!';
case 'stream.chat.disabled': return 'CHAT DESHABILITADO';
case 'stream.chat.disabled_timeout': return ({required Object time}) => 'El tiempo de espera expira: ${time}';
case 'stream.chat.timeout': return ({required InlineSpan mod, required InlineSpan user, required InlineSpan time}) => TextSpan(children: [
@ -455,6 +499,8 @@ extension on TranslationsEs {
case 'settings.wallet.disconnect_wallet': return 'Desconectar Cartera';
case 'settings.wallet.connect_1tap': return 'Conexión de 1 toma';
case 'settings.wallet.paste': return 'Pegar URL';
case 'settings.wallet.balance': return 'Saldo';
case 'settings.wallet.name': return 'Cartera';
case 'settings.wallet.error.logged_out': return 'No se puede conectar el monedero al cerrar la sesión';
case 'settings.wallet.error.nwc_auth_event_not_found': return 'No se ha encontrado ningún evento de autenticación de cartera';
case 'login.username': return 'Usuario';
@ -462,6 +508,23 @@ extension on TranslationsEs {
case 'login.key': return 'Inicio de sesión con clave';
case 'login.create': return 'Crear una cuenta';
case 'login.error.invalid_key': return 'Clave no válida';
case 'live.start': return 'EN VIVO';
case 'live.configure_stream': return 'Configurar Stream';
case 'live.endpoint': return 'Punto final';
case 'live.accept_tos': return 'Aceptar TOS';
case 'live.balance_left': return ({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('es'))(n,
zero: '',
other: '~${time}',
);
case 'live.title': return 'Título';
case 'live.summary': return 'Resumen';
case 'live.image': return 'Imagen de portada';
case 'live.tags': return 'Etiquetas';
case 'live.nsfw': return 'Contenido NSFW';
case 'live.nsfw_description': return 'Compruebe aquí si este flujo contiene desnudos o contenido pornográfico.';
case 'live.error.failed': return 'Corriente fallida';
case 'live.error.connection_error': return 'Error de conexión';
case 'live.error.start_failed': return 'Error en el inicio de la transmisión, compruebe su saldo';
default: return null;
}
}

View File

@ -51,6 +51,8 @@ class TranslationsFi extends Translations {
/// Nimetön käyttäjä
@override String get anon => 'Anon';
@override String full_amount_sats({required num n}) => '${NumberFormat.decimalPattern('fi').format(n)} sats';
/// Streamin katsojien määrä
@override String viewers({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('fi'))(n,
one: '1 katsoja',
@ -69,6 +71,7 @@ class TranslationsFi extends Translations {
@override late final _TranslationsProfileFi profile = _TranslationsProfileFi._(_root);
@override late final _TranslationsSettingsFi settings = _TranslationsSettingsFi._(_root);
@override late final _TranslationsLoginFi login = _TranslationsLoginFi._(_root);
@override late final _TranslationsLiveFi live = _TranslationsLiveFi._(_root);
}
// Path: stream
@ -80,6 +83,7 @@ class _TranslationsStreamFi extends TranslationsStreamEn {
// Translations
@override late final _TranslationsStreamStatusFi status = _TranslationsStreamStatusFi._(_root);
@override String started({required Object timestamp}) => 'Aloitettu ${timestamp}';
@override String notification({required Object name}) => '${name} meni suoraksi!';
@override late final _TranslationsStreamChatFi chat = _TranslationsStreamChatFi._(_root);
}
@ -204,6 +208,30 @@ class _TranslationsLoginFi extends TranslationsLoginEn {
@override late final _TranslationsLoginErrorFi error = _TranslationsLoginErrorFi._(_root);
}
// Path: live
class _TranslationsLiveFi extends TranslationsLiveEn {
_TranslationsLiveFi._(TranslationsFi root) : this._root = root, super.internal(root);
final TranslationsFi _root; // ignore: unused_field
// Translations
@override String get start => 'GO LIVE';
@override String get configure_stream => 'Määritä Stream';
@override String get endpoint => 'Loppupiste';
@override String get accept_tos => 'Hyväksy TOS';
@override String balance_left({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('fi'))(n,
zero: '',
other: '~${time}',
);
@override String get title => 'Otsikko';
@override String get summary => 'Yhteenveto';
@override String get image => 'Kansikuva';
@override String get tags => 'Tunnisteet';
@override String get nsfw => 'NSFW-sisältö';
@override String get nsfw_description => 'Tarkista täältä, jos tämä stream sisältää alastomuutta tai pornografista sisältöä.';
@override late final _TranslationsLiveErrorFi error = _TranslationsLiveErrorFi._(_root);
}
// Path: stream.status
class _TranslationsStreamStatusFi extends TranslationsStreamStatusEn {
_TranslationsStreamStatusFi._(TranslationsFi root) : this._root = root, super.internal(root);
@ -288,6 +316,8 @@ class _TranslationsSettingsWalletFi extends TranslationsSettingsWalletEn {
@override String get disconnect_wallet => 'Irrota lompakko';
@override String get connect_1tap => '1-Tap-liitäntä';
@override String get paste => 'Liitä URL-osoite';
@override String get balance => 'Balance';
@override String get name => 'Lompakko';
@override late final _TranslationsSettingsWalletErrorFi error = _TranslationsSettingsWalletErrorFi._(_root);
}
@ -301,6 +331,18 @@ class _TranslationsLoginErrorFi extends TranslationsLoginErrorEn {
@override String get invalid_key => 'Virheellinen avain';
}
// Path: live.error
class _TranslationsLiveErrorFi extends TranslationsLiveErrorEn {
_TranslationsLiveErrorFi._(TranslationsFi root) : this._root = root, super.internal(root);
final TranslationsFi _root; // ignore: unused_field
// Translations
@override String get failed => 'Stream epäonnistui';
@override String get connection_error => 'Yhteysvirhe';
@override String get start_failed => 'Virran käynnistys epäonnistui, tarkista saldosi';
}
// Path: stream.chat.write
class _TranslationsStreamChatWriteFi extends TranslationsStreamChatWriteEn {
_TranslationsStreamChatWriteFi._(TranslationsFi root) : this._root = root, super.internal(root);
@ -379,6 +421,7 @@ extension on TranslationsFi {
case 'most_zapped_streamers': return 'Eniten Zapped Streamers';
case 'no_user_found': return 'Käyttäjää ei löytynyt';
case 'anon': return 'Anon';
case 'full_amount_sats': return ({required num n}) => '${NumberFormat.decimalPattern('fi').format(n)} sats';
case 'viewers': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('fi'))(n,
one: '1 katsoja',
other: '${NumberFormat.decimalPattern('fi').format(n)} katsojat',
@ -387,6 +430,7 @@ extension on TranslationsFi {
case 'stream.status.ended': return 'ENDED';
case 'stream.status.planned': return 'SUUNNITELTU';
case 'stream.started': return ({required Object timestamp}) => 'Aloitettu ${timestamp}';
case 'stream.notification': return ({required Object name}) => '${name} meni suoraksi!';
case 'stream.chat.disabled': return 'CHAT POISTETTU KÄYTÖSTÄ';
case 'stream.chat.disabled_timeout': return ({required Object time}) => 'Aikakatkaisu päättyy: ${time}';
case 'stream.chat.timeout': return ({required InlineSpan mod, required InlineSpan user, required InlineSpan time}) => TextSpan(children: [
@ -455,6 +499,8 @@ extension on TranslationsFi {
case 'settings.wallet.disconnect_wallet': return 'Irrota lompakko';
case 'settings.wallet.connect_1tap': return '1-Tap-liitäntä';
case 'settings.wallet.paste': return 'Liitä URL-osoite';
case 'settings.wallet.balance': return 'Balance';
case 'settings.wallet.name': return 'Lompakko';
case 'settings.wallet.error.logged_out': return 'Ei voi muodostaa yhteyttä lompakkoon, kun on kirjautunut ulos';
case 'settings.wallet.error.nwc_auth_event_not_found': return 'Ei lompakko-auth-tapahtumaa löydetty';
case 'login.username': return 'Käyttäjätunnus';
@ -462,6 +508,23 @@ extension on TranslationsFi {
case 'login.key': return 'Kirjaudu sisään avaimella';
case 'login.create': return 'Luo tili';
case 'login.error.invalid_key': return 'Virheellinen avain';
case 'live.start': return 'GO LIVE';
case 'live.configure_stream': return 'Määritä Stream';
case 'live.endpoint': return 'Loppupiste';
case 'live.accept_tos': return 'Hyväksy TOS';
case 'live.balance_left': return ({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('fi'))(n,
zero: '',
other: '~${time}',
);
case 'live.title': return 'Otsikko';
case 'live.summary': return 'Yhteenveto';
case 'live.image': return 'Kansikuva';
case 'live.tags': return 'Tunnisteet';
case 'live.nsfw': return 'NSFW-sisältö';
case 'live.nsfw_description': return 'Tarkista täältä, jos tämä stream sisältää alastomuutta tai pornografista sisältöä.';
case 'live.error.failed': return 'Stream epäonnistui';
case 'live.error.connection_error': return 'Yhteysvirhe';
case 'live.error.start_failed': return 'Virran käynnistys epäonnistui, tarkista saldosi';
default: return null;
}
}

View File

@ -51,6 +51,8 @@ class TranslationsFr extends Translations {
/// Un utilisateur anonyme
@override String get anon => 'Anonyme';
@override String full_amount_sats({required num n}) => '${NumberFormat.decimalPattern('fr').format(n)} sats';
/// Nombre de spectateurs du flux
@override String viewers({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('fr'))(n,
one: '1 téléspectateur',
@ -69,6 +71,7 @@ class TranslationsFr extends Translations {
@override late final _TranslationsProfileFr profile = _TranslationsProfileFr._(_root);
@override late final _TranslationsSettingsFr settings = _TranslationsSettingsFr._(_root);
@override late final _TranslationsLoginFr login = _TranslationsLoginFr._(_root);
@override late final _TranslationsLiveFr live = _TranslationsLiveFr._(_root);
}
// Path: stream
@ -80,6 +83,7 @@ class _TranslationsStreamFr extends TranslationsStreamEn {
// Translations
@override late final _TranslationsStreamStatusFr status = _TranslationsStreamStatusFr._(_root);
@override String started({required Object timestamp}) => 'Commencé à ${timestamp}';
@override String notification({required Object name}) => '${name} est en ligne !';
@override late final _TranslationsStreamChatFr chat = _TranslationsStreamChatFr._(_root);
}
@ -204,6 +208,30 @@ class _TranslationsLoginFr extends TranslationsLoginEn {
@override late final _TranslationsLoginErrorFr error = _TranslationsLoginErrorFr._(_root);
}
// Path: live
class _TranslationsLiveFr extends TranslationsLiveEn {
_TranslationsLiveFr._(TranslationsFr root) : this._root = root, super.internal(root);
final TranslationsFr _root; // ignore: unused_field
// Translations
@override String get start => 'GO LIVE';
@override String get configure_stream => 'Configurer le flux';
@override String get endpoint => 'Point final';
@override String get accept_tos => 'Accepter les CGU';
@override String balance_left({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('fr'))(n,
zero: '',
other: '~${time}',
);
@override String get title => 'Titre';
@override String get summary => 'Résumé';
@override String get image => 'Image de couverture';
@override String get tags => 'Tags';
@override String get nsfw => 'Contenu NSFW';
@override String get nsfw_description => 'Cochez cette case si ce flux contient de la nudité ou du contenu pornographique.';
@override late final _TranslationsLiveErrorFr error = _TranslationsLiveErrorFr._(_root);
}
// Path: stream.status
class _TranslationsStreamStatusFr extends TranslationsStreamStatusEn {
_TranslationsStreamStatusFr._(TranslationsFr root) : this._root = root, super.internal(root);
@ -288,6 +316,8 @@ class _TranslationsSettingsWalletFr extends TranslationsSettingsWalletEn {
@override String get disconnect_wallet => 'Déconnecter le portefeuille';
@override String get connect_1tap => 'Connexion à 1 robinet';
@override String get paste => 'Coller l\'URL';
@override String get balance => 'Équilibre';
@override String get name => 'Portefeuille';
@override late final _TranslationsSettingsWalletErrorFr error = _TranslationsSettingsWalletErrorFr._(_root);
}
@ -301,6 +331,18 @@ class _TranslationsLoginErrorFr extends TranslationsLoginErrorEn {
@override String get invalid_key => 'Clé non valide';
}
// Path: live.error
class _TranslationsLiveErrorFr extends TranslationsLiveErrorEn {
_TranslationsLiveErrorFr._(TranslationsFr root) : this._root = root, super.internal(root);
final TranslationsFr _root; // ignore: unused_field
// Translations
@override String get failed => 'Échec du flux';
@override String get connection_error => 'Erreur de connexion';
@override String get start_failed => 'Le démarrage du flux a échoué, veuillez vérifier votre solde';
}
// Path: stream.chat.write
class _TranslationsStreamChatWriteFr extends TranslationsStreamChatWriteEn {
_TranslationsStreamChatWriteFr._(TranslationsFr root) : this._root = root, super.internal(root);
@ -379,6 +421,7 @@ extension on TranslationsFr {
case 'most_zapped_streamers': return 'Les Streamers les plus zappés';
case 'no_user_found': return 'Aucun utilisateur trouvé';
case 'anon': return 'Anonyme';
case 'full_amount_sats': return ({required num n}) => '${NumberFormat.decimalPattern('fr').format(n)} sats';
case 'viewers': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('fr'))(n,
one: '1 téléspectateur',
other: '${NumberFormat.decimalPattern('fr').format(n)} téléspectateurs',
@ -387,6 +430,7 @@ extension on TranslationsFr {
case 'stream.status.ended': return 'FINI';
case 'stream.status.planned': return 'PRÉVU';
case 'stream.started': return ({required Object timestamp}) => 'Commencé à ${timestamp}';
case 'stream.notification': return ({required Object name}) => '${name} est en ligne !';
case 'stream.chat.disabled': return 'CHAT DISABLED';
case 'stream.chat.disabled_timeout': return ({required Object time}) => 'Le délai expire : ${time}';
case 'stream.chat.timeout': return ({required InlineSpan mod, required InlineSpan user, required InlineSpan time}) => TextSpan(children: [
@ -455,6 +499,8 @@ extension on TranslationsFr {
case 'settings.wallet.disconnect_wallet': return 'Déconnecter le portefeuille';
case 'settings.wallet.connect_1tap': return 'Connexion à 1 robinet';
case 'settings.wallet.paste': return 'Coller l\'URL';
case 'settings.wallet.balance': return 'Équilibre';
case 'settings.wallet.name': return 'Portefeuille';
case 'settings.wallet.error.logged_out': return 'Impossible de se connecter au portefeuille lorsque l\'on est déconnecté';
case 'settings.wallet.error.nwc_auth_event_not_found': return 'Aucun événement d\'authentification de portefeuille n\'a été trouvé';
case 'login.username': return 'Nom dutilisateur';
@ -462,6 +508,23 @@ extension on TranslationsFr {
case 'login.key': return 'Connexion avec la clé';
case 'login.create': return 'Créer un Compte';
case 'login.error.invalid_key': return 'Clé non valide';
case 'live.start': return 'GO LIVE';
case 'live.configure_stream': return 'Configurer le flux';
case 'live.endpoint': return 'Point final';
case 'live.accept_tos': return 'Accepter les CGU';
case 'live.balance_left': return ({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('fr'))(n,
zero: '',
other: '~${time}',
);
case 'live.title': return 'Titre';
case 'live.summary': return 'Résumé';
case 'live.image': return 'Image de couverture';
case 'live.tags': return 'Tags';
case 'live.nsfw': return 'Contenu NSFW';
case 'live.nsfw_description': return 'Cochez cette case si ce flux contient de la nudité ou du contenu pornographique.';
case 'live.error.failed': return 'Échec du flux';
case 'live.error.connection_error': return 'Erreur de connexion';
case 'live.error.start_failed': return 'Le démarrage du flux a échoué, veuillez vérifier votre solde';
default: return null;
}
}

View File

@ -80,6 +80,7 @@ class _TranslationsStreamHu extends TranslationsStreamEn {
// Translations
@override late final _TranslationsStreamStatusHu status = _TranslationsStreamStatusHu._(_root);
@override String started({required Object timestamp}) => 'Elindult ${timestamp}';
@override String notification({required Object name}) => '${name} elindult!';
@override late final _TranslationsStreamChatHu chat = _TranslationsStreamChatHu._(_root);
}
@ -132,7 +133,7 @@ class _TranslationsEmbedHu extends TranslationsEmbedEn {
// Translations
@override String article_by({required Object name}) => 'Cikk ${name}';
@override String note_by({required Object name}) => '${name} bejegyzése';
@override String live_stream_by({required Object name}) => 'Élő közvetítés a ${name}oldalon';
@override String live_stream_by({required Object name}) => 'Élő közvetítés a ${name} oldalon';
}
// Path: stream_list
@ -347,7 +348,7 @@ class _TranslationsStreamChatRaidHu extends TranslationsStreamChatRaidEn {
@override String from({required Object name}) => 'RAID FROM ${name}';
/// Visszaszámláló időzítő az automatikus lovagláshoz
@override String countdown({required Object time}) => 'Raiding a ${time}oldalon';
@override String countdown({required Object time}) => 'Raiding a ${time} oldalon';
}
// Path: settings.profile.error
@ -388,6 +389,7 @@ extension on TranslationsHu {
case 'stream.status.ended': return 'ENDED';
case 'stream.status.planned': return 'TERVEZETT';
case 'stream.started': return ({required Object timestamp}) => 'Elindult ${timestamp}';
case 'stream.notification': return ({required Object name}) => '${name} elindult!';
case 'stream.chat.disabled': return 'CHAT KIKAPCSOLVA';
case 'stream.chat.disabled_timeout': return ({required Object time}) => 'Az időkorlát lejár: ${time}';
case 'stream.chat.timeout': return ({required InlineSpan mod, required InlineSpan user, required InlineSpan time}) => TextSpan(children: [
@ -411,7 +413,7 @@ extension on TranslationsHu {
case 'stream.chat.badge.awarded_to': return 'Elnyerte:';
case 'stream.chat.raid.to': return ({required Object name}) => 'RAIDING ${name}';
case 'stream.chat.raid.from': return ({required Object name}) => 'RAID FROM ${name}';
case 'stream.chat.raid.countdown': return ({required Object time}) => 'Raiding a ${time}oldalon';
case 'stream.chat.raid.countdown': return ({required Object time}) => 'Raiding a ${time} oldalon';
case 'goal.title': return ({required Object amount}) => 'Cél: ${amount}';
case 'goal.remaining': return ({required Object amount}) => 'Maradék: ${amount}';
case 'goal.complete': return 'TELJES';
@ -428,7 +430,7 @@ extension on TranslationsHu {
case 'button.settings': return 'Beállítások';
case 'embed.article_by': return ({required Object name}) => 'Cikk ${name}';
case 'embed.note_by': return ({required Object name}) => '${name} bejegyzése';
case 'embed.live_stream_by': return ({required Object name}) => 'Élő közvetítés a ${name}oldalon';
case 'embed.live_stream_by': return ({required Object name}) => 'Élő közvetítés a ${name} oldalon';
case 'stream_list.following': return 'Követettek bejegyzései';
case 'stream_list.live': return 'Élő';
case 'stream_list.planned': return 'Tervezett';

View File

@ -51,6 +51,8 @@ class TranslationsIt extends Translations {
/// Un utente anonimo
@override String get anon => 'Anonimo';
@override String full_amount_sats({required num n}) => '${NumberFormat.decimalPattern('it').format(n)} sats';
/// Numero di spettatori del flusso
@override String viewers({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('it'))(n,
one: '1 spettatore',
@ -69,6 +71,7 @@ class TranslationsIt extends Translations {
@override late final _TranslationsProfileIt profile = _TranslationsProfileIt._(_root);
@override late final _TranslationsSettingsIt settings = _TranslationsSettingsIt._(_root);
@override late final _TranslationsLoginIt login = _TranslationsLoginIt._(_root);
@override late final _TranslationsLiveIt live = _TranslationsLiveIt._(_root);
}
// Path: stream
@ -80,6 +83,7 @@ class _TranslationsStreamIt extends TranslationsStreamEn {
// Translations
@override late final _TranslationsStreamStatusIt status = _TranslationsStreamStatusIt._(_root);
@override String started({required Object timestamp}) => 'Avviato ${timestamp}';
@override String notification({required Object name}) => '${name} è andato in onda!';
@override late final _TranslationsStreamChatIt chat = _TranslationsStreamChatIt._(_root);
}
@ -204,6 +208,30 @@ class _TranslationsLoginIt extends TranslationsLoginEn {
@override late final _TranslationsLoginErrorIt error = _TranslationsLoginErrorIt._(_root);
}
// Path: live
class _TranslationsLiveIt extends TranslationsLiveEn {
_TranslationsLiveIt._(TranslationsIt root) : this._root = root, super.internal(root);
final TranslationsIt _root; // ignore: unused_field
// Translations
@override String get start => 'VAI IN DIRETTA';
@override String get configure_stream => 'Configurare il flusso';
@override String get endpoint => 'Punto finale';
@override String get accept_tos => 'Accettare i TOS';
@override String balance_left({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('it'))(n,
zero: '',
other: '~${time}',
);
@override String get title => 'Titolo';
@override String get summary => 'Sintesi';
@override String get image => 'Immagine di copertina';
@override String get tags => 'Tag';
@override String get nsfw => 'Contenuto NSFW';
@override String get nsfw_description => 'Controllare qui se questo streaming contiene nudità o contenuti pornografici.';
@override late final _TranslationsLiveErrorIt error = _TranslationsLiveErrorIt._(_root);
}
// Path: stream.status
class _TranslationsStreamStatusIt extends TranslationsStreamStatusEn {
_TranslationsStreamStatusIt._(TranslationsIt root) : this._root = root, super.internal(root);
@ -288,6 +316,8 @@ class _TranslationsSettingsWalletIt extends TranslationsSettingsWalletEn {
@override String get disconnect_wallet => 'Disconnettere il portafoglio';
@override String get connect_1tap => 'Connessione a 1 rubinetto';
@override String get paste => 'Incolla URL';
@override String get balance => 'Equilibrio';
@override String get name => 'Portafoglio';
@override late final _TranslationsSettingsWalletErrorIt error = _TranslationsSettingsWalletErrorIt._(_root);
}
@ -301,6 +331,18 @@ class _TranslationsLoginErrorIt extends TranslationsLoginErrorEn {
@override String get invalid_key => 'Chiave non valida';
}
// Path: live.error
class _TranslationsLiveErrorIt extends TranslationsLiveErrorEn {
_TranslationsLiveErrorIt._(TranslationsIt root) : this._root = root, super.internal(root);
final TranslationsIt _root; // ignore: unused_field
// Translations
@override String get failed => 'Flusso fallito';
@override String get connection_error => 'Errore di connessione';
@override String get start_failed => 'Avvio del flusso fallito, controllare il saldo';
}
// Path: stream.chat.write
class _TranslationsStreamChatWriteIt extends TranslationsStreamChatWriteEn {
_TranslationsStreamChatWriteIt._(TranslationsIt root) : this._root = root, super.internal(root);
@ -379,6 +421,7 @@ extension on TranslationsIt {
case 'most_zapped_streamers': return 'Il maggior numero di streamer bloccati';
case 'no_user_found': return 'Nessun utente trovato';
case 'anon': return 'Anonimo';
case 'full_amount_sats': return ({required num n}) => '${NumberFormat.decimalPattern('it').format(n)} sats';
case 'viewers': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('it'))(n,
one: '1 spettatore',
other: '${NumberFormat.decimalPattern('it').format(n)} spettatori',
@ -387,6 +430,7 @@ extension on TranslationsIt {
case 'stream.status.ended': return 'FINE';
case 'stream.status.planned': return 'PREVISTO';
case 'stream.started': return ({required Object timestamp}) => 'Avviato ${timestamp}';
case 'stream.notification': return ({required Object name}) => '${name} è andato in onda!';
case 'stream.chat.disabled': return 'CHAT DISABILITATA';
case 'stream.chat.disabled_timeout': return ({required Object time}) => 'Il timeout scade: ${time}';
case 'stream.chat.timeout': return ({required InlineSpan mod, required InlineSpan user, required InlineSpan time}) => TextSpan(children: [
@ -455,6 +499,8 @@ extension on TranslationsIt {
case 'settings.wallet.disconnect_wallet': return 'Disconnettere il portafoglio';
case 'settings.wallet.connect_1tap': return 'Connessione a 1 rubinetto';
case 'settings.wallet.paste': return 'Incolla URL';
case 'settings.wallet.balance': return 'Equilibrio';
case 'settings.wallet.name': return 'Portafoglio';
case 'settings.wallet.error.logged_out': return 'Impossibile connettere il portafoglio quando si è disconnessi';
case 'settings.wallet.error.nwc_auth_event_not_found': return 'Nessun evento wallet auth trovato';
case 'login.username': return 'Nome utente';
@ -462,6 +508,23 @@ extension on TranslationsIt {
case 'login.key': return 'Accesso con chiave';
case 'login.create': return 'Crea un account';
case 'login.error.invalid_key': return 'Chiave non valida';
case 'live.start': return 'VAI IN DIRETTA';
case 'live.configure_stream': return 'Configurare il flusso';
case 'live.endpoint': return 'Punto finale';
case 'live.accept_tos': return 'Accettare i TOS';
case 'live.balance_left': return ({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('it'))(n,
zero: '',
other: '~${time}',
);
case 'live.title': return 'Titolo';
case 'live.summary': return 'Sintesi';
case 'live.image': return 'Immagine di copertina';
case 'live.tags': return 'Tag';
case 'live.nsfw': return 'Contenuto NSFW';
case 'live.nsfw_description': return 'Controllare qui se questo streaming contiene nudità o contenuti pornografici.';
case 'live.error.failed': return 'Flusso fallito';
case 'live.error.connection_error': return 'Errore di connessione';
case 'live.error.start_failed': return 'Avvio del flusso fallito, controllare il saldo';
default: return null;
}
}

View File

@ -51,6 +51,8 @@ class TranslationsJa extends Translations {
/// 匿名ユーザー
@override String get anon => '匿名';
@override String full_amount_sats({required num n}) => '${NumberFormat.decimalPattern('ja').format(n)} サッツ';
/// ストリームの視聴者数
@override String viewers({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ja'))(n,
one: '1 視聴者',
@ -69,6 +71,7 @@ class TranslationsJa extends Translations {
@override late final _TranslationsProfileJa profile = _TranslationsProfileJa._(_root);
@override late final _TranslationsSettingsJa settings = _TranslationsSettingsJa._(_root);
@override late final _TranslationsLoginJa login = _TranslationsLoginJa._(_root);
@override late final _TranslationsLiveJa live = _TranslationsLiveJa._(_root);
}
// Path: stream
@ -80,6 +83,7 @@ class _TranslationsStreamJa extends TranslationsStreamEn {
// Translations
@override late final _TranslationsStreamStatusJa status = _TranslationsStreamStatusJa._(_root);
@override String started({required Object timestamp}) => '${timestamp} を開始';
@override String notification({required Object name}) => '${name} がライブを開始した!';
@override late final _TranslationsStreamChatJa chat = _TranslationsStreamChatJa._(_root);
}
@ -204,6 +208,30 @@ class _TranslationsLoginJa extends TranslationsLoginEn {
@override late final _TranslationsLoginErrorJa error = _TranslationsLoginErrorJa._(_root);
}
// Path: live
class _TranslationsLiveJa extends TranslationsLiveEn {
_TranslationsLiveJa._(TranslationsJa root) : this._root = root, super.internal(root);
final TranslationsJa _root; // ignore: unused_field
// Translations
@override String get start => 'ライブ中継';
@override String get configure_stream => 'ストリームの設定';
@override String get endpoint => 'エンドポイント';
@override String get accept_tos => 'TOSを受け入れる';
@override String balance_left({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ja'))(n,
zero: '',
other: '~${time}',
);
@override String get title => 'タイトル';
@override String get summary => '概要';
@override String get image => '表紙画像';
@override String get tags => 'タグ';
@override String get nsfw => 'NSFWコンテンツ';
@override String get nsfw_description => 'このストリームにヌードやポルノが含まれている場合は、ここをチェックしてください。';
@override late final _TranslationsLiveErrorJa error = _TranslationsLiveErrorJa._(_root);
}
// Path: stream.status
class _TranslationsStreamStatusJa extends TranslationsStreamStatusEn {
_TranslationsStreamStatusJa._(TranslationsJa root) : this._root = root, super.internal(root);
@ -288,6 +316,8 @@ class _TranslationsSettingsWalletJa extends TranslationsSettingsWalletEn {
@override String get disconnect_wallet => 'ウォレットの切断';
@override String get connect_1tap => '1タップ接続';
@override String get paste => 'URLを貼り付ける';
@override String get balance => 'バランス';
@override String get name => '財布';
@override late final _TranslationsSettingsWalletErrorJa error = _TranslationsSettingsWalletErrorJa._(_root);
}
@ -301,6 +331,18 @@ class _TranslationsLoginErrorJa extends TranslationsLoginErrorEn {
@override String get invalid_key => '無効なキー';
}
// Path: live.error
class _TranslationsLiveErrorJa extends TranslationsLiveErrorEn {
_TranslationsLiveErrorJa._(TranslationsJa root) : this._root = root, super.internal(root);
final TranslationsJa _root; // ignore: unused_field
// Translations
@override String get failed => 'ストリーム失敗';
@override String get connection_error => '接続エラー';
@override String get start_failed => 'ストリームの開始に失敗しました。';
}
// Path: stream.chat.write
class _TranslationsStreamChatWriteJa extends TranslationsStreamChatWriteEn {
_TranslationsStreamChatWriteJa._(TranslationsJa root) : this._root = root, super.internal(root);
@ -379,6 +421,7 @@ extension on TranslationsJa {
case 'most_zapped_streamers': return '最もザッピングされたストリーマー';
case 'no_user_found': return 'ユーザーが見つかりません';
case 'anon': return '匿名';
case 'full_amount_sats': return ({required num n}) => '${NumberFormat.decimalPattern('ja').format(n)} サッツ';
case 'viewers': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ja'))(n,
one: '1 視聴者',
other: '${NumberFormat.decimalPattern('ja').format(n)} 人が視聴中',
@ -387,6 +430,7 @@ extension on TranslationsJa {
case 'stream.status.ended': return '終了';
case 'stream.status.planned': return '予定';
case 'stream.started': return ({required Object timestamp}) => '${timestamp} を開始';
case 'stream.notification': return ({required Object name}) => '${name} がライブを開始した!';
case 'stream.chat.disabled': return 'チャット無効';
case 'stream.chat.disabled_timeout': return ({required Object time}) => 'タイムアウト: ${time}';
case 'stream.chat.timeout': return ({required InlineSpan mod, required InlineSpan user, required InlineSpan time}) => TextSpan(children: [
@ -455,6 +499,8 @@ extension on TranslationsJa {
case 'settings.wallet.disconnect_wallet': return 'ウォレットの切断';
case 'settings.wallet.connect_1tap': return '1タップ接続';
case 'settings.wallet.paste': return 'URLを貼り付ける';
case 'settings.wallet.balance': return 'バランス';
case 'settings.wallet.name': return '財布';
case 'settings.wallet.error.logged_out': return 'ログアウト時にウォレットに接続できない';
case 'settings.wallet.error.nwc_auth_event_not_found': return 'ウォレットの認証イベントが見つかりません';
case 'login.username': return 'ユーザー名';
@ -462,6 +508,23 @@ extension on TranslationsJa {
case 'login.key': return 'キーでログイン';
case 'login.create': return 'アカウントを作成する';
case 'login.error.invalid_key': return '無効なキー';
case 'live.start': return 'ライブ中継';
case 'live.configure_stream': return 'ストリームの設定';
case 'live.endpoint': return 'エンドポイント';
case 'live.accept_tos': return 'TOSを受け入れる';
case 'live.balance_left': return ({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ja'))(n,
zero: '',
other: '~${time}',
);
case 'live.title': return 'タイトル';
case 'live.summary': return '概要';
case 'live.image': return '表紙画像';
case 'live.tags': return 'タグ';
case 'live.nsfw': return 'NSFWコンテンツ';
case 'live.nsfw_description': return 'このストリームにヌードやポルノが含まれている場合は、ここをチェックしてください。';
case 'live.error.failed': return 'ストリーム失敗';
case 'live.error.connection_error': return '接続エラー';
case 'live.error.start_failed': return 'ストリームの開始に失敗しました。';
default: return null;
}
}

View File

@ -51,10 +51,12 @@ class TranslationsKo extends Translations {
/// 익명 사용자
@override String get anon => 'Anon';
@override String full_amount_sats({required num n}) => '${NumberFormat.decimalPattern('ko').format(n)} sats';
/// 스트림 시청자 수
@override String viewers({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ko'))(n,
one: '시청자 1명',
other: '${NumberFormat.decimalPattern('ko').format(n)} 시청자',
other: '{n:decimalPattern} 시청자',
);
@override late final _TranslationsStreamKo stream = _TranslationsStreamKo._(_root);
@ -69,6 +71,7 @@ class TranslationsKo extends Translations {
@override late final _TranslationsProfileKo profile = _TranslationsProfileKo._(_root);
@override late final _TranslationsSettingsKo settings = _TranslationsSettingsKo._(_root);
@override late final _TranslationsLoginKo login = _TranslationsLoginKo._(_root);
@override late final _TranslationsLiveKo live = _TranslationsLiveKo._(_root);
}
// Path: stream
@ -80,6 +83,7 @@ class _TranslationsStreamKo extends TranslationsStreamEn {
// Translations
@override late final _TranslationsStreamStatusKo status = _TranslationsStreamStatusKo._(_root);
@override String started({required Object timestamp}) => '시작 ${timestamp}';
@override String notification({required Object name}) => '${name} 라이브가 시작되었습니다!';
@override late final _TranslationsStreamChatKo chat = _TranslationsStreamChatKo._(_root);
}
@ -204,6 +208,30 @@ class _TranslationsLoginKo extends TranslationsLoginEn {
@override late final _TranslationsLoginErrorKo error = _TranslationsLoginErrorKo._(_root);
}
// Path: live
class _TranslationsLiveKo extends TranslationsLiveEn {
_TranslationsLiveKo._(TranslationsKo root) : this._root = root, super.internal(root);
final TranslationsKo _root; // ignore: unused_field
// Translations
@override String get start => '라이브 시작하기';
@override String get configure_stream => '스트림 구성';
@override String get endpoint => '엔드포인트';
@override String get accept_tos => 'TOS 수락';
@override String balance_left({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ko'))(n,
zero: '',
other: '~${time}',
);
@override String get title => '제목';
@override String get summary => '요약';
@override String get image => '표지 이미지';
@override String get tags => '태그';
@override String get nsfw => 'NSFW 콘텐츠';
@override String get nsfw_description => '이 스트림에 노출 또는 음란 콘텐츠가 포함되어 있는지 여기에서 확인하세요.';
@override late final _TranslationsLiveErrorKo error = _TranslationsLiveErrorKo._(_root);
}
// Path: stream.status
class _TranslationsStreamStatusKo extends TranslationsStreamStatusEn {
_TranslationsStreamStatusKo._(TranslationsKo root) : this._root = root, super.internal(root);
@ -288,6 +316,8 @@ class _TranslationsSettingsWalletKo extends TranslationsSettingsWalletEn {
@override String get disconnect_wallet => '지갑 연결 해제';
@override String get connect_1tap => '1-탭 연결';
@override String get paste => 'URL 붙여넣기';
@override String get balance => '잔액';
@override String get name => '지갑';
@override late final _TranslationsSettingsWalletErrorKo error = _TranslationsSettingsWalletErrorKo._(_root);
}
@ -301,6 +331,18 @@ class _TranslationsLoginErrorKo extends TranslationsLoginErrorEn {
@override String get invalid_key => '잘못된 키';
}
// Path: live.error
class _TranslationsLiveErrorKo extends TranslationsLiveErrorEn {
_TranslationsLiveErrorKo._(TranslationsKo root) : this._root = root, super.internal(root);
final TranslationsKo _root; // ignore: unused_field
// Translations
@override String get failed => '스트림 실패';
@override String get connection_error => '연결 오류';
@override String get start_failed => '스트림 시작에 실패했습니다. 잔액을 확인해 주세요.';
}
// Path: stream.chat.write
class _TranslationsStreamChatWriteKo extends TranslationsStreamChatWriteEn {
_TranslationsStreamChatWriteKo._(TranslationsKo root) : this._root = root, super.internal(root);
@ -379,14 +421,16 @@ extension on TranslationsKo {
case 'most_zapped_streamers': return '가장 많이 재핑된 스트리머';
case 'no_user_found': return '사용자를 찾을 수 없습니다.';
case 'anon': return 'Anon';
case 'full_amount_sats': return ({required num n}) => '${NumberFormat.decimalPattern('ko').format(n)} sats';
case 'viewers': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ko'))(n,
one: '시청자 1명',
other: '${NumberFormat.decimalPattern('ko').format(n)} 시청자',
other: '{n:decimalPattern} 시청자',
);
case 'stream.status.live': return '라이브';
case 'stream.status.ended': return '종료';
case 'stream.status.planned': return '계획된';
case 'stream.started': return ({required Object timestamp}) => '시작 ${timestamp}';
case 'stream.notification': return ({required Object name}) => '${name} 라이브가 시작되었습니다!';
case 'stream.chat.disabled': return '채팅 사용 안 함';
case 'stream.chat.disabled_timeout': return ({required Object time}) => '시간 초과가 만료되었습니다: ${time}';
case 'stream.chat.timeout': return ({required InlineSpan mod, required InlineSpan user, required InlineSpan time}) => TextSpan(children: [
@ -455,6 +499,8 @@ extension on TranslationsKo {
case 'settings.wallet.disconnect_wallet': return '지갑 연결 해제';
case 'settings.wallet.connect_1tap': return '1-탭 연결';
case 'settings.wallet.paste': return 'URL 붙여넣기';
case 'settings.wallet.balance': return '잔액';
case 'settings.wallet.name': return '지갑';
case 'settings.wallet.error.logged_out': return '로그아웃 시 지갑 연결 불가';
case 'settings.wallet.error.nwc_auth_event_not_found': return '지갑 인증 이벤트를 찾을 수 없습니다.';
case 'login.username': return '사용자 이름';
@ -462,6 +508,23 @@ extension on TranslationsKo {
case 'login.key': return '키로 로그인';
case 'login.create': return '계정 만들기';
case 'login.error.invalid_key': return '잘못된 키';
case 'live.start': return '라이브 시작하기';
case 'live.configure_stream': return '스트림 구성';
case 'live.endpoint': return '엔드포인트';
case 'live.accept_tos': return 'TOS 수락';
case 'live.balance_left': return ({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ko'))(n,
zero: '',
other: '~${time}',
);
case 'live.title': return '제목';
case 'live.summary': return '요약';
case 'live.image': return '표지 이미지';
case 'live.tags': return '태그';
case 'live.nsfw': return 'NSFW 콘텐츠';
case 'live.nsfw_description': return '이 스트림에 노출 또는 음란 콘텐츠가 포함되어 있는지 여기에서 확인하세요.';
case 'live.error.failed': return '스트림 실패';
case 'live.error.connection_error': return '연결 오류';
case 'live.error.start_failed': return '스트림 시작에 실패했습니다. 잔액을 확인해 주세요.';
default: return null;
}
}

View File

@ -51,6 +51,8 @@ class TranslationsNl extends Translations {
/// Een anonieme gebruiker
@override String get anon => 'Anon';
@override String full_amount_sats({required num n}) => '${NumberFormat.decimalPattern('nl').format(n)} sats';
/// Aantal kijkers van de stream
@override String viewers({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('nl'))(n,
one: '1 kijker',
@ -69,6 +71,7 @@ class TranslationsNl extends Translations {
@override late final _TranslationsProfileNl profile = _TranslationsProfileNl._(_root);
@override late final _TranslationsSettingsNl settings = _TranslationsSettingsNl._(_root);
@override late final _TranslationsLoginNl login = _TranslationsLoginNl._(_root);
@override late final _TranslationsLiveNl live = _TranslationsLiveNl._(_root);
}
// Path: stream
@ -80,6 +83,7 @@ class _TranslationsStreamNl extends TranslationsStreamEn {
// Translations
@override late final _TranslationsStreamStatusNl status = _TranslationsStreamStatusNl._(_root);
@override String started({required Object timestamp}) => 'Begonnen met ${timestamp}';
@override String notification({required Object name}) => '${name} ging live!';
@override late final _TranslationsStreamChatNl chat = _TranslationsStreamChatNl._(_root);
}
@ -204,6 +208,30 @@ class _TranslationsLoginNl extends TranslationsLoginEn {
@override late final _TranslationsLoginErrorNl error = _TranslationsLoginErrorNl._(_root);
}
// Path: live
class _TranslationsLiveNl extends TranslationsLiveEn {
_TranslationsLiveNl._(TranslationsNl root) : this._root = root, super.internal(root);
final TranslationsNl _root; // ignore: unused_field
// Translations
@override String get start => 'LIVE GAAN';
@override String get configure_stream => 'Stream configureren';
@override String get endpoint => 'Eindpunt';
@override String get accept_tos => 'TOS accepteren';
@override String balance_left({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('nl'))(n,
zero: '',
other: '~${time}',
);
@override String get title => 'Titel';
@override String get summary => 'Samenvatting';
@override String get image => 'Afbeelding omslag';
@override String get tags => 'Tags';
@override String get nsfw => 'NSFW-inhoud';
@override String get nsfw_description => 'Controleer hier of deze stream naaktheid of pornografische inhoud bevat.';
@override late final _TranslationsLiveErrorNl error = _TranslationsLiveErrorNl._(_root);
}
// Path: stream.status
class _TranslationsStreamStatusNl extends TranslationsStreamStatusEn {
_TranslationsStreamStatusNl._(TranslationsNl root) : this._root = root, super.internal(root);
@ -288,6 +316,8 @@ class _TranslationsSettingsWalletNl extends TranslationsSettingsWalletEn {
@override String get disconnect_wallet => 'Portefeuille loskoppelen';
@override String get connect_1tap => '1-Tap Aansluiting';
@override String get paste => 'URL plakken';
@override String get balance => 'Saldo';
@override String get name => 'Portemonnee';
@override late final _TranslationsSettingsWalletErrorNl error = _TranslationsSettingsWalletErrorNl._(_root);
}
@ -301,6 +331,18 @@ class _TranslationsLoginErrorNl extends TranslationsLoginErrorEn {
@override String get invalid_key => 'Ongeldige sleutel';
}
// Path: live.error
class _TranslationsLiveErrorNl extends TranslationsLiveErrorEn {
_TranslationsLiveErrorNl._(TranslationsNl root) : this._root = root, super.internal(root);
final TranslationsNl _root; // ignore: unused_field
// Translations
@override String get failed => 'Stream mislukt';
@override String get connection_error => 'Fout bij verbinding';
@override String get start_failed => 'Stream start mislukt, controleer uw saldo';
}
// Path: stream.chat.write
class _TranslationsStreamChatWriteNl extends TranslationsStreamChatWriteEn {
_TranslationsStreamChatWriteNl._(TranslationsNl root) : this._root = root, super.internal(root);
@ -379,6 +421,7 @@ extension on TranslationsNl {
case 'most_zapped_streamers': return 'Meeste Zapped Streamers';
case 'no_user_found': return 'Geen gebruiker gevonden';
case 'anon': return 'Anon';
case 'full_amount_sats': return ({required num n}) => '${NumberFormat.decimalPattern('nl').format(n)} sats';
case 'viewers': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('nl'))(n,
one: '1 kijker',
other: '${NumberFormat.decimalPattern('nl').format(n)} kijkers',
@ -387,6 +430,7 @@ extension on TranslationsNl {
case 'stream.status.ended': return 'GESLOTEN';
case 'stream.status.planned': return 'GEPLAND';
case 'stream.started': return ({required Object timestamp}) => 'Begonnen met ${timestamp}';
case 'stream.notification': return ({required Object name}) => '${name} ging live!';
case 'stream.chat.disabled': return 'CHAT UITGESCHAKELD';
case 'stream.chat.disabled_timeout': return ({required Object time}) => 'Time-out loopt af: ${time}';
case 'stream.chat.timeout': return ({required InlineSpan mod, required InlineSpan user, required InlineSpan time}) => TextSpan(children: [
@ -455,6 +499,8 @@ extension on TranslationsNl {
case 'settings.wallet.disconnect_wallet': return 'Portefeuille loskoppelen';
case 'settings.wallet.connect_1tap': return '1-Tap Aansluiting';
case 'settings.wallet.paste': return 'URL plakken';
case 'settings.wallet.balance': return 'Saldo';
case 'settings.wallet.name': return 'Portemonnee';
case 'settings.wallet.error.logged_out': return 'Kan geen verbinding maken met portemonnee als ik ben uitgelogd';
case 'settings.wallet.error.nwc_auth_event_not_found': return 'Geen portemonnee-auth-gebeurtenis gevonden';
case 'login.username': return 'Gebruikersnaam';
@ -462,6 +508,23 @@ extension on TranslationsNl {
case 'login.key': return 'Inloggen met sleutel';
case 'login.create': return 'Account aanmaken';
case 'login.error.invalid_key': return 'Ongeldige sleutel';
case 'live.start': return 'LIVE GAAN';
case 'live.configure_stream': return 'Stream configureren';
case 'live.endpoint': return 'Eindpunt';
case 'live.accept_tos': return 'TOS accepteren';
case 'live.balance_left': return ({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('nl'))(n,
zero: '',
other: '~${time}',
);
case 'live.title': return 'Titel';
case 'live.summary': return 'Samenvatting';
case 'live.image': return 'Afbeelding omslag';
case 'live.tags': return 'Tags';
case 'live.nsfw': return 'NSFW-inhoud';
case 'live.nsfw_description': return 'Controleer hier of deze stream naaktheid of pornografische inhoud bevat.';
case 'live.error.failed': return 'Stream mislukt';
case 'live.error.connection_error': return 'Fout bij verbinding';
case 'live.error.start_failed': return 'Stream start mislukt, controleer uw saldo';
default: return null;
}
}

View File

@ -51,6 +51,8 @@ class TranslationsPl extends Translations {
/// Anonimowy użytkownik
@override String get anon => 'Anon';
@override String full_amount_sats({required num n}) => '${NumberFormat.decimalPattern('pl').format(n)} sats';
/// Liczba widzów strumienia
@override String viewers({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('pl'))(n,
one: '1 przeglądarka',
@ -69,6 +71,7 @@ class TranslationsPl extends Translations {
@override late final _TranslationsProfilePl profile = _TranslationsProfilePl._(_root);
@override late final _TranslationsSettingsPl settings = _TranslationsSettingsPl._(_root);
@override late final _TranslationsLoginPl login = _TranslationsLoginPl._(_root);
@override late final _TranslationsLivePl live = _TranslationsLivePl._(_root);
}
// Path: stream
@ -80,6 +83,7 @@ class _TranslationsStreamPl extends TranslationsStreamEn {
// Translations
@override late final _TranslationsStreamStatusPl status = _TranslationsStreamStatusPl._(_root);
@override String started({required Object timestamp}) => 'Start ${timestamp}';
@override String notification({required Object name}) => '${name} został uruchomiony!';
@override late final _TranslationsStreamChatPl chat = _TranslationsStreamChatPl._(_root);
}
@ -204,6 +208,30 @@ class _TranslationsLoginPl extends TranslationsLoginEn {
@override late final _TranslationsLoginErrorPl error = _TranslationsLoginErrorPl._(_root);
}
// Path: live
class _TranslationsLivePl extends TranslationsLiveEn {
_TranslationsLivePl._(TranslationsPl root) : this._root = root, super.internal(root);
final TranslationsPl _root; // ignore: unused_field
// Translations
@override String get start => 'GO LIVE';
@override String get configure_stream => 'Konfiguracja strumienia';
@override String get endpoint => 'Punkt końcowy';
@override String get accept_tos => 'Zaakceptuj Regulamin';
@override String balance_left({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('pl'))(n,
zero: '',
other: '~${time}',
);
@override String get title => 'Tytuł';
@override String get summary => 'Podsumowanie';
@override String get image => 'Obraz na okładce';
@override String get tags => 'Tagi';
@override String get nsfw => 'Treści NSFW';
@override String get nsfw_description => 'Sprawdź tutaj, czy ten stream zawiera nagość lub treści pornograficzne.';
@override late final _TranslationsLiveErrorPl error = _TranslationsLiveErrorPl._(_root);
}
// Path: stream.status
class _TranslationsStreamStatusPl extends TranslationsStreamStatusEn {
_TranslationsStreamStatusPl._(TranslationsPl root) : this._root = root, super.internal(root);
@ -288,6 +316,8 @@ class _TranslationsSettingsWalletPl extends TranslationsSettingsWalletEn {
@override String get disconnect_wallet => 'Odłącz portfel';
@override String get connect_1tap => 'Połączenie 1-wtykowe';
@override String get paste => 'Wklej adres URL';
@override String get balance => 'Równowaga';
@override String get name => 'Portfel';
@override late final _TranslationsSettingsWalletErrorPl error = _TranslationsSettingsWalletErrorPl._(_root);
}
@ -301,6 +331,18 @@ class _TranslationsLoginErrorPl extends TranslationsLoginErrorEn {
@override String get invalid_key => 'Nieprawidłowy klucz';
}
// Path: live.error
class _TranslationsLiveErrorPl extends TranslationsLiveErrorEn {
_TranslationsLiveErrorPl._(TranslationsPl root) : this._root = root, super.internal(root);
final TranslationsPl _root; // ignore: unused_field
// Translations
@override String get failed => 'Strumień nie powiódł się';
@override String get connection_error => 'Błąd połączenia';
@override String get start_failed => 'Uruchomienie strumienia nie powiodło się, sprawdź saldo';
}
// Path: stream.chat.write
class _TranslationsStreamChatWritePl extends TranslationsStreamChatWriteEn {
_TranslationsStreamChatWritePl._(TranslationsPl root) : this._root = root, super.internal(root);
@ -379,6 +421,7 @@ extension on TranslationsPl {
case 'most_zapped_streamers': return 'Większość zapped streamerów';
case 'no_user_found': return 'Nie znaleziono użytkownika';
case 'anon': return 'Anon';
case 'full_amount_sats': return ({required num n}) => '${NumberFormat.decimalPattern('pl').format(n)} sats';
case 'viewers': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('pl'))(n,
one: '1 przeglądarka',
other: '{n:decimalPattern} widzów',
@ -387,6 +430,7 @@ extension on TranslationsPl {
case 'stream.status.ended': return 'ZAKOŃCZONY';
case 'stream.status.planned': return 'PLANOWANE';
case 'stream.started': return ({required Object timestamp}) => 'Start ${timestamp}';
case 'stream.notification': return ({required Object name}) => '${name} został uruchomiony!';
case 'stream.chat.disabled': return 'CZAT WYŁĄCZONY';
case 'stream.chat.disabled_timeout': return ({required Object time}) => 'Upłynął limit czasu: ${time}';
case 'stream.chat.timeout': return ({required InlineSpan mod, required InlineSpan user, required InlineSpan time}) => TextSpan(children: [
@ -455,6 +499,8 @@ extension on TranslationsPl {
case 'settings.wallet.disconnect_wallet': return 'Odłącz portfel';
case 'settings.wallet.connect_1tap': return 'Połączenie 1-wtykowe';
case 'settings.wallet.paste': return 'Wklej adres URL';
case 'settings.wallet.balance': return 'Równowaga';
case 'settings.wallet.name': return 'Portfel';
case 'settings.wallet.error.logged_out': return 'Nie można połączyć portfela po wylogowaniu';
case 'settings.wallet.error.nwc_auth_event_not_found': return 'Nie znaleziono zdarzenia autoryzacji portfela';
case 'login.username': return 'Nazwa użytkownika';
@ -462,6 +508,23 @@ extension on TranslationsPl {
case 'login.key': return 'Logowanie za pomocą klucza';
case 'login.create': return 'Utwórz konto';
case 'login.error.invalid_key': return 'Nieprawidłowy klucz';
case 'live.start': return 'GO LIVE';
case 'live.configure_stream': return 'Konfiguracja strumienia';
case 'live.endpoint': return 'Punkt końcowy';
case 'live.accept_tos': return 'Zaakceptuj Regulamin';
case 'live.balance_left': return ({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('pl'))(n,
zero: '',
other: '~${time}',
);
case 'live.title': return 'Tytuł';
case 'live.summary': return 'Podsumowanie';
case 'live.image': return 'Obraz na okładce';
case 'live.tags': return 'Tagi';
case 'live.nsfw': return 'Treści NSFW';
case 'live.nsfw_description': return 'Sprawdź tutaj, czy ten stream zawiera nagość lub treści pornograficzne.';
case 'live.error.failed': return 'Strumień nie powiódł się';
case 'live.error.connection_error': return 'Błąd połączenia';
case 'live.error.start_failed': return 'Uruchomienie strumienia nie powiodło się, sprawdź saldo';
default: return null;
}
}

View File

@ -51,6 +51,8 @@ class TranslationsPt extends Translations {
/// Um usuário anônimo
@override String get anon => 'Anônimo';
@override String full_amount_sats({required num n}) => '${NumberFormat.decimalPattern('pt').format(n)} sats';
/// Número de espectadores da transmissão
@override String viewers({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('pt'))(n,
one: '1 visualizador',
@ -69,6 +71,7 @@ class TranslationsPt extends Translations {
@override late final _TranslationsProfilePt profile = _TranslationsProfilePt._(_root);
@override late final _TranslationsSettingsPt settings = _TranslationsSettingsPt._(_root);
@override late final _TranslationsLoginPt login = _TranslationsLoginPt._(_root);
@override late final _TranslationsLivePt live = _TranslationsLivePt._(_root);
}
// Path: stream
@ -80,6 +83,7 @@ class _TranslationsStreamPt extends TranslationsStreamEn {
// Translations
@override late final _TranslationsStreamStatusPt status = _TranslationsStreamStatusPt._(_root);
@override String started({required Object timestamp}) => 'Iniciado em ${timestamp}';
@override String notification({required Object name}) => '${name} foi ao ar!';
@override late final _TranslationsStreamChatPt chat = _TranslationsStreamChatPt._(_root);
}
@ -204,6 +208,30 @@ class _TranslationsLoginPt extends TranslationsLoginEn {
@override late final _TranslationsLoginErrorPt error = _TranslationsLoginErrorPt._(_root);
}
// Path: live
class _TranslationsLivePt extends TranslationsLiveEn {
_TranslationsLivePt._(TranslationsPt root) : this._root = root, super.internal(root);
final TranslationsPt _root; // ignore: unused_field
// Translations
@override String get start => 'GO LIVE';
@override String get configure_stream => 'Configurar fluxo';
@override String get endpoint => 'Ponto final';
@override String get accept_tos => 'Aceitar os Termos de Serviço';
@override String balance_left({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('pt'))(n,
zero: '',
other: '~${time}',
);
@override String get title => 'Título';
@override String get summary => 'Resumo';
@override String get image => 'Imagem da capa';
@override String get tags => 'Tags';
@override String get nsfw => 'Conteúdo NSFW';
@override String get nsfw_description => 'Verifique aqui se essa transmissão contém nudez ou conteúdo pornográfico.';
@override late final _TranslationsLiveErrorPt error = _TranslationsLiveErrorPt._(_root);
}
// Path: stream.status
class _TranslationsStreamStatusPt extends TranslationsStreamStatusEn {
_TranslationsStreamStatusPt._(TranslationsPt root) : this._root = root, super.internal(root);
@ -288,6 +316,8 @@ class _TranslationsSettingsWalletPt extends TranslationsSettingsWalletEn {
@override String get disconnect_wallet => 'Desconectar carteira';
@override String get connect_1tap => 'Conexão de 1 torneira';
@override String get paste => 'Colar URL';
@override String get balance => 'Equilíbrio';
@override String get name => 'Carteira';
@override late final _TranslationsSettingsWalletErrorPt error = _TranslationsSettingsWalletErrorPt._(_root);
}
@ -301,6 +331,18 @@ class _TranslationsLoginErrorPt extends TranslationsLoginErrorEn {
@override String get invalid_key => 'Chave inválida';
}
// Path: live.error
class _TranslationsLiveErrorPt extends TranslationsLiveErrorEn {
_TranslationsLiveErrorPt._(TranslationsPt root) : this._root = root, super.internal(root);
final TranslationsPt _root; // ignore: unused_field
// Translations
@override String get failed => 'O fluxo falhou';
@override String get connection_error => 'Erro de conexão';
@override String get start_failed => 'Falha no início do fluxo, verifique seu saldo';
}
// Path: stream.chat.write
class _TranslationsStreamChatWritePt extends TranslationsStreamChatWriteEn {
_TranslationsStreamChatWritePt._(TranslationsPt root) : this._root = root, super.internal(root);
@ -379,6 +421,7 @@ extension on TranslationsPt {
case 'most_zapped_streamers': return 'Streamers mais afetados';
case 'no_user_found': return 'Nenhum usuário encontrado';
case 'anon': return 'Anônimo';
case 'full_amount_sats': return ({required num n}) => '${NumberFormat.decimalPattern('pt').format(n)} sats';
case 'viewers': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('pt'))(n,
one: '1 visualizador',
other: '${NumberFormat.decimalPattern('pt').format(n)} espectadores',
@ -387,6 +430,7 @@ extension on TranslationsPt {
case 'stream.status.ended': return 'FINALIZADO';
case 'stream.status.planned': return 'PLANEJADO';
case 'stream.started': return ({required Object timestamp}) => 'Iniciado em ${timestamp}';
case 'stream.notification': return ({required Object name}) => '${name} foi ao ar!';
case 'stream.chat.disabled': return 'BATE-PAPO DESATIVADO';
case 'stream.chat.disabled_timeout': return ({required Object time}) => 'O tempo limite expira: ${time}';
case 'stream.chat.timeout': return ({required InlineSpan mod, required InlineSpan user, required InlineSpan time}) => TextSpan(children: [
@ -455,6 +499,8 @@ extension on TranslationsPt {
case 'settings.wallet.disconnect_wallet': return 'Desconectar carteira';
case 'settings.wallet.connect_1tap': return 'Conexão de 1 torneira';
case 'settings.wallet.paste': return 'Colar URL';
case 'settings.wallet.balance': return 'Equilíbrio';
case 'settings.wallet.name': return 'Carteira';
case 'settings.wallet.error.logged_out': return 'Não consigo conectar a carteira quando estou desconectado';
case 'settings.wallet.error.nwc_auth_event_not_found': return 'Nenhum evento de autenticação de carteira encontrado';
case 'login.username': return 'Nome de usuário';
@ -462,6 +508,23 @@ extension on TranslationsPt {
case 'login.key': return 'Login com chave';
case 'login.create': return 'Criar Conta';
case 'login.error.invalid_key': return 'Chave inválida';
case 'live.start': return 'GO LIVE';
case 'live.configure_stream': return 'Configurar fluxo';
case 'live.endpoint': return 'Ponto final';
case 'live.accept_tos': return 'Aceitar os Termos de Serviço';
case 'live.balance_left': return ({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('pt'))(n,
zero: '',
other: '~${time}',
);
case 'live.title': return 'Título';
case 'live.summary': return 'Resumo';
case 'live.image': return 'Imagem da capa';
case 'live.tags': return 'Tags';
case 'live.nsfw': return 'Conteúdo NSFW';
case 'live.nsfw_description': return 'Verifique aqui se essa transmissão contém nudez ou conteúdo pornográfico.';
case 'live.error.failed': return 'O fluxo falhou';
case 'live.error.connection_error': return 'Erro de conexão';
case 'live.error.start_failed': return 'Falha no início do fluxo, verifique seu saldo';
default: return null;
}
}

View File

@ -51,6 +51,8 @@ class TranslationsRo extends Translations {
/// Un utilizator anonim
@override String get anon => 'Anon';
@override String full_amount_sats({required num n}) => '${NumberFormat.decimalPattern('ro').format(n)} sats';
/// Numărul de telespectatori ai fluxului
@override String viewers({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ro'))(n,
one: '1 vizualizator',
@ -69,6 +71,7 @@ class TranslationsRo extends Translations {
@override late final _TranslationsProfileRo profile = _TranslationsProfileRo._(_root);
@override late final _TranslationsSettingsRo settings = _TranslationsSettingsRo._(_root);
@override late final _TranslationsLoginRo login = _TranslationsLoginRo._(_root);
@override late final _TranslationsLiveRo live = _TranslationsLiveRo._(_root);
}
// Path: stream
@ -80,6 +83,7 @@ class _TranslationsStreamRo extends TranslationsStreamEn {
// Translations
@override late final _TranslationsStreamStatusRo status = _TranslationsStreamStatusRo._(_root);
@override String started({required Object timestamp}) => 'A început ${timestamp}';
@override String notification({required Object name}) => '${name} a intrat în direct!';
@override late final _TranslationsStreamChatRo chat = _TranslationsStreamChatRo._(_root);
}
@ -204,6 +208,30 @@ class _TranslationsLoginRo extends TranslationsLoginEn {
@override late final _TranslationsLoginErrorRo error = _TranslationsLoginErrorRo._(_root);
}
// Path: live
class _TranslationsLiveRo extends TranslationsLiveEn {
_TranslationsLiveRo._(TranslationsRo root) : this._root = root, super.internal(root);
final TranslationsRo _root; // ignore: unused_field
// Translations
@override String get start => 'GO LIVE';
@override String get configure_stream => 'Configurați fluxul';
@override String get endpoint => 'Punct final';
@override String get accept_tos => 'Acceptați TOS';
@override String balance_left({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ro'))(n,
zero: '',
other: '~${time}',
);
@override String get title => 'Titlu';
@override String get summary => 'Rezumat';
@override String get image => 'Imagine de copertă';
@override String get tags => 'Etichete';
@override String get nsfw => 'Conținut NSFW';
@override String get nsfw_description => 'Bifați aici dacă acest flux conține nuditate sau conținut pornografic.';
@override late final _TranslationsLiveErrorRo error = _TranslationsLiveErrorRo._(_root);
}
// Path: stream.status
class _TranslationsStreamStatusRo extends TranslationsStreamStatusEn {
_TranslationsStreamStatusRo._(TranslationsRo root) : this._root = root, super.internal(root);
@ -288,6 +316,8 @@ class _TranslationsSettingsWalletRo extends TranslationsSettingsWalletEn {
@override String get disconnect_wallet => 'Deconectați portofelul';
@override String get connect_1tap => 'Conexiune 1-Tap';
@override String get paste => 'Lipiți URL';
@override String get balance => 'Echilibru';
@override String get name => 'Portofel';
@override late final _TranslationsSettingsWalletErrorRo error = _TranslationsSettingsWalletErrorRo._(_root);
}
@ -301,6 +331,18 @@ class _TranslationsLoginErrorRo extends TranslationsLoginErrorEn {
@override String get invalid_key => 'Cheie invalidă';
}
// Path: live.error
class _TranslationsLiveErrorRo extends TranslationsLiveErrorEn {
_TranslationsLiveErrorRo._(TranslationsRo root) : this._root = root, super.internal(root);
final TranslationsRo _root; // ignore: unused_field
// Translations
@override String get failed => 'Fluxul a eșuat';
@override String get connection_error => 'Eroare de conectare';
@override String get start_failed => 'Pornirea fluxului a eșuat, vă rugăm să verificați soldul';
}
// Path: stream.chat.write
class _TranslationsStreamChatWriteRo extends TranslationsStreamChatWriteEn {
_TranslationsStreamChatWriteRo._(TranslationsRo root) : this._root = root, super.internal(root);
@ -379,6 +421,7 @@ extension on TranslationsRo {
case 'most_zapped_streamers': return 'Cele mai multe Streamers Zapped';
case 'no_user_found': return 'Niciun utilizator găsit';
case 'anon': return 'Anon';
case 'full_amount_sats': return ({required num n}) => '${NumberFormat.decimalPattern('ro').format(n)} sats';
case 'viewers': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ro'))(n,
one: '1 vizualizator',
other: '${NumberFormat.decimalPattern('ro').format(n)} telespectatori',
@ -387,6 +430,7 @@ extension on TranslationsRo {
case 'stream.status.ended': return 'TERMINAT';
case 'stream.status.planned': return 'PLANIFICATE';
case 'stream.started': return ({required Object timestamp}) => 'A început ${timestamp}';
case 'stream.notification': return ({required Object name}) => '${name} a intrat în direct!';
case 'stream.chat.disabled': return 'CHAT DEZACTIVAT';
case 'stream.chat.disabled_timeout': return ({required Object time}) => 'Timpul expiră: ${time}';
case 'stream.chat.timeout': return ({required InlineSpan mod, required InlineSpan user, required InlineSpan time}) => TextSpan(children: [
@ -455,6 +499,8 @@ extension on TranslationsRo {
case 'settings.wallet.disconnect_wallet': return 'Deconectați portofelul';
case 'settings.wallet.connect_1tap': return 'Conexiune 1-Tap';
case 'settings.wallet.paste': return 'Lipiți URL';
case 'settings.wallet.balance': return 'Echilibru';
case 'settings.wallet.name': return 'Portofel';
case 'settings.wallet.error.logged_out': return 'Nu puteți conecta portofelul atunci când sunteți deconectat';
case 'settings.wallet.error.nwc_auth_event_not_found': return 'Nu a fost găsit niciun eveniment de autorizare a portofelului';
case 'login.username': return 'Nume utilizator';
@ -462,6 +508,23 @@ extension on TranslationsRo {
case 'login.key': return 'Autentificare cu cheie';
case 'login.create': return 'Creare cont';
case 'login.error.invalid_key': return 'Cheie invalidă';
case 'live.start': return 'GO LIVE';
case 'live.configure_stream': return 'Configurați fluxul';
case 'live.endpoint': return 'Punct final';
case 'live.accept_tos': return 'Acceptați TOS';
case 'live.balance_left': return ({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ro'))(n,
zero: '',
other: '~${time}',
);
case 'live.title': return 'Titlu';
case 'live.summary': return 'Rezumat';
case 'live.image': return 'Imagine de copertă';
case 'live.tags': return 'Etichete';
case 'live.nsfw': return 'Conținut NSFW';
case 'live.nsfw_description': return 'Bifați aici dacă acest flux conține nuditate sau conținut pornografic.';
case 'live.error.failed': return 'Fluxul a eșuat';
case 'live.error.connection_error': return 'Eroare de conectare';
case 'live.error.start_failed': return 'Pornirea fluxului a eșuat, vă rugăm să verificați soldul';
default: return null;
}
}

View File

@ -51,6 +51,8 @@ class TranslationsRu extends Translations {
/// Анонимный пользователь
@override String get anon => 'Аноним';
@override String full_amount_sats({required num n}) => '${NumberFormat.decimalPattern('ru').format(n)} sats';
/// Количество зрителей потока
@override String viewers({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ru'))(n,
one: '1 зритель',
@ -69,6 +71,7 @@ class TranslationsRu extends Translations {
@override late final _TranslationsProfileRu profile = _TranslationsProfileRu._(_root);
@override late final _TranslationsSettingsRu settings = _TranslationsSettingsRu._(_root);
@override late final _TranslationsLoginRu login = _TranslationsLoginRu._(_root);
@override late final _TranslationsLiveRu live = _TranslationsLiveRu._(_root);
}
// Path: stream
@ -80,6 +83,7 @@ class _TranslationsStreamRu extends TranslationsStreamEn {
// Translations
@override late final _TranslationsStreamStatusRu status = _TranslationsStreamStatusRu._(_root);
@override String started({required Object timestamp}) => 'Начало ${timestamp}';
@override String notification({required Object name}) => '${name} запустился!';
@override late final _TranslationsStreamChatRu chat = _TranslationsStreamChatRu._(_root);
}
@ -204,6 +208,30 @@ class _TranslationsLoginRu extends TranslationsLoginEn {
@override late final _TranslationsLoginErrorRu error = _TranslationsLoginErrorRu._(_root);
}
// Path: live
class _TranslationsLiveRu extends TranslationsLiveEn {
_TranslationsLiveRu._(TranslationsRu root) : this._root = root, super.internal(root);
final TranslationsRu _root; // ignore: unused_field
// Translations
@override String get start => 'ПЕРЕЙТИ В ПРЯМОЙ ЭФИР';
@override String get configure_stream => 'Настроить поток';
@override String get endpoint => 'Конечная точка';
@override String get accept_tos => 'Принять TOS';
@override String balance_left({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ru'))(n,
zero: '',
other: '~${time}',
);
@override String get title => 'Название';
@override String get summary => 'Резюме';
@override String get image => 'Изображение на обложке';
@override String get tags => 'Теги';
@override String get nsfw => 'NSFW-контент';
@override String get nsfw_description => 'Отметьте здесь, если этот поток содержит наготу или порнографические материалы.';
@override late final _TranslationsLiveErrorRu error = _TranslationsLiveErrorRu._(_root);
}
// Path: stream.status
class _TranslationsStreamStatusRu extends TranslationsStreamStatusEn {
_TranslationsStreamStatusRu._(TranslationsRu root) : this._root = root, super.internal(root);
@ -288,6 +316,8 @@ class _TranslationsSettingsWalletRu extends TranslationsSettingsWalletEn {
@override String get disconnect_wallet => 'Отключить кошелек';
@override String get connect_1tap => '1-кратное соединение';
@override String get paste => 'Вставить URL';
@override String get balance => 'Баланс';
@override String get name => 'Кошелек';
@override late final _TranslationsSettingsWalletErrorRu error = _TranslationsSettingsWalletErrorRu._(_root);
}
@ -301,6 +331,18 @@ class _TranslationsLoginErrorRu extends TranslationsLoginErrorEn {
@override String get invalid_key => 'Неверный ключ';
}
// Path: live.error
class _TranslationsLiveErrorRu extends TranslationsLiveErrorEn {
_TranslationsLiveErrorRu._(TranslationsRu root) : this._root = root, super.internal(root);
final TranslationsRu _root; // ignore: unused_field
// Translations
@override String get failed => 'Сбой потока';
@override String get connection_error => 'Ошибка подключения';
@override String get start_failed => 'Запуск потока не удался, пожалуйста, проверьте баланс';
}
// Path: stream.chat.write
class _TranslationsStreamChatWriteRu extends TranslationsStreamChatWriteEn {
_TranslationsStreamChatWriteRu._(TranslationsRu root) : this._root = root, super.internal(root);
@ -379,6 +421,7 @@ extension on TranslationsRu {
case 'most_zapped_streamers': return 'Самые прыткие стримеры';
case 'no_user_found': return 'Пользователь не найден';
case 'anon': return 'Аноним';
case 'full_amount_sats': return ({required num n}) => '${NumberFormat.decimalPattern('ru').format(n)} sats';
case 'viewers': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ru'))(n,
one: '1 зритель',
other: '${NumberFormat.decimalPattern('ru').format(n)} зрителей',
@ -387,6 +430,7 @@ extension on TranslationsRu {
case 'stream.status.ended': return 'КОНЕЦ';
case 'stream.status.planned': return 'ПЛАНИРУЕМЫЙ';
case 'stream.started': return ({required Object timestamp}) => 'Начало ${timestamp}';
case 'stream.notification': return ({required Object name}) => '${name} запустился!';
case 'stream.chat.disabled': return 'ЧАТ ОТКЛЮЧЕН';
case 'stream.chat.disabled_timeout': return ({required Object time}) => 'Таймаут истекает: ${time}';
case 'stream.chat.timeout': return ({required InlineSpan mod, required InlineSpan user, required InlineSpan time}) => TextSpan(children: [
@ -455,6 +499,8 @@ extension on TranslationsRu {
case 'settings.wallet.disconnect_wallet': return 'Отключить кошелек';
case 'settings.wallet.connect_1tap': return '1-кратное соединение';
case 'settings.wallet.paste': return 'Вставить URL';
case 'settings.wallet.balance': return 'Баланс';
case 'settings.wallet.name': return 'Кошелек';
case 'settings.wallet.error.logged_out': return 'Невозможно подключить кошелек при выходе из системы';
case 'settings.wallet.error.nwc_auth_event_not_found': return 'Событие аутентификации кошелька не найдено';
case 'login.username': return 'Имя пользователя';
@ -462,6 +508,23 @@ extension on TranslationsRu {
case 'login.key': return 'Вход в систему с помощью ключа';
case 'login.create': return 'Создать аккаунт';
case 'login.error.invalid_key': return 'Неверный ключ';
case 'live.start': return 'ПЕРЕЙТИ В ПРЯМОЙ ЭФИР';
case 'live.configure_stream': return 'Настроить поток';
case 'live.endpoint': return 'Конечная точка';
case 'live.accept_tos': return 'Принять TOS';
case 'live.balance_left': return ({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('ru'))(n,
zero: '',
other: '~${time}',
);
case 'live.title': return 'Название';
case 'live.summary': return 'Резюме';
case 'live.image': return 'Изображение на обложке';
case 'live.tags': return 'Теги';
case 'live.nsfw': return 'NSFW-контент';
case 'live.nsfw_description': return 'Отметьте здесь, если этот поток содержит наготу или порнографические материалы.';
case 'live.error.failed': return 'Сбой потока';
case 'live.error.connection_error': return 'Ошибка подключения';
case 'live.error.start_failed': return 'Запуск потока не удался, пожалуйста, проверьте баланс';
default: return null;
}
}

View File

@ -39,19 +39,21 @@ class TranslationsSv extends Translations {
// Translations
/// Text som uppmanar användaren att trycka på avatarplatshållaren för att påbörja uppladdningen
/// Text som uppmanar användaren att trycka på avatar platshållaren för att påbörja uppladdningen
@override String get upload_avatar => 'Ladda upp avatar';
/// Rubrik över listade toppstreamers av zaps
@override String get most_zapped_streamers => 'De flesta zappade streamers';
/// Rubrik över listade topp streamers av zaps
@override String get most_zapped_streamers => 'De flest zappade streamers';
/// Ingen användare hittades vid sökning
@override String get no_user_found => 'Ingen användare hittades';
/// En anonym användare
@override String get anon => 'Anon';
@override String get anon => 'Anno';
/// Antal tittare på streamingen
@override String full_amount_sats({required num n}) => '${NumberFormat.decimalPattern('sv').format(n)} sats';
/// Antal tittare på strömmingen
@override String viewers({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('sv'))(n,
one: '1 tittare',
other: '${NumberFormat.decimalPattern('sv').format(n)} tittare',
@ -69,6 +71,7 @@ class TranslationsSv extends Translations {
@override late final _TranslationsProfileSv profile = _TranslationsProfileSv._(_root);
@override late final _TranslationsSettingsSv settings = _TranslationsSettingsSv._(_root);
@override late final _TranslationsLoginSv login = _TranslationsLoginSv._(_root);
@override late final _TranslationsLiveSv live = _TranslationsLiveSv._(_root);
}
// Path: stream
@ -79,7 +82,8 @@ class _TranslationsStreamSv extends TranslationsStreamEn {
// Translations
@override late final _TranslationsStreamStatusSv status = _TranslationsStreamStatusSv._(_root);
@override String started({required Object timestamp}) => 'Startade ${timestamp}';
@override String started({required Object timestamp}) => 'Startad ${timestamp}';
@override String notification({required Object name}) => '${name} gick live!';
@override late final _TranslationsStreamChatSv chat = _TranslationsStreamChatSv._(_root);
}
@ -112,7 +116,7 @@ class _TranslationsButtonSv extends TranslationsButtonEn {
/// Knapptext för följ-knappen
@override String get follow => 'Följ';
/// Knapptext för avföljningsknappen
/// Knapptext för sluta följa knappen
@override String get unfollow => 'Sluta följa';
@override String get mute => 'Tysta';
@ -204,6 +208,30 @@ class _TranslationsLoginSv extends TranslationsLoginEn {
@override late final _TranslationsLoginErrorSv error = _TranslationsLoginErrorSv._(_root);
}
// Path: live
class _TranslationsLiveSv extends TranslationsLiveEn {
_TranslationsLiveSv._(TranslationsSv root) : this._root = root, super.internal(root);
final TranslationsSv _root; // ignore: unused_field
// Translations
@override String get start => 'GÅ DIREKT';
@override String get configure_stream => 'Konfigurera ström';
@override String get endpoint => 'Slutpunkt';
@override String get accept_tos => 'Acceptera TOS';
@override String balance_left({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('sv'))(n,
zero: '',
other: '~${time}',
);
@override String get title => 'Titel';
@override String get summary => 'Sammanfattning';
@override String get image => 'Omslagsbild';
@override String get tags => 'Etiketter';
@override String get nsfw => 'NSFW-innehåll';
@override String get nsfw_description => 'Markera här om denna stream innehåller nakenhet eller pornografiskt innehåll.';
@override late final _TranslationsLiveErrorSv error = _TranslationsLiveErrorSv._(_root);
}
// Path: stream.status
class _TranslationsStreamStatusSv extends TranslationsStreamStatusEn {
_TranslationsStreamStatusSv._(TranslationsSv root) : this._root = root, super.internal(root);
@ -235,7 +263,7 @@ class _TranslationsStreamChatSv extends TranslationsStreamChatEn {
time,
]);
/// Stream avslutade sidfoten längst ner på chatten
/// Streama slutade sidfot längst ned i chatten
@override String get ended => 'STREAM AVSLUTAD';
/// Chattmeddelande som visar strömavbrott
@ -272,8 +300,8 @@ class _TranslationsSettingsProfileSv extends TranslationsSettingsProfileEn {
// Translations
@override String get display_name => 'Visa namn';
@override String get about => 'Om';
@override String get nip05 => 'Nostr Adress';
@override String get lud16 => 'Adress för blixtnedslag';
@override String get nip05 => 'Nostr adress';
@override String get lud16 => 'Lightning-adress';
@override late final _TranslationsSettingsProfileErrorSv error = _TranslationsSettingsProfileErrorSv._(_root);
}
@ -284,10 +312,12 @@ class _TranslationsSettingsWalletSv extends TranslationsSettingsWalletEn {
final TranslationsSv _root; // ignore: unused_field
// Translations
@override String get connect_wallet => 'Connect plånbok (NWC nostr+walletconnect://)';
@override String get connect_wallet => 'Anslut plånbok (NWC nostr+walletconnect://)';
@override String get disconnect_wallet => 'Koppla bort plånboken';
@override String get connect_1tap => '1-Tap-anslutning';
@override String get connect_1tap => '1-tryck anslutning';
@override String get paste => 'Klistra in URL';
@override String get balance => 'Balans';
@override String get name => 'Plånbok';
@override late final _TranslationsSettingsWalletErrorSv error = _TranslationsSettingsWalletErrorSv._(_root);
}
@ -301,6 +331,18 @@ class _TranslationsLoginErrorSv extends TranslationsLoginErrorEn {
@override String get invalid_key => 'Ogiltig nyckel';
}
// Path: live.error
class _TranslationsLiveErrorSv extends TranslationsLiveErrorEn {
_TranslationsLiveErrorSv._(TranslationsSv root) : this._root = root, super.internal(root);
final TranslationsSv _root; // ignore: unused_field
// Translations
@override String get failed => 'Strömmen misslyckades';
@override String get connection_error => 'Anslutningsfel';
@override String get start_failed => 'Stream start misslyckades, vänligen kontrollera ditt saldo';
}
// Path: stream.chat.write
class _TranslationsStreamChatWriteSv extends TranslationsStreamChatWriteEn {
_TranslationsStreamChatWriteSv._(TranslationsSv root) : this._root = root, super.internal(root);
@ -312,8 +354,8 @@ class _TranslationsStreamChatWriteSv extends TranslationsStreamChatWriteEn {
/// Etikett på inmatningsrutan för chattmeddelanden
@override String get label => 'Skriv meddelande';
/// Chattinmatningsmeddelande som visas när användaren endast är inloggad med pubkey
@override String get no_signer => 'Det går inte att skriva meddelanden med npub-inloggning';
/// Chattinmatningsmeddelande som visas när användaren endast är inloggad med publik nyckel
@override String get no_signer => 'Det går inte att skriva meddelanden med n-pub inloggning';
/// Chattinmatningsmeddelande som visas när användaren är utloggad
@override String get login => 'Logga in för att skicka meddelanden';
@ -327,7 +369,7 @@ class _TranslationsStreamChatBadgeSv extends TranslationsStreamChatBadgeEn {
// Translations
/// Rubrik över lista över användare som tilldelats en badge
/// Rubrik över listan över användare som tilldelas ett märke
@override String get awarded_to => 'Tilldelas till:';
}
@ -339,14 +381,14 @@ class _TranslationsStreamChatRaidSv extends TranslationsStreamChatRaidEn {
// Translations
/// Chatta raidmeddelande till en annan ström
/// Chatt raid meddelande till en annan ström
@override String to({required Object name}) => 'RAIDING ${name}';
/// Chat raid-meddelande från en annan ström
/// Chatt raid meddelande från en annan ström
@override String from({required Object name}) => 'RAID FRÅN ${name}';
/// Nedräkningstimer för auto-raiding
@override String countdown({required Object time}) => 'Raiding ${time}';
/// Nedräkningstimer för auto- radiering
@override String countdown({required Object time}) => 'Radiering i ${time}';
}
// Path: settings.profile.error
@ -366,7 +408,7 @@ class _TranslationsSettingsWalletErrorSv extends TranslationsSettingsWalletError
final TranslationsSv _root; // ignore: unused_field
// Translations
@override String get logged_out => 'Kan inte ansluta plånbok när du är inloggad';
@override String get logged_out => 'Kan inte ansluta plånbok när du är utloggad';
@override String get nwc_auth_event_not_found => 'Inget autentiseringshändelse för plånbok hittades';
}
@ -376,9 +418,10 @@ extension on TranslationsSv {
dynamic _flatMapFunction(String path) {
switch (path) {
case 'upload_avatar': return 'Ladda upp avatar';
case 'most_zapped_streamers': return 'De flesta zappade streamers';
case 'most_zapped_streamers': return 'De flest zappade streamers';
case 'no_user_found': return 'Ingen användare hittades';
case 'anon': return 'Anon';
case 'anon': return 'Anno';
case 'full_amount_sats': return ({required num n}) => '${NumberFormat.decimalPattern('sv').format(n)} sats';
case 'viewers': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('sv'))(n,
one: '1 tittare',
other: '${NumberFormat.decimalPattern('sv').format(n)} tittare',
@ -386,7 +429,8 @@ extension on TranslationsSv {
case 'stream.status.live': return 'LIVE';
case 'stream.status.ended': return 'AVSLUTAD';
case 'stream.status.planned': return 'PLANERADE';
case 'stream.started': return ({required Object timestamp}) => 'Startade ${timestamp}';
case 'stream.started': return ({required Object timestamp}) => 'Startad ${timestamp}';
case 'stream.notification': return ({required Object name}) => '${name} gick live!';
case 'stream.chat.disabled': return 'CHAT AVSTÄNGD';
case 'stream.chat.disabled_timeout': return ({required Object time}) => 'Tidsgränsen går ut: ${time}';
case 'stream.chat.timeout': return ({required InlineSpan mod, required InlineSpan user, required InlineSpan time}) => TextSpan(children: [
@ -404,12 +448,12 @@ extension on TranslationsSv {
const TextSpan(text: ' sats'),
]);
case 'stream.chat.write.label': return 'Skriv meddelande';
case 'stream.chat.write.no_signer': return 'Det går inte att skriva meddelanden med npub-inloggning';
case 'stream.chat.write.no_signer': return 'Det går inte att skriva meddelanden med n-pub inloggning';
case 'stream.chat.write.login': return 'Logga in för att skicka meddelanden';
case 'stream.chat.badge.awarded_to': return 'Tilldelas till:';
case 'stream.chat.raid.to': return ({required Object name}) => 'RAIDING ${name}';
case 'stream.chat.raid.from': return ({required Object name}) => 'RAID FRÅN ${name}';
case 'stream.chat.raid.countdown': return ({required Object time}) => 'Raiding ${time}';
case 'stream.chat.raid.countdown': return ({required Object time}) => 'Radiering i ${time}';
case 'goal.title': return ({required Object amount}) => 'Mål: ${amount}';
case 'goal.remaining': return ({required Object amount}) => 'Kvarvarande: ${amount}';
case 'goal.complete': return 'KOMPLETT';
@ -448,20 +492,39 @@ extension on TranslationsSv {
case 'settings.button_wallet': return 'Inställningar för plånbok';
case 'settings.profile.display_name': return 'Visa namn';
case 'settings.profile.about': return 'Om';
case 'settings.profile.nip05': return 'Nostr Adress';
case 'settings.profile.lud16': return 'Adress för blixtnedslag';
case 'settings.profile.nip05': return 'Nostr adress';
case 'settings.profile.lud16': return 'Lightning-adress';
case 'settings.profile.error.logged_out': return 'Kan inte redigera profil när jag är utloggad';
case 'settings.wallet.connect_wallet': return 'Connect plånbok (NWC nostr+walletconnect://)';
case 'settings.wallet.connect_wallet': return 'Anslut plånbok (NWC nostr+walletconnect://)';
case 'settings.wallet.disconnect_wallet': return 'Koppla bort plånboken';
case 'settings.wallet.connect_1tap': return '1-Tap-anslutning';
case 'settings.wallet.connect_1tap': return '1-tryck anslutning';
case 'settings.wallet.paste': return 'Klistra in URL';
case 'settings.wallet.error.logged_out': return 'Kan inte ansluta plånbok när du är inloggad';
case 'settings.wallet.balance': return 'Balans';
case 'settings.wallet.name': return 'Plånbok';
case 'settings.wallet.error.logged_out': return 'Kan inte ansluta plånbok när du är utloggad';
case 'settings.wallet.error.nwc_auth_event_not_found': return 'Inget autentiseringshändelse för plånbok hittades';
case 'login.username': return 'Användarnamn';
case 'login.amber': return 'Logga in med Amber';
case 'login.key': return 'Logga in med nyckel';
case 'login.create': return 'Skapa konto';
case 'login.error.invalid_key': return 'Ogiltig nyckel';
case 'live.start': return 'GÅ DIREKT';
case 'live.configure_stream': return 'Konfigurera ström';
case 'live.endpoint': return 'Slutpunkt';
case 'live.accept_tos': return 'Acceptera TOS';
case 'live.balance_left': return ({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('sv'))(n,
zero: '',
other: '~${time}',
);
case 'live.title': return 'Titel';
case 'live.summary': return 'Sammanfattning';
case 'live.image': return 'Omslagsbild';
case 'live.tags': return 'Etiketter';
case 'live.nsfw': return 'NSFW-innehåll';
case 'live.nsfw_description': return 'Markera här om denna stream innehåller nakenhet eller pornografiskt innehåll.';
case 'live.error.failed': return 'Strömmen misslyckades';
case 'live.error.connection_error': return 'Anslutningsfel';
case 'live.error.start_failed': return 'Stream start misslyckades, vänligen kontrollera ditt saldo';
default: return null;
}
}

View File

@ -80,6 +80,7 @@ class _TranslationsStreamTr extends TranslationsStreamEn {
// Translations
@override late final _TranslationsStreamStatusTr status = _TranslationsStreamStatusTr._(_root);
@override String started({required Object timestamp}) => 'Başlatıldı ${timestamp}';
@override String notification({required Object name}) => '${name} yayına girdi!';
@override late final _TranslationsStreamChatTr chat = _TranslationsStreamChatTr._(_root);
}
@ -231,8 +232,9 @@ class _TranslationsStreamChatTr extends TranslationsStreamChatEn {
mod,
const TextSpan(text: ' zaman aşımına uğradı '),
user,
const TextSpan(text: ' için '),
const TextSpan(text: ' '),
time,
const TextSpan(text: 'için'),
]);
/// Sohbetin alt kısmında akış sona erdi altbilgisi
@ -343,7 +345,7 @@ class _TranslationsStreamChatRaidTr extends TranslationsStreamChatRaidEn {
@override String to({required Object name}) => 'RAIDING ${name}';
/// Başka bir akıştan sohbet baskını mesajı
@override String from({required Object name}) => '${name}ADRESINDEN RAID';
@override String from({required Object name}) => '${name} ADRESINDEN RAID';
/// Otomatik sürüş için geri sayım sayacı
@override String countdown({required Object time}) => '${time}adresinde baskın';
@ -387,14 +389,16 @@ extension on TranslationsTr {
case 'stream.status.ended': return 'SONLANDI';
case 'stream.status.planned': return 'PLANLANMIŞ';
case 'stream.started': return ({required Object timestamp}) => 'Başlatıldı ${timestamp}';
case 'stream.notification': return ({required Object name}) => '${name} yayına girdi!';
case 'stream.chat.disabled': return 'SOHBET DEVRE DIŞI';
case 'stream.chat.disabled_timeout': return ({required Object time}) => 'Zaman aşımı sona eriyor: ${time}';
case 'stream.chat.timeout': return ({required InlineSpan mod, required InlineSpan user, required InlineSpan time}) => TextSpan(children: [
mod,
const TextSpan(text: ' zaman aşımına uğradı '),
user,
const TextSpan(text: ' için '),
const TextSpan(text: ' '),
time,
const TextSpan(text: 'için'),
]);
case 'stream.chat.ended': return 'YAYIN SONLANDI';
case 'stream.chat.zap': return ({required InlineSpan user, required InlineSpan amount}) => TextSpan(children: [
@ -408,7 +412,7 @@ extension on TranslationsTr {
case 'stream.chat.write.login': return 'Mesaj göndermek için lütfen giriş yapın';
case 'stream.chat.badge.awarded_to': return 'Ödüllendirildi:';
case 'stream.chat.raid.to': return ({required Object name}) => 'RAIDING ${name}';
case 'stream.chat.raid.from': return ({required Object name}) => '${name}ADRESINDEN RAID';
case 'stream.chat.raid.from': return ({required Object name}) => '${name} ADRESINDEN RAID';
case 'stream.chat.raid.countdown': return ({required Object time}) => '${time}adresinde baskın';
case 'goal.title': return ({required Object amount}) => 'Hedef: ${amount}';
case 'goal.remaining': return ({required Object amount}) => 'Kalan: ${amount}';

View File

@ -51,6 +51,8 @@ class TranslationsUk extends Translations {
/// Анонімний користувач
@override String get anon => 'Анонім.';
@override String full_amount_sats({required num n}) => '${NumberFormat.decimalPattern('uk').format(n)} сатів';
/// Кількість глядачів стріму
@override String viewers({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('uk'))(n,
one: '1 глядач',
@ -69,6 +71,7 @@ class TranslationsUk extends Translations {
@override late final _TranslationsProfileUk profile = _TranslationsProfileUk._(_root);
@override late final _TranslationsSettingsUk settings = _TranslationsSettingsUk._(_root);
@override late final _TranslationsLoginUk login = _TranslationsLoginUk._(_root);
@override late final _TranslationsLiveUk live = _TranslationsLiveUk._(_root);
}
// Path: stream
@ -80,6 +83,7 @@ class _TranslationsStreamUk extends TranslationsStreamEn {
// Translations
@override late final _TranslationsStreamStatusUk status = _TranslationsStreamStatusUk._(_root);
@override String started({required Object timestamp}) => 'Запустив ${timestamp}';
@override String notification({required Object name}) => '${name} запрацював!';
@override late final _TranslationsStreamChatUk chat = _TranslationsStreamChatUk._(_root);
}
@ -204,6 +208,30 @@ class _TranslationsLoginUk extends TranslationsLoginEn {
@override late final _TranslationsLoginErrorUk error = _TranslationsLoginErrorUk._(_root);
}
// Path: live
class _TranslationsLiveUk extends TranslationsLiveEn {
_TranslationsLiveUk._(TranslationsUk root) : this._root = root, super.internal(root);
final TranslationsUk _root; // ignore: unused_field
// Translations
@override String get start => 'GO LIVE';
@override String get configure_stream => 'Налаштувати потік';
@override String get endpoint => 'Кінцева точка';
@override String get accept_tos => 'Прийміть ТЗ';
@override String balance_left({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('uk'))(n,
zero: '',
other: '~${time}',
);
@override String get title => 'Назва';
@override String get summary => 'Підсумок';
@override String get image => 'Зображення обкладинки';
@override String get tags => 'Теги';
@override String get nsfw => 'Нецензурна лексика';
@override String get nsfw_description => 'Перевірте тут, чи містить цей потік оголену натуру або порнографічний контент.';
@override late final _TranslationsLiveErrorUk error = _TranslationsLiveErrorUk._(_root);
}
// Path: stream.status
class _TranslationsStreamStatusUk extends TranslationsStreamStatusEn {
_TranslationsStreamStatusUk._(TranslationsUk root) : this._root = root, super.internal(root);
@ -288,6 +316,8 @@ class _TranslationsSettingsWalletUk extends TranslationsSettingsWalletEn {
@override String get disconnect_wallet => 'Відключити гаманець';
@override String get connect_1tap => 'Підключення в 1 кран';
@override String get paste => 'Вставити URL-адресу';
@override String get balance => 'Баланс';
@override String get name => 'Гаманець';
@override late final _TranslationsSettingsWalletErrorUk error = _TranslationsSettingsWalletErrorUk._(_root);
}
@ -301,6 +331,18 @@ class _TranslationsLoginErrorUk extends TranslationsLoginErrorEn {
@override String get invalid_key => 'Неправильний ключ';
}
// Path: live.error
class _TranslationsLiveErrorUk extends TranslationsLiveErrorEn {
_TranslationsLiveErrorUk._(TranslationsUk root) : this._root = root, super.internal(root);
final TranslationsUk _root; // ignore: unused_field
// Translations
@override String get failed => 'Потік не вдалося запустити';
@override String get connection_error => 'Помилка з\'єднання';
@override String get start_failed => 'Не вдалося запустити трансляцію, будь ласка, перевірте свій баланс';
}
// Path: stream.chat.write
class _TranslationsStreamChatWriteUk extends TranslationsStreamChatWriteEn {
_TranslationsStreamChatWriteUk._(TranslationsUk root) : this._root = root, super.internal(root);
@ -379,6 +421,7 @@ extension on TranslationsUk {
case 'most_zapped_streamers': return 'Більшість стримерів, які були під напругою';
case 'no_user_found': return 'Користувача не знайдено';
case 'anon': return 'Анонім.';
case 'full_amount_sats': return ({required num n}) => '${NumberFormat.decimalPattern('uk').format(n)} сатів';
case 'viewers': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('uk'))(n,
one: '1 глядач',
other: '${NumberFormat.decimalPattern('uk').format(n)} глядачів',
@ -387,6 +430,7 @@ extension on TranslationsUk {
case 'stream.status.ended': return 'ЗАКІНЧЕНО';
case 'stream.status.planned': return 'ЗАПЛАНОВАНО';
case 'stream.started': return ({required Object timestamp}) => 'Запустив ${timestamp}';
case 'stream.notification': return ({required Object name}) => '${name} запрацював!';
case 'stream.chat.disabled': return 'ЧАТ ВІДКЛЮЧЕНО';
case 'stream.chat.disabled_timeout': return ({required Object time}) => 'Тайм-аут закінчився: ${time}';
case 'stream.chat.timeout': return ({required InlineSpan mod, required InlineSpan user, required InlineSpan time}) => TextSpan(children: [
@ -455,6 +499,8 @@ extension on TranslationsUk {
case 'settings.wallet.disconnect_wallet': return 'Відключити гаманець';
case 'settings.wallet.connect_1tap': return 'Підключення в 1 кран';
case 'settings.wallet.paste': return 'Вставити URL-адресу';
case 'settings.wallet.balance': return 'Баланс';
case 'settings.wallet.name': return 'Гаманець';
case 'settings.wallet.error.logged_out': return 'Не вдається підключити гаманець, коли ви вийшли з системи';
case 'settings.wallet.error.nwc_auth_event_not_found': return 'Не знайдено жодної події авторизації гаманця';
case 'login.username': return 'Ім\'я користувача';
@ -462,6 +508,23 @@ extension on TranslationsUk {
case 'login.key': return 'Увійдіть за допомогою ключа';
case 'login.create': return 'Створити обліковий запис';
case 'login.error.invalid_key': return 'Неправильний ключ';
case 'live.start': return 'GO LIVE';
case 'live.configure_stream': return 'Налаштувати потік';
case 'live.endpoint': return 'Кінцева точка';
case 'live.accept_tos': return 'Прийміть ТЗ';
case 'live.balance_left': return ({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('uk'))(n,
zero: '',
other: '~${time}',
);
case 'live.title': return 'Назва';
case 'live.summary': return 'Підсумок';
case 'live.image': return 'Зображення обкладинки';
case 'live.tags': return 'Теги';
case 'live.nsfw': return 'Нецензурна лексика';
case 'live.nsfw_description': return 'Перевірте тут, чи містить цей потік оголену натуру або порнографічний контент.';
case 'live.error.failed': return 'Потік не вдалося запустити';
case 'live.error.connection_error': return 'Помилка з\'єднання';
case 'live.error.start_failed': return 'Не вдалося запустити трансляцію, будь ласка, перевірте свій баланс';
default: return null;
}
}

View File

@ -51,6 +51,8 @@ class TranslationsZh extends Translations {
/// 匿名使用者
@override String get anon => '匿名';
@override String full_amount_sats({required num n}) => '${NumberFormat.decimalPattern('zh').format(n)} Sats';
/// 串流的觀看者人數
@override String viewers({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('zh'))(n,
one: '1 個檢視器',
@ -69,6 +71,7 @@ class TranslationsZh extends Translations {
@override late final _TranslationsProfileZh profile = _TranslationsProfileZh._(_root);
@override late final _TranslationsSettingsZh settings = _TranslationsSettingsZh._(_root);
@override late final _TranslationsLoginZh login = _TranslationsLoginZh._(_root);
@override late final _TranslationsLiveZh live = _TranslationsLiveZh._(_root);
}
// Path: stream
@ -80,6 +83,7 @@ class _TranslationsStreamZh extends TranslationsStreamEn {
// Translations
@override late final _TranslationsStreamStatusZh status = _TranslationsStreamStatusZh._(_root);
@override String started({required Object timestamp}) => '開始 ${timestamp}';
@override String notification({required Object name}) => '${name} 已啟用!';
@override late final _TranslationsStreamChatZh chat = _TranslationsStreamChatZh._(_root);
}
@ -204,6 +208,30 @@ class _TranslationsLoginZh extends TranslationsLoginEn {
@override late final _TranslationsLoginErrorZh error = _TranslationsLoginErrorZh._(_root);
}
// Path: live
class _TranslationsLiveZh extends TranslationsLiveEn {
_TranslationsLiveZh._(TranslationsZh root) : this._root = root, super.internal(root);
final TranslationsZh _root; // ignore: unused_field
// Translations
@override String get start => '開始直播';
@override String get configure_stream => '設定串流';
@override String get endpoint => '終點';
@override String get accept_tos => '接受服務條款';
@override String balance_left({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('zh'))(n,
zero: '',
other: '~${time}',
);
@override String get title => '標題';
@override String get summary => '摘要';
@override String get image => '封面圖片';
@override String get tags => '標籤';
@override String get nsfw => 'NSFW 內容';
@override String get nsfw_description => '請檢查此串流是否包含裸體或色情內容。';
@override late final _TranslationsLiveErrorZh error = _TranslationsLiveErrorZh._(_root);
}
// Path: stream.status
class _TranslationsStreamStatusZh extends TranslationsStreamStatusEn {
_TranslationsStreamStatusZh._(TranslationsZh root) : this._root = root, super.internal(root);
@ -288,6 +316,8 @@ class _TranslationsSettingsWalletZh extends TranslationsSettingsWalletEn {
@override String get disconnect_wallet => '斷開錢包';
@override String get connect_1tap => '1 抽頭連接';
@override String get paste => '貼上 URL';
@override String get balance => '平衡';
@override String get name => '錢包';
@override late final _TranslationsSettingsWalletErrorZh error = _TranslationsSettingsWalletErrorZh._(_root);
}
@ -301,6 +331,18 @@ class _TranslationsLoginErrorZh extends TranslationsLoginErrorEn {
@override String get invalid_key => '無效按鍵';
}
// Path: live.error
class _TranslationsLiveErrorZh extends TranslationsLiveErrorEn {
_TranslationsLiveErrorZh._(TranslationsZh root) : this._root = root, super.internal(root);
final TranslationsZh _root; // ignore: unused_field
// Translations
@override String get failed => '串流失敗';
@override String get connection_error => '連線錯誤';
@override String get start_failed => '串流啟動失敗,請檢查您的餘額';
}
// Path: stream.chat.write
class _TranslationsStreamChatWriteZh extends TranslationsStreamChatWriteEn {
_TranslationsStreamChatWriteZh._(TranslationsZh root) : this._root = root, super.internal(root);
@ -379,6 +421,7 @@ extension on TranslationsZh {
case 'most_zapped_streamers': return '最多被擊中的溪流';
case 'no_user_found': return '未找到使用者';
case 'anon': return '匿名';
case 'full_amount_sats': return ({required num n}) => '${NumberFormat.decimalPattern('zh').format(n)} Sats';
case 'viewers': return ({required num n}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('zh'))(n,
one: '1 個檢視器',
other: '${NumberFormat.decimalPattern('zh').format(n)} 觀眾',
@ -387,6 +430,7 @@ extension on TranslationsZh {
case 'stream.status.ended': return '結束';
case 'stream.status.planned': return '計劃';
case 'stream.started': return ({required Object timestamp}) => '開始 ${timestamp}';
case 'stream.notification': return ({required Object name}) => '${name} 已啟用!';
case 'stream.chat.disabled': return '關閉聊天';
case 'stream.chat.disabled_timeout': return ({required Object time}) => '超時過期: ${time}';
case 'stream.chat.timeout': return ({required InlineSpan mod, required InlineSpan user, required InlineSpan time}) => TextSpan(children: [
@ -455,6 +499,8 @@ extension on TranslationsZh {
case 'settings.wallet.disconnect_wallet': return '斷開錢包';
case 'settings.wallet.connect_1tap': return '1 抽頭連接';
case 'settings.wallet.paste': return '貼上 URL';
case 'settings.wallet.balance': return '平衡';
case 'settings.wallet.name': return '錢包';
case 'settings.wallet.error.logged_out': return '登出時無法連接錢包';
case 'settings.wallet.error.nwc_auth_event_not_found': return '未找到錢包認證事件';
case 'login.username': return '用戶名';
@ -462,6 +508,23 @@ extension on TranslationsZh {
case 'login.key': return '使用鑰匙登入';
case 'login.create': return '創建帳戶';
case 'login.error.invalid_key': return '無效按鍵';
case 'live.start': return '開始直播';
case 'live.configure_stream': return '設定串流';
case 'live.endpoint': return '終點';
case 'live.accept_tos': return '接受服務條款';
case 'live.balance_left': return ({required num n, required Object time}) => (_root.$meta.cardinalResolver ?? PluralResolvers.cardinal('zh'))(n,
zero: '',
other: '~${time}',
);
case 'live.title': return '標題';
case 'live.summary': return '摘要';
case 'live.image': return '封面圖片';
case 'live.tags': return '標籤';
case 'live.nsfw': return 'NSFW 內容';
case 'live.nsfw_description': return '請檢查此串流是否包含裸體或色情內容。';
case 'live.error.failed': return '串流失敗';
case 'live.error.connection_error': return '連線錯誤';
case 'live.error.start_failed': return '串流啟動失敗,請檢查您的餘額';
default: return null;
}
}

View File

@ -8,9 +8,10 @@ no_user_found: لم يتم العثور على مستخدم
"@no_user_found":
description: لم يتم العثور على مستخدم عند البحث
anon: هوية مخفية
full_amount_sats: "${n:decimalPattern} دولار ساتس"
viewers:
one: 1 مشاهد
other: "${n:decimalPattern} المشاهدين"
other: "{n:decimalPattern} المشاهدين"
"@viewers":
description: عدد مشاهدي البث
"@anon":
@ -21,10 +22,11 @@ stream:
ended: انتهى
planned: مخطط
started: بدأ $timestamp
notification: ${name} بدأ البث المباشر!
chat:
disabled: تم تعطيل الدردشة
disabled_timeout: "تنتهي المهلة: $time"
timeout(rich): $mod انتهى الوقت $user لـ $time
timeout(rich): $mod انتهى الوقت $user لـ ${time}
"@timeout":
description: رسالة دردشة تظهر أحداث المهلة
ended: انتهى البث
@ -122,6 +124,8 @@ settings:
disconnect_wallet: محفظة قطع الاتصال
connect_1tap: 1-التوصيل بنقرة 1
paste: لصق عنوان URL
balance: الرصيد
name: المحفظة
error:
logged_out: لا يمكن الاتصال بالمحفظة عند تسجيل الخروج
nwc_auth_event_not_found: لم يتم العثور على حدث مصادقة المحفظة
@ -132,3 +136,21 @@ login:
create: إنشاء حساب
error:
invalid_key: مفتاح غير صالح
live:
start: ابدأ البث المباشر
configure_stream: تكوين الدفق
endpoint: نقطة النهاية
accept_tos: قبول شروط الخدمة
balance_left:
zero:
other: ~${time}
title: العنوان
summary: الملخص
image: صورة الغلاف
tags: الوسوم
nsfw: محتوى غير لائق جنسيًا
nsfw_description: تحقق هنا إذا كان هذا البث يحتوي على محتوى إباحي أو عري.
error:
failed: فشل البث
connection_error: خطأ في الاتصال
start_failed: فشل بدء البث فشل، يرجى التحقق من رصيدك

View File

@ -9,6 +9,7 @@ no_user_found: Nebyl nalezen žádný uživatel
"@no_user_found":
description: Při vyhledávání nebyl nalezen žádný uživatel
anon: Anon
full_amount_sats: ${n:decimalPattern} sats
viewers:
one: 1 divák
other: ${n:decimalPattern} diváků
@ -22,10 +23,11 @@ stream:
ended: KONEC
planned: PLÁNOVANÉ
started: Založeno $timestamp
notification: ${name} byl spuštěn!
chat:
disabled: CHAT ZRUŠEN
disabled_timeout: "Časový limit vyprší: $time"
timeout(rich): $mod vypršel čas $user pro $time
timeout(rich): $mod vypršel čas $user pro ${time}
"@timeout":
description: Zpráva chatu zobrazující události časového limitu
ended: STREAM UKONČEN
@ -123,6 +125,8 @@ settings:
disconnect_wallet: Odpojení peněženky
connect_1tap: Připojení 1 kohoutku
paste: Vložit adresu URL
balance: Bilance
name: Peněženka
error:
logged_out: Nelze se připojit k peněžence, když jste odhlášeni
nwc_auth_event_not_found: Nebyla nalezena žádná událost autentizace peněženky
@ -133,3 +137,21 @@ login:
create: Vytvořit účet
error:
invalid_key: Neplatný klíč
live:
start: PŘEJÍT NA ŽIVOT
configure_stream: Konfigurace streamu
endpoint: Koncový bod
accept_tos: Přijmout TOS
balance_left:
zero:
other: ~${time}
title: Název
summary: Souhrn
image: Obrázek na obálce
tags: Štítky
nsfw: Obsah NSFW
nsfw_description: Zde zkontrolujte, zda tento stream obsahuje nahotu nebo pornografický obsah.
error:
failed: Stream se nezdařil
connection_error: Chyba připojení
start_failed: Spuštění streamu se nezdařilo, zkontrolujte prosím zůstatek

View File

@ -9,6 +9,7 @@ no_user_found: Ingen bruger fundet
"@no_user_found":
description: Ingen bruger fundet ved søgning
anon: Anon
full_amount_sats: ${n:decimalPattern} sats
viewers:
one: 1 seer
other: ${n:decimalPattern} seere
@ -22,10 +23,11 @@ stream:
ended: AFSLUTTET
planned: PLANLAGT
started: Startet $timestamp
notification: ${name} gik live!
chat:
disabled: CHAT DEAKTIVERET
disabled_timeout: "Timeout udløber: $time"
timeout(rich): $mod udløbet $user for $time
timeout(rich): $mod udløbet $user for ${time}
"@timeout":
description: Chatbesked, der viser timeout-hændelser
ended: STREAM AFSLUTTET
@ -123,6 +125,8 @@ settings:
disconnect_wallet: Afbryd forbindelsen til tegnebogen
connect_1tap: 1-Tap-forbindelse
paste: Indsæt URL
balance: Balance
name: Tegnebog
error:
logged_out: Kan ikke oprette forbindelse til wallet, når jeg er logget ud
nwc_auth_event_not_found: Ingen wallet-auth-begivenhed fundet
@ -133,3 +137,21 @@ login:
create: Opret konto
error:
invalid_key: Ugyldig nøgle
live:
start: GO LIVE
configure_stream: Konfigurer stream
endpoint: Slutpunkt
accept_tos: Accepter TOS
balance_left:
zero:
other: ~${time}
title: Titel
summary: Sammenfatning
image: Forsidebillede
tags: Tags
nsfw: NSFW-indhold
nsfw_description: Tjek her, om denne stream indeholder nøgenhed eller pornografisk indhold.
error:
failed: Strømmen mislykkedes
connection_error: Forbindelsesfejl
start_failed: Stream-start mislykkedes, tjek venligst din saldo

View File

@ -4,36 +4,38 @@ upload_avatar: Avatar hochladen
klicken, um den Upload zu starten
most_zapped_streamers: Meistgezappte Streamer
"@most_zapped_streamers":
description: Überschrift über gelistete Top-Streamer von zaps
description: Überschrift über gelistete Top-Streamer nach Zaps
no_user_found: Kein Benutzer gefunden
"@no_user_found":
description: Kein Benutzer bei der Suche gefunden
anon: Anon
full_amount_sats: ${n:decimalPattern} sats
viewers:
one: 1 Zuschauer
other: ${n:decimalPattern} Zuschauer
"@viewers":
description: Anzahl der Betrachter des Streams
description: Anzahl der Zuschauer des Streams
"@anon":
description: Ein anonymer Benutzer
stream:
status:
live: LIVE
ended: ENDED
ended: BEENDET
planned: GEPLANT
started: Gestartet $timestamp
notification: ${name} ging live!
chat:
disabled: CHAT DEAKTIVIERT
disabled_timeout: "Die Zeitüberschreitung läuft ab: $time"
timeout(rich): $mod Zeitüberschreitung $user für $time
disabled_timeout: "Timeout läuft ab: $time"
timeout(rich): $mod gibt $user einen Timeout für ${time}
"@timeout":
description: Chat-Nachricht mit Zeitüberschreitungsereignissen
description: Chat-Nachricht mit Timeout-Ereignissen
ended: STREAM BEENDET
"@ended":
description: Stream beendet Fußzeile am Ende des Chats
zap(rich): $user hat $amount sats gezappt
"@zap":
description: Chatnachricht mit Stream Zaps
description: Chat-Nachricht mit Stream-Zaps
write:
label: Nachricht schreiben
"@label":
@ -124,6 +126,8 @@ settings:
disconnect_wallet: Brieftasche abtrennen
connect_1tap: 1-Tap-Verbindung
paste: URL einfügen
balance: Bilanz
name: Brieftasche
error:
logged_out: Kann keine Verbindung zur Brieftasche herstellen, wenn ich abgemeldet bin
nwc_auth_event_not_found: Kein Wallet-Authentifizierungsereignis gefunden
@ -134,3 +138,22 @@ login:
create: Konto erstellen
error:
invalid_key: Ungültiger Schlüssel
live:
start: LIVE GEHEN
configure_stream: Stream konfigurieren
endpoint: Endpunkt
accept_tos: TOS akzeptieren
balance_left:
zero:
other: ~${time}
title: Titel
summary: Zusammenfassung
image: Titelbild
tags: Tags
nsfw: NSFW-Inhalt
nsfw_description: Prüfen Sie hier, ob dieser Stream Nacktheit oder
pornografische Inhalte enthält.
error:
failed: Stream fehlgeschlagen
connection_error: Verbindungsfehler
start_failed: Streamstart fehlgeschlagen, bitte überprüfen Sie Ihr Guthaben

View File

@ -9,6 +9,7 @@ no_user_found: Δεν βρέθηκε χρήστης
"@no_user_found":
description: Δεν βρέθηκε χρήστης κατά την αναζήτηση
anon: Anon
full_amount_sats: ${n:decimalPattern} sats
viewers:
one: 1 θεατής
other: ${n:decimalPattern} θεατές
@ -22,10 +23,11 @@ stream:
ended: ENDED
planned: ΣΧΕΔΙΑΣΜΟΣ
started: Ξεκίνησε $timestamp
notification: ${name} βγήκε ζωντανά!
chat:
disabled: ΑΠΕΝΕΡΓΟΠΟΙΗΜΈΝΗ ΣΥΝΟΜΙΛΊΑ
disabled_timeout: "Το χρονικό όριο λήγει: $time"
timeout(rich): $mod χρονομετρημένη λήξη $user για $time
timeout(rich): $mod χρονομετρημένη λήξη $user για ${time}
"@timeout":
description: Μήνυμα συνομιλίας που εμφανίζει συμβάντα timeout
ended: STREAM ΤΕΛΕΙΩΣΕ
@ -126,6 +128,8 @@ settings:
disconnect_wallet: Αποσύνδεση πορτοφολιού
connect_1tap: Σύνδεση 1 βρύσης
paste: Επικόλληση URL
balance: Υπόλοιπο
name: Πορτοφόλι
error:
logged_out: Δεν μπορώ να συνδεθώ με πορτοφόλι όταν έχω αποσυνδεθεί
nwc_auth_event_not_found: Δεν βρέθηκε συμβάν εξουσιοδότησης πορτοφολιού
@ -136,3 +140,21 @@ login:
create: Δημιουργία λογαριασμού
error:
invalid_key: Μη έγκυρο κλειδί
live:
start: GO LIVE
configure_stream: Διαμόρφωση ροής
endpoint: Τελικό σημείο
accept_tos: Αποδοχή TOS
balance_left:
zero:
other: ~${time}
title: Τίτλος
summary: Περίληψη
image: Εικόνα εξωφύλλου
tags: Ετικέτες
nsfw: Περιεχόμενο NSFW
nsfw_description: Ελέγξτε εδώ αν αυτή η ροή περιέχει γυμνό ή πορνογραφικό περιεχόμενο.
error:
failed: Το ρεύμα απέτυχε
connection_error: Σφάλμα σύνδεσης
start_failed: Η εκκίνηση της ροής απέτυχε, παρακαλούμε ελέγξτε το υπόλοιπό σας

View File

@ -8,6 +8,7 @@ no_user_found: No user found
"@no_user_found":
description: No user found when searching
anon: Anon
full_amount_sats: ${n:decimalPattern} sats
viewers:
one: 1 viewer
other: ${n:decimalPattern} viewers
@ -21,6 +22,7 @@ stream:
ended: ENDED
planned: PLANNED
started: Started $timestamp
notification: ${name} went live!
chat:
disabled: CHAT DISABLED
disabled_timeout: "Timeout expires: $time"
@ -121,6 +123,8 @@ settings:
disconnect_wallet: Disconnect Wallet
connect_1tap: 1-Tap Connection
paste: Paste URL
balance: Balance
name: Wallet
error:
logged_out: Cant connect wallet when logged out
nwc_auth_event_not_found: No wallet auth event found
@ -131,3 +135,21 @@ login:
create: Create Account
error:
invalid_key: Invalid key
live:
start: GO LIVE
configure_stream: Configure Stream
endpoint: Endpoint
accept_tos: Accept TOS
balance_left:
zero:
other: ~${time}
title: Title
summary: Summary
image: Cover Image
tags: Tags
nsfw: NSFW Content
nsfw_description: Check here if this stream contains nudity or pornographic content.
error:
failed: Stream failed
connection_error: Connection Error
start_failed: Stream start failed, please check your balance

View File

@ -9,6 +9,7 @@ no_user_found: No se ha encontrado ningún usuario
"@no_user_found":
description: No se ha encontrado ningún usuario al realizar la búsqueda
anon: Anónimo
full_amount_sats: ${n:decimalPattern} sats
viewers:
one: 1 espectador
other: ${n:decimalPattern} espectadores
@ -22,10 +23,11 @@ stream:
ended: FIN
planned: PLANIFICADO
started: Comenzó $timestamp
notification: ${name} ¡se ha puesto en marcha!
chat:
disabled: CHAT DESHABILITADO
disabled_timeout: "El tiempo de espera expira: $time"
timeout(rich): $mod timed out $user para $time
timeout(rich): $mod timed out $user para ${time}
"@timeout":
description: Mensaje de chat que muestra los eventos de tiempo de espera
ended: STREAM FINED
@ -126,6 +128,8 @@ settings:
disconnect_wallet: Desconectar Cartera
connect_1tap: Conexión de 1 toma
paste: Pegar URL
balance: Saldo
name: Cartera
error:
logged_out: No se puede conectar el monedero al cerrar la sesión
nwc_auth_event_not_found: No se ha encontrado ningún evento de autenticación de cartera
@ -136,3 +140,21 @@ login:
create: Crear una cuenta
error:
invalid_key: Clave no válida
live:
start: EN VIVO
configure_stream: Configurar Stream
endpoint: Punto final
accept_tos: Aceptar TOS
balance_left:
zero:
other: ~${time}
title: Título
summary: Resumen
image: Imagen de portada
tags: Etiquetas
nsfw: Contenido NSFW
nsfw_description: Compruebe aquí si este flujo contiene desnudos o contenido pornográfico.
error:
failed: Corriente fallida
connection_error: Error de conexión
start_failed: Error en el inicio de la transmisión, compruebe su saldo

View File

@ -9,6 +9,7 @@ no_user_found: Käyttäjää ei löytynyt
"@no_user_found":
description: Käyttäjää ei löytynyt haun yhteydessä
anon: Anon
full_amount_sats: ${n:decimalPattern} sats
viewers:
one: 1 katsoja
other: ${n:decimalPattern} katsojat
@ -22,10 +23,11 @@ stream:
ended: ENDED
planned: SUUNNITELTU
started: Aloitettu $timestamp
notification: ${name} meni suoraksi!
chat:
disabled: CHAT POISTETTU KÄYTÖSTÄ
disabled_timeout: "Aikakatkaisu päättyy: $time"
timeout(rich): $mod ajastettu $user for $time
timeout(rich): $mod ajastettu $user for ${time}
"@timeout":
description: Chat-viesti, joka näyttää aikakatkaisutapahtumat
ended: STREAM PÄÄTTYNYT
@ -124,6 +126,8 @@ settings:
disconnect_wallet: Irrota lompakko
connect_1tap: 1-Tap-liitäntä
paste: Liitä URL-osoite
balance: Balance
name: Lompakko
error:
logged_out: Ei voi muodostaa yhteyttä lompakkoon, kun on kirjautunut ulos
nwc_auth_event_not_found: Ei lompakko-auth-tapahtumaa löydetty
@ -134,3 +138,22 @@ login:
create: Luo tili
error:
invalid_key: Virheellinen avain
live:
start: GO LIVE
configure_stream: Määritä Stream
endpoint: Loppupiste
accept_tos: Hyväksy TOS
balance_left:
zero:
other: ~${time}
title: Otsikko
summary: Yhteenveto
image: Kansikuva
tags: Tunnisteet
nsfw: NSFW-sisältö
nsfw_description: Tarkista täältä, jos tämä stream sisältää alastomuutta tai
pornografista sisältöä.
error:
failed: Stream epäonnistui
connection_error: Yhteysvirhe
start_failed: Virran käynnistys epäonnistui, tarkista saldosi

View File

@ -9,6 +9,7 @@ no_user_found: Aucun utilisateur trouvé
"@no_user_found":
description: Aucun utilisateur n'a été trouvé lors de la recherche
anon: Anonyme
full_amount_sats: "${n:decimalPattern} sats"
viewers:
one: 1 téléspectateur
other: "${n:decimalPattern} téléspectateurs"
@ -22,10 +23,11 @@ stream:
ended: FINI
planned: PRÉVU
started: Commencé à $timestamp
notification: ${name} est en ligne !
chat:
disabled: CHAT DISABLED
disabled_timeout: "Le délai expire : $time"
timeout(rich): $mod $user a expiré dans le temps pour $time
timeout(rich): $mod $user a expiré dans le temps pour ${time}
"@timeout":
description: Message de chat indiquant les événements de dépassement de délai
ended: STREAM ENDED
@ -123,6 +125,8 @@ settings:
disconnect_wallet: Déconnecter le portefeuille
connect_1tap: Connexion à 1 robinet
paste: Coller l'URL
balance: Équilibre
name: Portefeuille
error:
logged_out: Impossible de se connecter au portefeuille lorsque l'on est déconnecté
nwc_auth_event_not_found: Aucun événement d'authentification de portefeuille n'a été trouvé
@ -133,3 +137,22 @@ login:
create: Créer un Compte
error:
invalid_key: Clé non valide
live:
start: GO LIVE
configure_stream: Configurer le flux
endpoint: Point final
accept_tos: Accepter les CGU
balance_left:
zero:
other: ~${time}
title: Titre
summary: Résumé
image: Image de couverture
tags: Tags
nsfw: Contenu NSFW
nsfw_description: Cochez cette case si ce flux contient de la nudité ou du
contenu pornographique.
error:
failed: Échec du flux
connection_error: Erreur de connexion
start_failed: Le démarrage du flux a échoué, veuillez vérifier votre solde

View File

@ -22,6 +22,7 @@ stream:
ended: ENDED
planned: TERVEZETT
started: Elindult $timestamp
notification: ${name} elindult!
chat:
disabled: CHAT KIKAPCSOLVA
disabled_timeout: "Az időkorlát lejár: $time"
@ -56,7 +57,7 @@ stream:
from: RAID FROM $name
"@from":
description: Chat raid üzenet egy másik folyamból
countdown: Raiding a ${time}oldalon
countdown: Raiding a ${time} oldalon
"@countdown":
description: Visszaszámláló időzítő az automatikus lovagláshoz
goal:
@ -84,7 +85,7 @@ button:
embed:
article_by: Cikk ${name}
note_by: $name bejegyzése
live_stream_by: Élő közvetítés a ${name}oldalon
live_stream_by: Élő közvetítés a ${name} oldalon
stream_list:
following: Követettek bejegyzései
live: Élő

View File

@ -9,6 +9,7 @@ no_user_found: Nessun utente trovato
"@no_user_found":
description: Nessun utente trovato durante la ricerca
anon: Anonimo
full_amount_sats: ${n:decimalPattern} sats
viewers:
one: 1 spettatore
other: ${n:decimalPattern} spettatori
@ -22,10 +23,11 @@ stream:
ended: FINE
planned: PREVISTO
started: Avviato $timestamp
notification: ${name} è andato in onda!
chat:
disabled: CHAT DISABILITATA
disabled_timeout: "Il timeout scade: $time"
timeout(rich): $mod time out $user per $time
timeout(rich): $mod time out $user per ${time}
"@timeout":
description: Messaggio di chat che mostra gli eventi di timeout
ended: STREAM ENDED
@ -124,6 +126,8 @@ settings:
disconnect_wallet: Disconnettere il portafoglio
connect_1tap: Connessione a 1 rubinetto
paste: Incolla URL
balance: Equilibrio
name: Portafoglio
error:
logged_out: Impossibile connettere il portafoglio quando si è disconnessi
nwc_auth_event_not_found: Nessun evento wallet auth trovato
@ -134,3 +138,21 @@ login:
create: Crea un account
error:
invalid_key: Chiave non valida
live:
start: VAI IN DIRETTA
configure_stream: Configurare il flusso
endpoint: Punto finale
accept_tos: Accettare i TOS
balance_left:
zero:
other: ~${time}
title: Titolo
summary: Sintesi
image: Immagine di copertina
tags: Tag
nsfw: Contenuto NSFW
nsfw_description: Controllare qui se questo streaming contiene nudità o contenuti pornografici.
error:
failed: Flusso fallito
connection_error: Errore di connessione
start_failed: Avvio del flusso fallito, controllare il saldo

View File

@ -8,6 +8,7 @@ no_user_found: ユーザーが見つかりません
"@no_user_found":
description: 検索してもユーザーが見つからない
anon: 匿名
full_amount_sats: "${n:decimalPattern} サッツ"
viewers:
one: 1 視聴者
other: ${n:decimalPattern} 人が視聴中
@ -21,10 +22,11 @@ stream:
ended: 終了
planned: 予定
started: $timestamp を開始
notification: ${name} がライブを開始した!
chat:
disabled: チャット無効
disabled_timeout: タイムアウト: $time
timeout(rich): $mod タイムアウト $user for $time
timeout(rich): $mod タイムアウト $user for ${time}
"@timeout":
description: タイムアウトイベントを表示するチャットメッセージ
ended: 配信終了
@ -121,6 +123,8 @@ settings:
disconnect_wallet: ウォレットの切断
connect_1tap: 1タップ接続
paste: URLを貼り付ける
balance: バランス
name: 財布
error:
logged_out: ログアウト時にウォレットに接続できない
nwc_auth_event_not_found: ウォレットの認証イベントが見つかりません
@ -131,3 +135,21 @@ login:
create: アカウントを作成する
error:
invalid_key: 無効なキー
live:
start: ライブ中継
configure_stream: ストリームの設定
endpoint: エンドポイント
accept_tos: TOSを受け入れる
balance_left:
zero:
other: ~${time}
title: タイトル
summary: 概要
image: 表紙画像
tags: タグ
nsfw: NSFWコンテンツ
nsfw_description: このストリームにヌードやポルノが含まれている場合は、ここをチェックしてください。
error:
failed: ストリーム失敗
connection_error: 接続エラー
start_failed: ストリームの開始に失敗しました。

View File

@ -8,9 +8,10 @@ no_user_found: 사용자를 찾을 수 없습니다.
"@no_user_found":
description: 검색 시 사용자를 찾을 수 없음
anon: Anon
full_amount_sats: "${n:decimalPattern} sats"
viewers:
one: 시청자 1명
other: "${n:decimalPattern} 시청자"
other: "{n:decimalPattern} 시청자"
"@viewers":
description: 스트림 시청자 수
"@anon":
@ -21,10 +22,11 @@ stream:
ended: 종료
planned: 계획된
started: 시작 $timestamp
notification: ${name} 라이브가 시작되었습니다!
chat:
disabled: 채팅 사용 안 함
disabled_timeout: "시간 초과가 만료되었습니다: $time"
timeout(rich): $mod 시간 초과됨 $user $time
timeout(rich): $mod 시간 초과됨 $user ${time}
"@timeout":
description: 시간 초과 이벤트를 표시하는 채팅 메시지
ended: 스트림 종료
@ -121,6 +123,8 @@ settings:
disconnect_wallet: 지갑 연결 해제
connect_1tap: 1-탭 연결
paste: URL 붙여넣기
balance: 잔액
name: 지갑
error:
logged_out: 로그아웃 시 지갑 연결 불가
nwc_auth_event_not_found: 지갑 인증 이벤트를 찾을 수 없습니다.
@ -131,3 +135,21 @@ login:
create: 계정 만들기
error:
invalid_key: 잘못된 키
live:
start: 라이브 시작하기
configure_stream: 스트림 구성
endpoint: 엔드포인트
accept_tos: TOS 수락
balance_left:
zero:
other: ~${time}
title: 제목
summary: 요약
image: 표지 이미지
tags: 태그
nsfw: NSFW 콘텐츠
nsfw_description: 이 스트림에 노출 또는 음란 콘텐츠가 포함되어 있는지 여기에서 확인하세요.
error:
failed: 스트림 실패
connection_error: 연결 오류
start_failed: 스트림 시작에 실패했습니다. 잔액을 확인해 주세요.

View File

@ -9,6 +9,7 @@ no_user_found: Geen gebruiker gevonden
"@no_user_found":
description: Geen gebruiker gevonden bij het zoeken
anon: Anon
full_amount_sats: ${n:decimalPattern} sats
viewers:
one: 1 kijker
other: ${n:decimalPattern} kijkers
@ -22,10 +23,11 @@ stream:
ended: GESLOTEN
planned: GEPLAND
started: Begonnen met $timestamp
notification: ${name} ging live!
chat:
disabled: CHAT UITGESCHAKELD
disabled_timeout: "Time-out loopt af: $time"
timeout(rich): $mod timed out $user voor $time
timeout(rich): $mod timed out $user voor ${time}
"@timeout":
description: Chatbericht met time-outgebeurtenissen
ended: STREAM BEËINDIGD
@ -124,6 +126,8 @@ settings:
disconnect_wallet: Portefeuille loskoppelen
connect_1tap: 1-Tap Aansluiting
paste: URL plakken
balance: Saldo
name: Portemonnee
error:
logged_out: Kan geen verbinding maken met portemonnee als ik ben uitgelogd
nwc_auth_event_not_found: Geen portemonnee-auth-gebeurtenis gevonden
@ -134,3 +138,21 @@ login:
create: Account aanmaken
error:
invalid_key: Ongeldige sleutel
live:
start: LIVE GAAN
configure_stream: Stream configureren
endpoint: Eindpunt
accept_tos: TOS accepteren
balance_left:
zero:
other: ~${time}
title: Titel
summary: Samenvatting
image: Afbeelding omslag
tags: Tags
nsfw: NSFW-inhoud
nsfw_description: Controleer hier of deze stream naaktheid of pornografische inhoud bevat.
error:
failed: Stream mislukt
connection_error: Fout bij verbinding
start_failed: Stream start mislukt, controleer uw saldo

View File

@ -9,6 +9,7 @@ no_user_found: Nie znaleziono użytkownika
"@no_user_found":
description: Nie znaleziono użytkownika podczas wyszukiwania
anon: Anon
full_amount_sats: ${n:decimalPattern} sats
viewers:
one: 1 przeglądarka
other: "{n:decimalPattern} widzów"
@ -22,10 +23,11 @@ stream:
ended: ZAKOŃCZONY
planned: PLANOWANE
started: Start $timestamp
notification: ${name} został uruchomiony!
chat:
disabled: CZAT WYŁĄCZONY
disabled_timeout: "Upłynął limit czasu: $time"
timeout(rich): $mod upłynął limit czasu $user dla $time
timeout(rich): $mod upłynął limit czasu $user dla ${time}
"@timeout":
description: Komunikat czatu pokazujący zdarzenia przekroczenia limitu czasu
ended: TRANSMISJA ZAKOŃCZONA
@ -124,6 +126,8 @@ settings:
disconnect_wallet: Odłącz portfel
connect_1tap: Połączenie 1-wtykowe
paste: Wklej adres URL
balance: Równowaga
name: Portfel
error:
logged_out: Nie można połączyć portfela po wylogowaniu
nwc_auth_event_not_found: Nie znaleziono zdarzenia autoryzacji portfela
@ -134,3 +138,21 @@ login:
create: Utwórz konto
error:
invalid_key: Nieprawidłowy klucz
live:
start: GO LIVE
configure_stream: Konfiguracja strumienia
endpoint: Punkt końcowy
accept_tos: Zaakceptuj Regulamin
balance_left:
zero:
other: ~${time}
title: Tytuł
summary: Podsumowanie
image: Obraz na okładce
tags: Tagi
nsfw: Treści NSFW
nsfw_description: Sprawdź tutaj, czy ten stream zawiera nagość lub treści pornograficzne.
error:
failed: Strumień nie powiódł się
connection_error: Błąd połączenia
start_failed: Uruchomienie strumienia nie powiodło się, sprawdź saldo

View File

@ -9,6 +9,7 @@ no_user_found: Nenhum usuário encontrado
"@no_user_found":
description: Nenhum usuário foi encontrado durante a pesquisa
anon: Anônimo
full_amount_sats: ${n:decimalPattern} sats
viewers:
one: 1 visualizador
other: ${n:decimalPattern} espectadores
@ -22,10 +23,11 @@ stream:
ended: FINALIZADO
planned: PLANEJADO
started: Iniciado em $timestamp
notification: ${name} foi ao ar!
chat:
disabled: BATE-PAPO DESATIVADO
disabled_timeout: "O tempo limite expira: $time"
timeout(rich): $mod Tempo esgotado $user para $time
timeout(rich): $mod Tempo esgotado $user para ${time}
"@timeout":
description: Mensagem de bate-papo mostrando eventos de tempo limite
ended: TRANSMISSÃO ENCERRADA
@ -124,6 +126,8 @@ settings:
disconnect_wallet: Desconectar carteira
connect_1tap: Conexão de 1 torneira
paste: Colar URL
balance: Equilíbrio
name: Carteira
error:
logged_out: Não consigo conectar a carteira quando estou desconectado
nwc_auth_event_not_found: Nenhum evento de autenticação de carteira encontrado
@ -134,3 +138,21 @@ login:
create: Criar Conta
error:
invalid_key: Chave inválida
live:
start: GO LIVE
configure_stream: Configurar fluxo
endpoint: Ponto final
accept_tos: Aceitar os Termos de Serviço
balance_left:
zero:
other: ~${time}
title: Título
summary: Resumo
image: Imagem da capa
tags: Tags
nsfw: Conteúdo NSFW
nsfw_description: Verifique aqui se essa transmissão contém nudez ou conteúdo pornográfico.
error:
failed: O fluxo falhou
connection_error: Erro de conexão
start_failed: Falha no início do fluxo, verifique seu saldo

View File

@ -9,6 +9,7 @@ no_user_found: Niciun utilizator găsit
"@no_user_found":
description: Nu s-a găsit niciun utilizator la căutare
anon: Anon
full_amount_sats: ${n:decimalPattern} sats
viewers:
one: 1 vizualizator
other: ${n:decimalPattern} telespectatori
@ -22,10 +23,11 @@ stream:
ended: TERMINAT
planned: PLANIFICATE
started: A început $timestamp
notification: ${name} a intrat în direct!
chat:
disabled: CHAT DEZACTIVAT
disabled_timeout: "Timpul expiră: $time"
timeout(rich): $mod Timed out $user pentru $time
timeout(rich): $mod Timed out $user pentru ${time}
"@timeout":
description: Mesaj de chat care afișează evenimentele de timeout
ended: STREAM ÎNCHEIAT
@ -125,6 +127,8 @@ settings:
disconnect_wallet: Deconectați portofelul
connect_1tap: Conexiune 1-Tap
paste: Lipiți URL
balance: Echilibru
name: Portofel
error:
logged_out: Nu puteți conecta portofelul atunci când sunteți deconectat
nwc_auth_event_not_found: Nu a fost găsit niciun eveniment de autorizare a portofelului
@ -135,3 +139,21 @@ login:
create: Creare cont
error:
invalid_key: Cheie invalidă
live:
start: GO LIVE
configure_stream: Configurați fluxul
endpoint: Punct final
accept_tos: Acceptați TOS
balance_left:
zero:
other: ~${time}
title: Titlu
summary: Rezumat
image: Imagine de copertă
tags: Etichete
nsfw: Conținut NSFW
nsfw_description: Bifați aici dacă acest flux conține nuditate sau conținut pornografic.
error:
failed: Fluxul a eșuat
connection_error: Eroare de conectare
start_failed: Pornirea fluxului a eșuat, vă rugăm să verificați soldul

View File

@ -9,6 +9,7 @@ no_user_found: Пользователь не найден
"@no_user_found":
description: Пользователь не найден при поиске
anon: Аноним
full_amount_sats: ${n:decimalPattern} sats
viewers:
one: 1 зритель
other: ${n:decimalPattern} зрителей
@ -22,10 +23,11 @@ stream:
ended: КОНЕЦ
planned: ПЛАНИРУЕМЫЙ
started: Начало $timestamp
notification: ${name} запустился!
chat:
disabled: ЧАТ ОТКЛЮЧЕН
disabled_timeout: "Таймаут истекает: $time"
timeout(rich): $mod тайм-аут $user для $time
timeout(rich): $mod тайм-аут $user для ${time}
"@timeout":
description: Сообщение в чате, показывающее события по тайм-ауту
ended: ТРАНСЛЯЦИЯ ОКОНЧЕНА
@ -125,6 +127,8 @@ settings:
disconnect_wallet: Отключить кошелек
connect_1tap: 1-кратное соединение
paste: Вставить URL
balance: Баланс
name: Кошелек
error:
logged_out: Невозможно подключить кошелек при выходе из системы
nwc_auth_event_not_found: Событие аутентификации кошелька не найдено
@ -135,3 +139,22 @@ login:
create: Создать аккаунт
error:
invalid_key: Неверный ключ
live:
start: ПЕРЕЙТИ В ПРЯМОЙ ЭФИР
configure_stream: Настроить поток
endpoint: Конечная точка
accept_tos: Принять TOS
balance_left:
zero:
other: ~${time}
title: Название
summary: Резюме
image: Изображение на обложке
tags: Теги
nsfw: NSFW-контент
nsfw_description: Отметьте здесь, если этот поток содержит наготу или
порнографические материалы.
error:
failed: Сбой потока
connection_error: Ошибка подключения
start_failed: Запуск потока не удался, пожалуйста, проверьте баланс

View File

@ -1,19 +1,20 @@
upload_avatar: Ladda upp avatar
"@upload_avatar":
description: Text som uppmanar användaren att trycka på avatarplatshållaren för
description: Text som uppmanar användaren att trycka på avatar platshållaren för
att påbörja uppladdningen
most_zapped_streamers: De flesta zappade streamers
most_zapped_streamers: De flest zappade streamers
"@most_zapped_streamers":
description: Rubrik över listade toppstreamers av zaps
description: Rubrik över listade topp streamers av zaps
no_user_found: Ingen användare hittades
"@no_user_found":
description: Ingen användare hittades vid sökning
anon: Anon
anon: Anno
full_amount_sats: ${n:decimalPattern} sats
viewers:
one: 1 tittare
other: ${n:decimalPattern} tittare
"@viewers":
description: Antal tittare på streamingen
description: Antal tittare på strömmingen
"@anon":
description: En anonym användare
stream:
@ -21,16 +22,17 @@ stream:
live: LIVE
ended: AVSLUTAD
planned: PLANERADE
started: Startade $timestamp
started: Startad $timestamp
notification: ${name} gick live!
chat:
disabled: CHAT AVSTÄNGD
disabled_timeout: "Tidsgränsen går ut: $time"
timeout(rich): $mod tidsbegränsad $user för $time
timeout(rich): $mod tidsbegränsad $user för ${time}
"@timeout":
description: Chattmeddelande som visar timeout-händelser
ended: STREAM AVSLUTAD
"@ended":
description: Stream avslutade sidfoten längst ner på chatten
description: Streama slutade sidfot längst ned i chatten
zap(rich): $user zapped $amount sats
"@zap":
description: Chattmeddelande som visar strömavbrott
@ -38,27 +40,27 @@ stream:
label: Skriv meddelande
"@label":
description: Etikett på inmatningsrutan för chattmeddelanden
no_signer: Det går inte att skriva meddelanden med npub-inloggning
no_signer: Det går inte att skriva meddelanden med n-pub inloggning
"@no_signer":
description: Chattinmatningsmeddelande som visas när användaren endast är
inloggad med pubkey
inloggad med publik nyckel
login: Logga in för att skicka meddelanden
"@login":
description: Chattinmatningsmeddelande som visas när användaren är utloggad
badge:
awarded_to: "Tilldelas till:"
"@awarded_to":
description: Rubrik över lista över användare som tilldelats en badge
description: Rubrik över listan över användare som tilldelas ett märke
raid:
to: RAIDING $name
to: RAIDING ${name}
"@to":
description: Chatta raidmeddelande till en annan ström
from: RAID FRÅN $name
description: Chatt raid meddelande till en annan ström
from: RAID FRÅN ${name}
"@from":
description: Chat raid-meddelande från en annan ström
countdown: Raiding $time
description: Chatt raid meddelande från en annan ström
countdown: Radiering i ${time}
"@countdown":
description: Nedräkningstimer för auto-raiding
description: Nedräkningstimer för auto- radiering
goal:
title: "Mål: $amount"
remaining: "Kvarvarande: $amount"
@ -74,7 +76,7 @@ button:
description: Knapptext för följ-knappen
unfollow: Sluta följa
"@unfollow":
description: Knapptext för avföljningsknappen
description: Knapptext för sluta följa knappen
mute: Tysta
unmute: Avtysta
share: Dela
@ -82,9 +84,9 @@ button:
connect: Anslut
settings: Inställningar
embed:
article_by: Artikel av $name
article_by: Artikel av ${name}
note_by: Anteckning av $name
live_stream_by: Direktsändning via $name
live_stream_by: Direktsändning via ${name}
stream_list:
following: Följer
live: Live
@ -114,17 +116,19 @@ settings:
profile:
display_name: Visa namn
about: Om
nip05: Nostr Adress
lud16: Adress för blixtnedslag
nip05: Nostr adress
lud16: Lightning-adress
error:
logged_out: Kan inte redigera profil när jag är utloggad
wallet:
connect_wallet: Connect plånbok (NWC nostr+walletconnect://)
connect_wallet: Anslut plånbok (NWC nostr+walletconnect://)
disconnect_wallet: Koppla bort plånboken
connect_1tap: 1-Tap-anslutning
connect_1tap: 1-tryck anslutning
paste: Klistra in URL
balance: Balans
name: Plånbok
error:
logged_out: Kan inte ansluta plånbok när du är inloggad
logged_out: Kan inte ansluta plånbok när du är utloggad
nwc_auth_event_not_found: Inget autentiseringshändelse för plånbok hittades
login:
username: Användarnamn
@ -133,3 +137,21 @@ login:
create: Skapa konto
error:
invalid_key: Ogiltig nyckel
live:
start: GÅ DIREKT
configure_stream: Konfigurera ström
endpoint: Slutpunkt
accept_tos: Acceptera TOS
balance_left:
zero:
other: ~${time}
title: Titel
summary: Sammanfattning
image: Omslagsbild
tags: Etiketter
nsfw: NSFW-innehåll
nsfw_description: Markera här om denna stream innehåller nakenhet eller pornografiskt innehåll.
error:
failed: Strömmen misslyckades
connection_error: Anslutningsfel
start_failed: Stream start misslyckades, vänligen kontrollera ditt saldo

View File

@ -22,10 +22,11 @@ stream:
ended: SONLANDI
planned: PLANLANMIŞ
started: Başlatıldı $timestamp
notification: ${name} yayına girdi!
chat:
disabled: SOHBET DEVRE DIŞI
disabled_timeout: "Zaman aşımı sona eriyor: $time"
timeout(rich): $mod zaman aşımına uğradı $user için $time
timeout(rich): $mod zaman aşımına uğradı $user ${time}için
"@timeout":
description: Zaman aşımı olaylarını gösteren sohbet mesajı
ended: YAYIN SONLANDI
@ -53,7 +54,7 @@ stream:
to: RAIDING ${name}
"@to":
description: Başka bir akışa sohbet baskını mesajı
from: ${name}ADRESINDEN RAID
from: ${name} ADRESINDEN RAID
"@from":
description: Başka bir akıştan sohbet baskını mesajı
countdown: ${time}adresinde baskın

View File

@ -9,6 +9,7 @@ no_user_found: Користувача не знайдено
"@no_user_found":
description: Користувача не знайдено при пошуку
anon: Анонім.
full_amount_sats: ${n:decimalPattern} сатів
viewers:
one: 1 глядач
other: ${n:decimalPattern} глядачів
@ -22,10 +23,11 @@ stream:
ended: ЗАКІНЧЕНО
planned: ЗАПЛАНОВАНО
started: Запустив $timestamp
notification: ${name} запрацював!
chat:
disabled: ЧАТ ВІДКЛЮЧЕНО
disabled_timeout: "Тайм-аут закінчився: $time"
timeout(rich): $mod таймінг $user для $time
timeout(rich): $mod таймінг $user для ${time}
"@timeout":
description: Повідомлення в чаті про події тайм-ауту
ended: СТРІМ ЗАКІНЧИВСЯ
@ -125,6 +127,8 @@ settings:
disconnect_wallet: Відключити гаманець
connect_1tap: Підключення в 1 кран
paste: Вставити URL-адресу
balance: Баланс
name: Гаманець
error:
logged_out: Не вдається підключити гаманець, коли ви вийшли з системи
nwc_auth_event_not_found: Не знайдено жодної події авторизації гаманця
@ -135,3 +139,22 @@ login:
create: Створити обліковий запис
error:
invalid_key: Неправильний ключ
live:
start: GO LIVE
configure_stream: Налаштувати потік
endpoint: Кінцева точка
accept_tos: Прийміть ТЗ
balance_left:
zero:
other: ~${time}
title: Назва
summary: Підсумок
image: Зображення обкладинки
tags: Теги
nsfw: Нецензурна лексика
nsfw_description: Перевірте тут, чи містить цей потік оголену натуру або
порнографічний контент.
error:
failed: Потік не вдалося запустити
connection_error: Помилка з'єднання
start_failed: Не вдалося запустити трансляцію, будь ласка, перевірте свій баланс

View File

@ -8,6 +8,7 @@ no_user_found: 未找到使用者
"@no_user_found":
description: 搜尋時未找到使用者
anon: 匿名
full_amount_sats: ${n:decimalPattern} Sats
viewers:
one: 1 個檢視器
other: ${n:decimalPattern} 觀眾
@ -21,10 +22,11 @@ stream:
ended: 結束
planned: 計劃
started: 開始 $timestamp
notification: ${name} 已啟用!
chat:
disabled: 關閉聊天
disabled_timeout: 超時過期: $time
timeout(rich): $mod 超時 $user for $time
timeout(rich): $mod 超時 $user for ${time}
"@timeout":
description: 顯示逾時事件的聊天訊息
ended: 串流結束
@ -121,6 +123,8 @@ settings:
disconnect_wallet: 斷開錢包
connect_1tap: 1 抽頭連接
paste: 貼上 URL
balance: 平衡
name: 錢包
error:
logged_out: 登出時無法連接錢包
nwc_auth_event_not_found: 未找到錢包認證事件
@ -131,3 +135,21 @@ login:
create: 創建帳戶
error:
invalid_key: 無效按鍵
live:
start: 開始直播
configure_stream: 設定串流
endpoint: 終點
accept_tos: 接受服務條款
balance_left:
zero:
other: ~${time}
title: 標題
summary: 摘要
image: 封面圖片
tags: 標籤
nsfw: NSFW 內容
nsfw_description: 請檢查此串流是否包含裸體或色情內容。
error:
failed: 串流失敗
connection_error: 連線錯誤
start_failed: 串流啟動失敗,請檢查您的餘額

View File

@ -38,8 +38,16 @@ class WalletConfig {
}
}
class WalletInfo {
final String name;
final int balance;
const WalletInfo({required this.name, required this.balance});
}
abstract class SimpleWallet {
Future<String> payInvoice(String pr);
Future<WalletInfo> getInfo();
}
class NWCWrapper extends SimpleWallet {
@ -60,6 +68,13 @@ class NWCWrapper extends SimpleWallet {
return rsp.preimage!;
}
}
@override
Future<WalletInfo> getInfo() async {
final info = await ndk.nwc.getInfo(_conn);
final balance = await ndk.nwc.getBalance(_conn);
return WalletInfo(name: info.alias, balance: balance.balanceSats);
}
}
class LoginAccount {
@ -68,6 +83,7 @@ class LoginAccount {
final String? privateKey;
final List<String>? signerRelays;
final WalletConfig? wallet;
final String? streamEndpoint;
SimpleWallet? _cachedWallet;
@ -77,6 +93,7 @@ class LoginAccount {
this.privateKey,
this.signerRelays,
this.wallet,
this.streamEndpoint,
});
static LoginAccount nip19(String key) {
@ -124,6 +141,7 @@ class LoginAccount {
"pubKey": acc?.pubkey,
"privateKey": acc?.privateKey,
"wallet": acc?.wallet?.toJson(),
"streamEndpoint": acc?.streamEndpoint,
};
static LoginAccount? fromJson(Map<String, dynamic> json) {
@ -147,6 +165,7 @@ class LoginAccount {
json.containsKey("wallet") && json["wallet"] != null
? WalletConfig.fromJson(json["wallet"])
: null,
streamEndpoint: json["streamEndpoint"],
);
}
return null;
@ -200,4 +219,21 @@ class LoginData extends ValueNotifier<LoginAccount?> {
}
}
}
void configure({
List<String>? signerRelays,
WalletConfig? wallet,
String? streamEndpoint,
}) {
if (value != null) {
value = LoginAccount(
type: value!.type,
pubkey: value!.pubkey,
privateKey: value!.privateKey,
signerRelays: signerRelays ?? value!.signerRelays,
wallet: wallet,
streamEndpoint: streamEndpoint ?? value!.streamEndpoint,
);
}
}
}

View File

@ -1,5 +1,6 @@
import 'dart:developer' as developer;
import 'package:audio_service/audio_service.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
@ -7,6 +8,9 @@ import 'package:zap_stream_flutter/app.dart';
import 'package:zap_stream_flutter/const.dart';
import 'package:zap_stream_flutter/i18n/strings.g.dart';
import 'package:zap_stream_flutter/notifications.dart';
import 'package:zap_stream_flutter/player.dart';
late final MainPlayer mainPlayer;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
@ -20,5 +24,14 @@ Future<void> main() async {
developer.log("Failed to setup notifications: $e");
});
mainPlayer = await AudioService.init(
builder: () => MainPlayer(),
config: AudioServiceConfig(
androidNotificationChannelId: "io.nostrlabs.zap_stream_flutter.player",
androidNotificationChannelName: "Player Status",
androidNotificationOngoing: true
),
);
runZapStream();
}

View File

@ -10,11 +10,14 @@ import 'package:flutter/foundation.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:ndk/ndk.dart';
import 'package:ndk_objectbox/ndk_objectbox.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:zap_stream_flutter/const.dart';
import 'package:http/http.dart' as http;
import 'package:zap_stream_flutter/firebase_options.dart';
import 'package:zap_stream_flutter/i18n/strings.g.dart';
import 'package:zap_stream_flutter/utils.dart';
import 'package:zap_stream_flutter/widgets/profile.dart';
class Notepush {
final String base;
@ -183,6 +186,82 @@ class NotificationsStore extends ValueNotifier<NotificationsState?> {
}
}
Future<void> _initLocalNotifications() async {
await localNotifications.initialize(
InitializationSettings(
android: AndroidInitializationSettings("@drawable/ic_stat_name"),
iOS: DarwinInitializationSettings(),
),
);
}
@pragma('vm:entry-point')
Future<void> _onBackgroundNotification(RemoteMessage msg) async {
await LocaleSettings.useDeviceLocale();
final cache = DbObjectBox(attach: true);
await _initLocalNotifications();
await _handleNotification(msg, cache);
}
Future<void> _onNotification(RemoteMessage msg) async {
await _handleNotification(msg, ndkCache);
}
Future<void> _handleNotification(RemoteMessage msg, DbObjectBox cache) async {
final notification = msg.notification;
if (notification != null && notification.android != null) {
final String? json = msg.data["nostr_event"];
final event =
json != null ? Nip01Event.fromJson(JsonCodec().decode(json)) : null;
await _showNotification(notification, ndkCache, event);
}
}
Future<void> _showNotification(
RemoteNotification notification,
DbObjectBox cache,
Nip01Event? event,
) async {
final stream = event != null ? StreamEvent(event) : null;
final hostProfile =
stream != null ? await cache.loadMetadata(stream.info.host) : null;
final newTitle =
hostProfile != null
? t.stream.notification(
name: ProfileNameWidget.nameFromProfile(hostProfile),
)
: null;
localNotifications.show(
notification.hashCode,
newTitle ?? notification.title,
stream?.info.title ?? notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
notification.android!.channelId ?? "fcm",
"Push Notifications",
category: AndroidNotificationCategory.social,
),
),
);
}
Future<void> _onOpenMessage(RemoteMessage msg) async {
try {
final notification = msg.notification;
final String? json = msg.data["nostr_event"];
if (notification != null && json != null) {
// Just launch the URL because we support deep links
final event = Nip01Event.fromJson(JsonCodec().decode(json));
final stream = StreamEvent(event);
launchUrl(Uri.parse("https://zap.stream/${stream.link}"));
}
} catch (e) {
developer.log("Failed to process push notification\n ${e.toString()}");
}
}
// global notifications store
final notifications = NotificationsStore(null);
@ -191,75 +270,48 @@ Future<void> setupNotifications() async {
final signer = ndk.accounts.getLoggedAccount()?.signer;
if (signer != null) {
final pusher = Notepush(dotenv.env["NOTEPUSH_URL"]!, signer: signer);
final fbase = FirebaseMessaging.instance;
FirebaseMessaging.onMessage.listen((msg) {
developer.log(msg.notification?.body ?? "");
final notification = msg.notification;
if (notification != null && notification.android != null) {
FlutterLocalNotificationsPlugin().show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
notification.android!.channelId ?? "fcm",
"fcm",
),
),
);
}
});
FirebaseMessaging.onMessageOpenedApp.listen((msg) {
try {
final notification = msg.notification;
final String? json = msg.data["nostr_event"];
if (notification != null && json != null) {
// Just launch the URL because we support deep links
final event = Nip01Event.fromJson(JsonCodec().decode(json));
final stream = StreamEvent(event);
launchUrl(Uri.parse("https://zap.stream/${stream.link}"));
}
} catch (e) {
developer.log("Failed to process push notification\n ${e.toString()}");
}
});
final settings = await fbase.requestPermission(provisional: true);
await fbase.setAutoInitEnabled(true);
await fbase.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
if (Platform.isIOS) {
final apnsToken = await FirebaseMessaging.instance.getAPNSToken();
if (apnsToken == null) {
throw "APNS token not availble";
}
}
await localNotifications.initialize(
InitializationSettings(
android: AndroidInitializationSettings("@mipmap/ic_launcher"),
iOS: DarwinInitializationSettings(),
),
);
fbase.onTokenRefresh.listen((token) async {
developer.log("NEW TOKEN: $token");
await pusher.register(token);
await pusher.setNotificationSettings(token, [30_311]);
});
final fcmToken = await FirebaseMessaging.instance.getToken();
if (fcmToken == null) {
throw "Push token is null";
}
await pusher.register(fcmToken);
await pusher.setNotificationSettings(fcmToken, [30_311]);
notifications.value = await NotificationsState.init(
settings.authorizationStatus,
);
await configureNotifications(signer);
}
}
Future<void> configureNotifications(EventSigner signer) async {
FirebaseMessaging.onMessage.listen(_onNotification);
//FirebaseMessaging.onBackgroundMessage(_onBackgroundNotification);
FirebaseMessaging.onMessageOpenedApp.listen(_onOpenMessage);
final settings = await FirebaseMessaging.instance.requestPermission(
provisional: true,
);
await FirebaseMessaging.instance.setAutoInitEnabled(true);
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
if (Platform.isIOS) {
final apnsToken = await FirebaseMessaging.instance.getAPNSToken();
if (apnsToken == null) {
throw "APNS token not availble";
}
}
await _initLocalNotifications();
final pusher = Notepush(dotenv.env["NOTEPUSH_URL"]!, signer: signer);
FirebaseMessaging.instance.onTokenRefresh.listen((token) async {
developer.log("NEW TOKEN: $token");
await pusher.register(token);
await pusher.setNotificationSettings(token, [30_311]);
});
final fcmToken = await FirebaseMessaging.instance.getToken();
if (fcmToken == null) {
throw "Push token is null";
}
await pusher.register(fcmToken);
await pusher.setNotificationSettings(fcmToken, [30_311]);
notifications.value = await NotificationsState.init(
settings.authorizationStatus,
);
}

429
lib/pages/live.dart Normal file
View File

@ -0,0 +1,429 @@
import 'dart:async';
import 'dart:developer' as developer;
import 'package:apivideo_live_stream/apivideo_live_stream.dart';
import 'package:collection/collection.dart';
import 'package:duration/duration.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:go_router/go_router.dart';
import 'package:ndk/ndk.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
import 'package:zap_stream_flutter/api.dart';
import 'package:zap_stream_flutter/const.dart';
import 'package:zap_stream_flutter/i18n/strings.g.dart';
import 'package:zap_stream_flutter/rx_filter.dart';
import 'package:zap_stream_flutter/theme.dart';
import 'package:zap_stream_flutter/utils.dart';
import 'package:zap_stream_flutter/widgets/button.dart';
import 'package:zap_stream_flutter/widgets/chat.dart';
import 'package:zap_stream_flutter/widgets/pill.dart';
import 'package:zap_stream_flutter/widgets/stream_config.dart';
Future<bool?> showExitStreamDialog(BuildContext context) {
return showDialog<bool>(
context: context,
barrierDismissible: false,
useRootNavigator: false,
builder: (context) {
return Dialog(
child: Container(
padding: EdgeInsets.all(10),
child: Column(
mainAxisSize: MainAxisSize.min,
spacing: 16,
children: [
Text("Exit live stream?", style: TextStyle(fontSize: 24)),
Row(
spacing: 16,
children: [
Flexible(
child: BasicButton.text(
"Yes, stop stream",
onTap: (context) => context.pop(true),
),
),
Flexible(
child: BasicButton.text(
"No",
onTap: (context) => context.pop(false),
),
),
],
),
],
),
),
);
},
);
}
class LivePage extends StatefulWidget {
const LivePage({super.key});
@override
State<StatefulWidget> createState() => _LivePage();
}
class _LivePage extends State<LivePage>
implements ApiVideoLiveStreamEventsListener {
late final ApiVideoLiveStreamController _controller;
late final ZapStreamApi _api;
AccountInfo? _account;
late final Timer _accountTimer;
bool _streaming = false;
Future<void> _reloadAccount() async {
final info = await _api.getAccountInfo();
setState(() {
_account = info;
});
}
@override
void initState() {
_controller = ApiVideoLiveStreamController(
initialAudioConfig: AudioConfig(),
initialVideoConfig: VideoConfig.withDefaultBitrate(),
);
_controller.initialize();
_api = ZapStreamApi.instance();
_reloadAccount();
_accountTimer = Timer.periodic(Duration(seconds: 30), (_) async {
await _reloadAccount();
});
_controller.addEventsListener(this);
WakelockPlus.enable();
super.initState();
}
@override
void dispose() {
_accountTimer.cancel();
_controller.stopStreaming();
_controller.dispose();
WakelockPlus.disable();
super.dispose();
}
void _showError(BuildContext context, String msg, {Exception? error}) {
if (error != null) {
developer.log(error.toString());
}
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: WARNING,
content: Text(msg, style: TextStyle(fontWeight: FontWeight.bold)),
),
);
}
String _calcTimeRemaining(IngestEndpoint endpoint, double balance) {
if (endpoint.cost.rate == 0) {
return "";
}
final units = balance / endpoint.cost.rate;
if (endpoint.cost.unit == "min") {
return Duration(
seconds: (units * 60).clamp(0, double.infinity).floor(),
).pretty(abbreviated: true);
}
return "0s";
}
@override
Widget build(BuildContext context) {
final mq = MediaQuery.of(context);
return PopScope(
canPop: false,
onPopInvokedWithResult: (didPop, result) async {
if (_streaming) {
final go = await showExitStreamDialog(context);
if (context.mounted) {
if (go == true) {
context.go("/");
}
}
} else {
context.go("/");
}
},
child: ValueListenableBuilder(
valueListenable: loginData,
builder: (context, state, _) {
final endpoint = _account?.endpoints.firstWhereOrNull(
(e) => e.name == state?.streamEndpoint,
);
final balance = _account?.balance ?? 0;
return RxFilter<Nip01Event>(
Key("live-stream"),
filters: [
Filter(
kinds: [30_311],
limit: 100,
pTags: [loginData.value!.pubkey],
),
Filter(
kinds: [30_311],
limit: 100,
authors: [loginData.value!.pubkey],
),
],
builder: (context, streamState) {
final ev = streamState
?.sortedBy((e) => e.createdAt)
.firstWhereOrNull((e) => e.getFirstTag("status") == "live");
final stream = ev != null ? StreamEvent(ev) : null;
return Stack(
children: [
ApiVideoCameraPreview(controller: _controller),
Positioned(
top: 10,
left: 10,
width: mq.size.width - 20,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
PillWidget(
color: LAYER_2,
child: Row(
spacing: 4,
children: [
Text(t.full_amount_sats(n: balance)),
if (endpoint != null)
Text(
t.live.balance_left(
n: endpoint.cost.rate,
time: _calcTimeRemaining(endpoint, balance),
),
style: TextStyle(color: LAYER_5),
),
],
),
),
if ((stream?.info.participants ?? 0) > 0)
PillWidget(
color: LAYER_2,
child: Text(
t.viewers(n: stream?.info.participants ?? 0),
style: TextStyle(
color: Colors.white,
fontSize: 14,
),
),
),
],
),
),
if (_account != null)
Positioned(
width: mq.size.width,
bottom: 15,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton.filled(
iconSize: 40,
style: ButtonStyle(
iconColor: WidgetStateColor.resolveWith(
(_) => FONT_COLOR,
),
backgroundColor: WidgetStateColor.resolveWith(
(_) => LAYER_3,
),
),
onPressed: () {
_controller.switchCamera();
},
icon: Icon(Icons.cameraswitch_rounded),
),
Spacer(),
if (_account != null && !_account!.tos.accepted)
Column(
spacing: 16,
children: [
BasicButton.text(
"Read TOS",
onTap: (context) {
if (_account?.tos.link != null) {
launchUrl(Uri.parse(_account!.tos.link!));
}
},
),
BasicButton.text(
t.live.accept_tos,
color: WARNING,
onTap: (context) {
_api
.acceptTos()
.then((_) {
_reloadAccount();
})
.catchError((e) {
_showError(
context,
e.toString(),
error: e,
);
});
},
),
],
)
else if (state?.streamEndpoint == null ||
endpoint == null)
BasicButton.text(
t.live.configure_stream,
color: WARNING,
),
if (endpoint != null)
IconButton.filled(
iconSize: 40,
style: ButtonStyle(
iconColor: WidgetStateColor.resolveWith(
(_) => WARNING,
),
backgroundColor: WidgetStateColor.resolveWith(
(_) => LAYER_3,
),
),
onPressed: () async {
if (_streaming) {
_controller.stopStreaming().catchError((e) {
_showError(context, e.toString(), error: e);
});
} else {
_controller
.startStreaming(
streamKey: endpoint.key,
url: endpoint.url,
)
.catchError((e) {
_showError(
context,
t.live.error.start_failed,
error: e,
);
});
}
},
icon: Icon(
_streaming ? Icons.stop : Icons.circle,
),
),
Spacer(),
IconButton.filled(
iconSize: 40,
style: ButtonStyle(
iconColor: WidgetStateColor.resolveWith(
(_) => FONT_COLOR,
),
backgroundColor: WidgetStateColor.resolveWith(
(_) => LAYER_3,
),
),
onPressed: () {
showModalBottomSheet(
context: context,
constraints: BoxConstraints.expand(),
builder: (context) {
return StreamConfigWidget(
api: _api,
account: _account!,
hideEndpointConfig: _streaming,
);
},
).then((_) {
_reloadAccount();
});
},
icon: Icon(Icons.settings),
),
],
),
),
if (_account != null && stream != null)
Positioned(
bottom: 80,
child: Container(
width: mq.size.width,
padding: EdgeInsets.symmetric(horizontal: 10),
constraints: BoxConstraints(
maxHeight: mq.size.height * 0.3,
minHeight: 200,
),
child: ShaderMask(
shaderCallback: (Rect bounds) {
return LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: [
Colors.white.withAlpha(255),
Colors.white.withAlpha(200),
Colors.white.withAlpha(0),
],
stops: [0.0, 0.7, 1.0],
).createShader(bounds);
},
blendMode: BlendMode.dstIn,
child: ChatWidget(
stream: stream,
showGoals: false,
showTopZappers: false,
),
),
),
),
],
);
},
);
},
),
);
}
@override
get onConnectionFailed => (s) {
developer.log(s, name: "onConnectionFailed");
_showError(context, t.live.error.connection_error);
};
@override
get onConnectionSuccess => () {
developer.log("Connected", name: "onConnectionSuccess");
setState(() {
_streaming = true;
});
};
@override
get onDisconnection => () {
developer.log("Disconnected", name: "onDisconnection");
setState(() {
_streaming = false;
});
};
@override
get onError => (e) {
developer.log(e.toString(), name: "onError");
if (e is PlatformException) {
if (e.details is String &&
(e.details as String).contains("Connection error")) {
_showError(context, t.live.error.connection_error, error: e);
}
}
};
@override
get onVideoSizeChanged => (s) {
developer.log(s.toString(), name: "onVideoSizeChanged");
};
}

View File

@ -38,6 +38,7 @@ class _NewAccountPage extends State<NewAccountPage> {
pubKey: _privateKey.publicKey,
name: _name.text,
picture: _avatar,
lud16: "${_privateKey.publicKey}@zap.stream",
),
);
}

View File

@ -12,6 +12,7 @@ import 'package:zap_stream_flutter/widgets/button.dart';
import 'package:zap_stream_flutter/widgets/button_follow.dart';
import 'package:zap_stream_flutter/widgets/header.dart';
import 'package:zap_stream_flutter/widgets/nostr_text.dart';
import 'package:zap_stream_flutter/widgets/notifications_button.dart';
import 'package:zap_stream_flutter/widgets/profile.dart';
import 'package:zap_stream_flutter/widgets/stream_grid.dart';
@ -91,7 +92,14 @@ class ProfilePage extends StatelessWidget {
),
],
),
if (!isMe) FollowButton(pubkey: hexPubkey),
if (!isMe)
Row(
spacing: 8,
children: [
FollowButton(pubkey: hexPubkey),
NotificationsButtonWidget(pubkey: hexPubkey),
],
),
Text(
t.profile.past_streams,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.w600),

View File

@ -87,7 +87,7 @@ class _Inner extends State<SettingsWalletPage> with ProtocolListener {
queryParameters: {
"relay": nwcRelays,
"name": "zap.stream",
"request_methods": "pay_invoice",
"request_methods": "pay_invoice get_info get_balance",
"icon": "https://zap.stream/logo.png",
"return_to": nwaHandlerUrl,
},
@ -100,13 +100,7 @@ class _Inner extends State<SettingsWalletPage> with ProtocolListener {
}
_setWallet(WalletConfig? cfg) {
loginData.value = LoginAccount(
type: loginData.value!.type,
pubkey: loginData.value!.pubkey,
privateKey: loginData.value!.privateKey,
signerRelays: loginData.value!.signerRelays,
wallet: cfg,
);
loginData.configure(wallet: cfg);
}
@override
@ -174,13 +168,43 @@ class _Inner extends State<SettingsWalletPage> with ProtocolListener {
],
);
} else {
return BasicButton.text(
t.settings.wallet.disconnect_wallet,
onTap: (context) {
_setWallet(null);
if (context.mounted) {
context.pop();
}
return FutureBuilder(
future: () async {
final wallet = await state!.getWallet();
return await wallet?.getInfo();
}(),
builder: (context, state) {
return Column(
spacing: 8,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Wallet: ${state.data?.name ?? ""}",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 24),
),
Text.rich(
TextSpan(
style: TextStyle(fontWeight: FontWeight.w500),
children: [
TextSpan(text: t.settings.wallet.balance),
TextSpan(text: ": "),
TextSpan(
text: t.full_amount_sats(n: state.data?.balance ?? 0),
),
],
),
),
BasicButton.text(
t.settings.wallet.disconnect_wallet,
onTap: (context) {
_setWallet(null);
if (context.mounted) {
context.pop();
}
},
),
],
);
},
);
}

View File

@ -17,7 +17,7 @@ import 'package:zap_stream_flutter/widgets/notifications_button.dart';
import 'package:zap_stream_flutter/widgets/pill.dart';
import 'package:zap_stream_flutter/widgets/profile.dart';
import 'package:zap_stream_flutter/widgets/stream_info.dart';
import 'package:zap_stream_flutter/widgets/video_player.dart';
import 'package:zap_stream_flutter/widgets/video_player_main.dart';
import 'package:zap_stream_flutter/widgets/zap.dart';
class StreamPage extends StatefulWidget {
@ -141,11 +141,11 @@ class _StreamPage extends State<StreamPage> with RouteAware {
aspectRatio: 16 / 9,
child:
(stream.info.stream != null && !_offScreen)
? VideoPlayerWidget(
? MainVideoPlayerWidget(
url: stream.info.stream!,
placeholder: stream.info.image,
aspectRatio: 16 / 9,
isLive: true,
title: stream.info.title,
)
: (stream.info.image?.isNotEmpty ?? false)
? ProxyImg(url: stream.info.image)
@ -159,7 +159,7 @@ class _StreamPage extends State<StreamPage> with RouteAware {
ProfileWidget.pubkey(
stream.info.host,
children: [
NotificationsButtonWidget(stream: widget.stream),
NotificationsButtonWidget(pubkey: widget.stream.info.host),
BasicButton(
Row(
children: [Icon(Icons.bolt, size: 14), Text(t.zap.button_zap)],

101
lib/player.dart Normal file
View File

@ -0,0 +1,101 @@
import 'package:audio_service/audio_service.dart';
import 'package:chewie/chewie.dart';
import 'package:video_player/video_player.dart';
import 'package:zap_stream_flutter/const.dart';
import 'package:zap_stream_flutter/imgproxy.dart';
class MainPlayer extends BaseAudioHandler {
VideoPlayerController? _controller;
ChewieController? _chewieController;
ChewieController? get chewie {
return _chewieController;
}
@override
Future<void> play() async {
await _chewieController?.play();
}
@override
Future<void> pause() async {
await _chewieController?.pause();
}
@override
Future<void> stop() async {
await _chewieController?.pause();
}
void loadUrl(
String url, {
String? title,
bool? autoPlay,
double? aspectRatio,
bool? isLive,
String? placeholder,
String? artist,
}) {
if (_chewieController != null) {
_chewieController!.dispose();
_controller!.dispose();
}
_controller = VideoPlayerController.networkUrl(
Uri.parse(url),
httpHeaders: Map.from({"user-agent": userAgent}),
videoPlayerOptions: VideoPlayerOptions(allowBackgroundPlayback: true),
);
_chewieController = ChewieController(
videoPlayerController: _controller!,
autoPlay: autoPlay ?? true,
aspectRatio: aspectRatio,
isLive: isLive ?? false,
autoInitialize: true,
allowedScreenSleep: false,
placeholder:
(placeholder?.isNotEmpty ?? false)
? ProxyImg(url: placeholder!)
: null,
);
// insert media item
mediaItem.add(
MediaItem(
id: url.hashCode.toString(),
title: title ?? url,
artist: artist,
isLive: _chewieController!.isLive,
artUri:
(placeholder?.isNotEmpty ?? false) ? Uri.parse(placeholder!) : null,
),
);
_chewieController!.videoPlayerController.addListener(updatePlayerState);
}
void updatePlayerState() {
final isPlaying =
_chewieController?.videoPlayerController.value.isPlaying ?? false;
playbackState.add(
playbackState.value.copyWith(
controls: [
if (playbackState.value.playing)
MediaControl.pause
else
MediaControl.play,
MediaControl.stop,
],
playing: isPlaying,
processingState: switch (_chewieController
?.videoPlayerController
.value
.isInitialized) {
true => AudioProcessingState.ready,
false => AudioProcessingState.idle,
_ => AudioProcessingState.completed,
},
),
);
}
}

View File

@ -375,14 +375,6 @@ Map<String, TopZaps> topZapReceiver(Iterable<ZapReceipt> zaps) {
);
}
String formatSecondsToHHMMSS(int seconds) {
int hours = seconds ~/ 3600;
int minutes = (seconds % 3600) ~/ 60;
int remainingSeconds = seconds % 60;
return '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${remainingSeconds.toString().padLeft(2, '0')}';
}
String bech32ToHex(String bech32) {
final decoder = Bech32Decoder();
final data = decoder.convert(bech32, 10_000);

View File

@ -3,6 +3,7 @@ import 'package:zap_stream_flutter/theme.dart';
class BasicButton extends StatelessWidget {
final Widget? child;
final Color? color;
final BoxDecoration? decoration;
final EdgeInsetsGeometry? padding;
final EdgeInsetsGeometry? margin;
@ -12,6 +13,7 @@ class BasicButton extends StatelessWidget {
const BasicButton(
this.child, {
super.key,
this.color,
this.decoration,
this.padding,
this.margin,
@ -21,6 +23,7 @@ class BasicButton extends StatelessWidget {
static Widget text(
String text, {
Color? color,
BoxDecoration? decoration,
EdgeInsetsGeometry? padding,
EdgeInsetsGeometry? margin,
@ -46,6 +49,7 @@ class BasicButton extends StatelessWidget {
),
),
disabled: disabled,
color: color,
decoration: decoration,
padding: padding ?? EdgeInsets.symmetric(vertical: 4, horizontal: 12),
margin: margin,
@ -55,12 +59,17 @@ class BasicButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
assert(
!(color != null && decoration != null),
"Cant set both 'color' and 'decoration'",
);
final defaultBr = BorderRadius.all(Radius.circular(100));
final inner = Container(
padding: padding,
margin: margin,
decoration:
decoration ?? BoxDecoration(color: LAYER_2, borderRadius: defaultBr),
decoration ??
BoxDecoration(color: color ?? LAYER_2, borderRadius: defaultBr),
child: Center(child: child),
);
return GestureDetector(

View File

@ -18,8 +18,17 @@ import 'package:zap_stream_flutter/widgets/profile.dart';
class ChatWidget extends StatelessWidget {
final StreamEvent stream;
final bool? showGoals;
final bool? showTopZappers;
final bool? showRaids;
const ChatWidget({super.key, required this.stream});
const ChatWidget({
super.key,
required this.stream,
this.showGoals,
this.showTopZappers,
this.showRaids,
});
@override
Widget build(BuildContext context) {
@ -31,7 +40,8 @@ class ChatWidget extends StatelessWidget {
var filters = [
Filter(kinds: [1311, 9735], limit: 200, aTags: [stream.aTag]),
Filter(kinds: [1312, 1313], limit: 200, aTags: [stream.aTag]),
if (showRaids ?? true)
Filter(kinds: [1312, 1313], limit: 200, aTags: [stream.aTag]),
Filter(kinds: [Nip51List.kMute], authors: moderators),
Filter(kinds: [1314], authors: moderators),
Filter(kinds: [8], authors: [stream.info.host]),
@ -108,10 +118,13 @@ class ChatWidget extends StatelessWidget {
spacing: 8,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (zaps.isNotEmpty) _TopZappersWidget(events: zaps),
if (stream.info.goal != null) GoalWidget.id(stream.info.goal!),
if (zaps.isNotEmpty && (showTopZappers ?? true))
_TopZappersWidget(events: zaps),
if (stream.info.goal != null && (showGoals ?? true))
GoalWidget.id(stream.info.goal!),
Expanded(
child: ListView.builder(
padding: EdgeInsets.only(top: 80),
reverse: true,
itemCount: filteredChat.length,
itemBuilder: (ctx, idx) {

View File

@ -24,6 +24,7 @@ class __WriteMessageWidget extends State<WriteMessageWidget> {
OverlayEntry? _entry;
late FocusNode _focusNode;
List<List<String>> _tags = List.empty(growable: true);
final GlobalKey _positioned = GlobalKey();
@override
void initState() {
@ -69,7 +70,8 @@ class __WriteMessageWidget extends State<WriteMessageWidget> {
_entry = null;
}
final pos = context.findRenderObject() as RenderBox?;
final pos = _positioned.currentContext!.findRenderObject() as RenderBox?;
final posGlobal = pos?.localToGlobal(Offset.zero);
_entry = OverlayEntry(
builder: (context) {
return ValueListenableBuilder(
@ -85,12 +87,13 @@ class __WriteMessageWidget extends State<WriteMessageWidget> {
if (search.isEmpty) {
return SizedBox();
}
final mq = MediaQuery.of(context);
return Stack(
children: [
Positioned(
left: 0,
bottom: (pos?.paintBounds.bottom ?? 0),
width: MediaQuery.of(context).size.width,
left: posGlobal?.dx,
bottom: mq.size.height - (posGlobal?.dy ?? 0) - 30,
width: pos?.size.width,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 4, vertical: 8),
decoration: BoxDecoration(
@ -162,15 +165,17 @@ class __WriteMessageWidget extends State<WriteMessageWidget> {
_entry = null;
}
final pos = context.findRenderObject() as RenderBox?;
final pos = _positioned.currentContext!.findRenderObject() as RenderBox?;
final posGlobal = pos?.localToGlobal(Offset.zero);
_entry = OverlayEntry(
builder: (context) {
final mq = MediaQuery.of(context);
return Stack(
children: [
Positioned(
left: 0,
bottom: (pos?.paintBounds.bottom ?? 0),
width: MediaQuery.of(context).size.width,
left: posGlobal?.dx,
bottom: mq.size.height - (posGlobal?.dy ?? 0) - 30,
width: pos?.size.width,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 4, vertical: 8),
decoration: BoxDecoration(
@ -239,9 +244,13 @@ class __WriteMessageWidget extends State<WriteMessageWidget> {
final isLogin = ndk.accounts.isLoggedIn;
return Container(
key: _positioned,
margin: EdgeInsets.fromLTRB(4, 8, 4, 0),
padding: EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration(color: LAYER_2, borderRadius: DEFAULT_BR),
decoration: BoxDecoration(
color: LAYER_2.withAlpha(200),
borderRadius: DEFAULT_BR,
),
child:
canSign
? Row(

View File

@ -27,7 +27,19 @@ class ChatZapWidget extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_zapperRowZap(context, parsed),
if (parsed.comment?.isNotEmpty ?? false) NoteText(event: zap),
if (parsed.comment?.isNotEmpty ?? false)
RichText(
text: TextSpan(
children: textToSpans(
context,
parsed.comment ?? "",
[],
parsed.sender ?? "",
showEmbeds: false,
embedMedia: false,
),
),
),
],
),
);

View File

@ -6,6 +6,7 @@ import 'package:zap_stream_flutter/i18n/strings.g.dart';
import 'package:zap_stream_flutter/const.dart';
import 'package:zap_stream_flutter/theme.dart';
import 'package:zap_stream_flutter/widgets/avatar.dart';
import 'package:zap_stream_flutter/widgets/button.dart';
class HeaderWidget extends StatefulWidget {
const HeaderWidget({super.key});
@ -39,12 +40,36 @@ class LoginButtonWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
if (ndk.accounts.isLoggedIn) {
return GestureDetector(
onTap:
() => context.go(
"/p/${Nip19.encodePubKey(ndk.accounts.getPublicKey()!)}",
return Row(
spacing: 8,
children: [
BasicButton(
padding: EdgeInsets.symmetric(horizontal: 10),
decoration: BoxDecoration(
border: Border.all(color: WARNING),
borderRadius: DEFAULT_BR,
),
child: AvatarWidget.pubkey(ndk.accounts.getPublicKey()!),
Row(
spacing: 4,
children: [
Icon(Icons.videocam),
Text(
t.live.start,
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
onTap: (context) => context.push("/live"),
),
GestureDetector(
onTap:
() => context.push(
"/p/${Nip19.encodePubKey(ndk.accounts.getPublicKey()!)}",
),
child: AvatarWidget.pubkey(ndk.accounts.getPublicKey()!),
),
],
);
} else {
return GestureDetector(
@ -59,10 +84,7 @@ class LoginButtonWidget extends StatelessWidget {
),
child: Row(
spacing: 8,
children: [
Text(t.button.login),
Icon(Icons.login, size: 16),
],
children: [Text(t.button.login), Icon(Icons.login, size: 16)],
),
),
);

View File

@ -1,7 +1,7 @@
import 'dart:async';
import 'package:duration/duration.dart';
import 'package:flutter/material.dart';
import 'package:zap_stream_flutter/theme.dart';
import 'package:zap_stream_flutter/utils.dart';
import 'package:zap_stream_flutter/widgets/pill.dart';
class LiveTimerWidget extends StatefulWidget {
@ -37,12 +37,13 @@ class _LiveTimerWidget extends State<LiveTimerWidget> {
return PillWidget(
color: LAYER_2,
child: Text(
formatSecondsToHHMMSS(
((DateTime.now().millisecondsSinceEpoch -
widget.started.millisecondsSinceEpoch) /
1000)
.toInt(),
),
Duration(
seconds:
((DateTime.now().millisecondsSinceEpoch -
widget.started.millisecondsSinceEpoch) /
1000)
.toInt(),
).pretty(abbreviated: true),
),
);
}

Some files were not shown because too many files have changed in this diff Show More