From f33ef22446a4eb7674c145d546598e34a8868e6c Mon Sep 17 00:00:00 2001 From: otsmr Date: Fri, 25 Sep 2026 11:56:30 +0200 Subject: [PATCH] add arabic language --- .gitignore | 1 + .../app/src/main/res/values-ar/strings.xml | 9 + lib/app.dart | 8 +- lib/core/frb_generated.dart | 98 +- lib/core/user_config.dart | 6 + .../generated/app_localizations.dart | 142 +- .../generated/app_localizations_ar.dart | 2854 +++++++++++++++++ .../generated/app_localizations_de.dart | 70 +- .../generated/app_localizations_en.dart | 69 +- lib/src/localization/translations/ar.arb | 796 +++++ lib/src/localization/translations/de.arb | 24 +- lib/src/localization/translations/en.arb | 24 +- lib/src/providers/settings.provider.dart | 29 + .../visual/views/contact/contact.view.dart | 2 +- lib/src/visual/views/groups/group.view.dart | 2 +- .../views/settings/appearance.view.dart | 65 + .../views/webxdc/webxdc_chat_apps.view.dart | 2 +- rust/src/frb_generated.rs | 4 + rust/src/native/notifications.rs | 16 +- rust/src/services/notifications.rs | 23 +- rust/src/user_config.rs | 4 + scripts/check_arb.py | 41 +- 22 files changed, 3902 insertions(+), 387 deletions(-) create mode 100644 android/app/src/main/res/values-ar/strings.xml create mode 100644 lib/src/localization/generated/app_localizations_ar.dart create mode 100644 lib/src/localization/translations/ar.arb diff --git a/.gitignore b/.gitignore index a555743a..13a35b50 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,4 @@ fastlane/repo/status/running.json # Widget Preview related .widget_preview/ ios/build/ +APKS/*.apk diff --git a/android/app/src/main/res/values-ar/strings.xml b/android/app/src/main/res/values-ar/strings.xml new file mode 100644 index 00000000..9e77b816 --- /dev/null +++ b/android/app/src/main/res/values-ar/strings.xml @@ -0,0 +1,9 @@ + + + صور يرسلها إليك أصدقاؤك + اختر مجموعات جهات الاتصال + اختر من يمكنه إرسال الصور إلى هذه الأداة. + افتح twonly مرة واحدة لتحميل مجموعات جهات الاتصال الخاصة بك. + إضافة الأداة + أحدث صورة في أداتك + diff --git a/lib/app.dart b/lib/app.dart index 00a8bb7e..18791223 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -107,11 +107,6 @@ class _AppState extends State with WidgetsBindingObserver { GlobalCupertinoLocalizations.delegate, ]; - const supportedLocales = [ - Locale('en', ''), - Locale('de', ''), - ]; - final settings = context.watch(); if (widget.storageError) { @@ -119,6 +114,7 @@ class _AppState extends State with WidgetsBindingObserver { localizationsDelegates: localizationsDelegates, debugShowCheckedModeBanner: false, supportedLocales: supportedLocales, + locale: settings.locale, title: 'twonly', theme: getLightTheme(settings.primaryColor), darkTheme: getDarkTheme(settings.primaryColor), @@ -132,6 +128,7 @@ class _AppState extends State with WidgetsBindingObserver { localizationsDelegates: localizationsDelegates, debugShowCheckedModeBanner: false, supportedLocales: supportedLocales, + locale: settings.locale, title: 'twonly', theme: getLightTheme(settings.primaryColor), darkTheme: getDarkTheme(settings.primaryColor), @@ -145,6 +142,7 @@ class _AppState extends State with WidgetsBindingObserver { localizationsDelegates: localizationsDelegates, debugShowCheckedModeBanner: false, supportedLocales: supportedLocales, + locale: settings.locale, title: 'twonly', theme: getLightTheme(settings.primaryColor), darkTheme: getDarkTheme(settings.primaryColor), diff --git a/lib/core/frb_generated.dart b/lib/core/frb_generated.dart index 89416fa6..3dc9f5ff 100644 --- a/lib/core/frb_generated.dart +++ b/lib/core/frb_generated.dart @@ -7905,8 +7905,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { UserConfig dco_decode_user_config(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 62) - throw Exception('unexpected arr length: expect 62 but see ${arr.length}'); + if (arr.length != 63) + throw Exception('unexpected arr length: expect 63 but see ${arr.length}'); return UserConfig( userId: dco_decode_i_64(arr[0]), username: dco_decode_String(arr[1]), @@ -7926,55 +7926,56 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { additionalUserInvites: dco_decode_opt_String(arr[15]), themeMode: dco_decode_theme_mode(arr[16]), primaryColorValue: dco_decode_opt_box_autoadd_i_64(arr[17]), - defaultShowTime: dco_decode_opt_box_autoadd_i_64(arr[18]), - requestedAudioPermission: dco_decode_bool(arr[19]), - enableDatabaseLogging: dco_decode_bool(arr[20]), - automaticallyMarkEqualMediaFilesAsOpened: dco_decode_bool(arr[21]), - showNewsShortcut: dco_decode_bool(arr[22]), - showShowImagePreviewWhenSending: dco_decode_bool(arr[23]), - startWithCameraOpen: dco_decode_bool(arr[24]), - preSelectedEmojies: dco_decode_opt_list_String(arr[25]), - autoDownloadOptions: dco_decode_opt_Map_String_list_String_None(arr[26]), - storeMediaFilesInGallery: dco_decode_bool(arr[27]), - storeLocationInMemories: dco_decode_bool(arr[28]), - autoStoreAllSendUnlimitedMediaFiles: dco_decode_bool(arr[29]), - typingIndicators: dco_decode_bool(arr[30]), - showRestoreFlame: dco_decode_bool(arr[31]), - myBestFriendGroupId: dco_decode_opt_String(arr[32]), + language: dco_decode_opt_String(arr[18]), + defaultShowTime: dco_decode_opt_box_autoadd_i_64(arr[19]), + requestedAudioPermission: dco_decode_bool(arr[20]), + enableDatabaseLogging: dco_decode_bool(arr[21]), + automaticallyMarkEqualMediaFilesAsOpened: dco_decode_bool(arr[22]), + showNewsShortcut: dco_decode_bool(arr[23]), + showShowImagePreviewWhenSending: dco_decode_bool(arr[24]), + startWithCameraOpen: dco_decode_bool(arr[25]), + preSelectedEmojies: dco_decode_opt_list_String(arr[26]), + autoDownloadOptions: dco_decode_opt_Map_String_list_String_None(arr[27]), + storeMediaFilesInGallery: dco_decode_bool(arr[28]), + storeLocationInMemories: dco_decode_bool(arr[29]), + autoStoreAllSendUnlimitedMediaFiles: dco_decode_bool(arr[30]), + typingIndicators: dco_decode_bool(arr[31]), + showRestoreFlame: dco_decode_bool(arr[32]), + myBestFriendGroupId: dco_decode_opt_String(arr[33]), signalLastSignedPreKeyUpdated: dco_decode_opt_box_autoadd_Chrono_Utc( - arr[33], - ), - signalLastPqcPreKeysUploaded: dco_decode_opt_box_autoadd_Chrono_Utc( arr[34], ), - allowErrorTrackingViaSentry: dco_decode_bool(arr[35]), - screenLockEnabled: dco_decode_bool(arr[36]), - isCloudBackupEnabled: dco_decode_bool(arr[37]), - isUserDiscoveryEnabled: dco_decode_bool(arr[38]), - requiredSendImages: dco_decode_i_64(arr[39]), - userDiscoveryThreshold: dco_decode_u_8(arr[40]), - userDiscoveryRequiresManualApproval: dco_decode_bool(arr[41]), - userDiscoverySharePromotion: dco_decode_bool(arr[42]), - userDiscoveryInitializationError: dco_decode_bool(arr[43]), - askForFriendPromotions: dco_decode_bool(arr[44]), - currentPreKeyIndexStart: dco_decode_i_64(arr[45]), - currentSignedPreKeyIndexStart: dco_decode_i_64(arr[46]), - lastChangeLogHash: dco_decode_opt_list_prim_u_8_strict(arr[47]), - hideChangeLog: dco_decode_bool(arr[48]), - hideMemoriesBackupPromo: dco_decode_bool(arr[49]), - hideWidgetShareExplainer: dco_decode_bool(arr[50]), - updateFcmToken: dco_decode_bool(arr[51]), - canUseLoginTokenForAuth: dco_decode_bool(arr[52]), - twonlySafeBackup: dco_decode_opt_box_autoadd_twonly_safe_backup(arr[53]), - isBackupEnabled: dco_decode_bool(arr[54]), + signalLastPqcPreKeysUploaded: dco_decode_opt_box_autoadd_Chrono_Utc( + arr[35], + ), + allowErrorTrackingViaSentry: dco_decode_bool(arr[36]), + screenLockEnabled: dco_decode_bool(arr[37]), + isCloudBackupEnabled: dco_decode_bool(arr[38]), + isUserDiscoveryEnabled: dco_decode_bool(arr[39]), + requiredSendImages: dco_decode_i_64(arr[40]), + userDiscoveryThreshold: dco_decode_u_8(arr[41]), + userDiscoveryRequiresManualApproval: dco_decode_bool(arr[42]), + userDiscoverySharePromotion: dco_decode_bool(arr[43]), + userDiscoveryInitializationError: dco_decode_bool(arr[44]), + askForFriendPromotions: dco_decode_bool(arr[45]), + currentPreKeyIndexStart: dco_decode_i_64(arr[46]), + currentSignedPreKeyIndexStart: dco_decode_i_64(arr[47]), + lastChangeLogHash: dco_decode_opt_list_prim_u_8_strict(arr[48]), + hideChangeLog: dco_decode_bool(arr[49]), + hideMemoriesBackupPromo: dco_decode_bool(arr[50]), + hideWidgetShareExplainer: dco_decode_bool(arr[51]), + updateFcmToken: dco_decode_bool(arr[52]), + canUseLoginTokenForAuth: dco_decode_bool(arr[53]), + twonlySafeBackup: dco_decode_opt_box_autoadd_twonly_safe_backup(arr[54]), + isBackupEnabled: dco_decode_bool(arr[55]), passwordLessRecovery: - dco_decode_opt_box_autoadd_passwordless_recovery_config(arr[55]), - fcmToken: dco_decode_opt_String(arr[56]), - lastFcmWakeupAt: dco_decode_opt_box_autoadd_i_64(arr[57]), - lastServerMessageAt: dco_decode_opt_box_autoadd_i_64(arr[58]), - currentSetupPage: dco_decode_opt_String(arr[59]), - skipSetupPages: dco_decode_bool(arr[60]), - hasZoomed: dco_decode_bool(arr[61]), + dco_decode_opt_box_autoadd_passwordless_recovery_config(arr[56]), + fcmToken: dco_decode_opt_String(arr[57]), + lastFcmWakeupAt: dco_decode_opt_box_autoadd_i_64(arr[58]), + lastServerMessageAt: dco_decode_opt_box_autoadd_i_64(arr[59]), + currentSetupPage: dco_decode_opt_String(arr[60]), + skipSetupPages: dco_decode_bool(arr[61]), + hasZoomed: dco_decode_bool(arr[62]), ); } @@ -9241,6 +9242,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { var var_additionalUserInvites = sse_decode_opt_String(deserializer); var var_themeMode = sse_decode_theme_mode(deserializer); var var_primaryColorValue = sse_decode_opt_box_autoadd_i_64(deserializer); + var var_language = sse_decode_opt_String(deserializer); var var_defaultShowTime = sse_decode_opt_box_autoadd_i_64(deserializer); var var_requestedAudioPermission = sse_decode_bool(deserializer); var var_enableDatabaseLogging = sse_decode_bool(deserializer); @@ -9315,6 +9317,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { additionalUserInvites: var_additionalUserInvites, themeMode: var_themeMode, primaryColorValue: var_primaryColorValue, + language: var_language, defaultShowTime: var_defaultShowTime, requestedAudioPermission: var_requestedAudioPermission, enableDatabaseLogging: var_enableDatabaseLogging, @@ -10604,6 +10607,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_opt_String(self.additionalUserInvites, serializer); sse_encode_theme_mode(self.themeMode, serializer); sse_encode_opt_box_autoadd_i_64(self.primaryColorValue, serializer); + sse_encode_opt_String(self.language, serializer); sse_encode_opt_box_autoadd_i_64(self.defaultShowTime, serializer); sse_encode_bool(self.requestedAudioPermission, serializer); sse_encode_bool(self.enableDatabaseLogging, serializer); diff --git a/lib/core/user_config.dart b/lib/core/user_config.dart index 7f1005cd..a8cdf241 100644 --- a/lib/core/user_config.dart +++ b/lib/core/user_config.dart @@ -121,6 +121,9 @@ class UserConfig { String? additionalUserInvites; ThemeMode themeMode; PlatformInt64? primaryColorValue; + + /// Language code the app is shown in. `None` follows the device. + String? language; PlatformInt64? defaultShowTime; bool requestedAudioPermission; bool enableDatabaseLogging; @@ -200,6 +203,7 @@ class UserConfig { this.additionalUserInvites, required this.themeMode, this.primaryColorValue, + this.language, this.defaultShowTime, required this.requestedAudioPermission, required this.enableDatabaseLogging, @@ -266,6 +270,7 @@ class UserConfig { additionalUserInvites.hashCode ^ themeMode.hashCode ^ primaryColorValue.hashCode ^ + language.hashCode ^ defaultShowTime.hashCode ^ requestedAudioPermission.hashCode ^ enableDatabaseLogging.hashCode ^ @@ -334,6 +339,7 @@ class UserConfig { additionalUserInvites == other.additionalUserInvites && themeMode == other.themeMode && primaryColorValue == other.primaryColorValue && + language == other.language && defaultShowTime == other.defaultShowTime && requestedAudioPermission == other.requestedAudioPermission && enableDatabaseLogging == other.enableDatabaseLogging && diff --git a/lib/src/localization/generated/app_localizations.dart b/lib/src/localization/generated/app_localizations.dart index fbafccaf..4801fa41 100644 --- a/lib/src/localization/generated/app_localizations.dart +++ b/lib/src/localization/generated/app_localizations.dart @@ -5,6 +5,7 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:intl/intl.dart' as intl; +import 'app_localizations_ar.dart'; import 'app_localizations_de.dart'; import 'app_localizations_en.dart'; @@ -94,6 +95,7 @@ abstract class AppLocalizations { /// A list of this localizations delegate's supported locales. static const List supportedLocales = [ + Locale('ar'), Locale('de'), Locale('en'), ]; @@ -716,12 +718,6 @@ abstract class AppLocalizations { /// **'FAQ'** String get settingsHelpFAQ; - /// No description provided for @feedbackTooltip. - /// - /// In en, this message translates to: - /// **'Give Feedback to improve twonly.'** - String get feedbackTooltip; - /// No description provided for @settingsHelpContactUs. /// /// In en, this message translates to: @@ -866,6 +862,12 @@ abstract class AppLocalizations { /// **'Theme'** String get settingsAppearanceTheme; + /// No description provided for @settingsAppearanceLanguage. + /// + /// In en, this message translates to: + /// **'Language'** + String get settingsAppearanceLanguage; + /// No description provided for @settingsAccountDeleteAccount. /// /// In en, this message translates to: @@ -1436,48 +1438,12 @@ abstract class AppLocalizations { /// **'Your additional users'** String get additionalUsersList; - /// No description provided for @galleryDelete. - /// - /// In en, this message translates to: - /// **'Delete file'** - String get galleryDelete; - - /// No description provided for @galleryExport. - /// - /// In en, this message translates to: - /// **'Export to gallery'** - String get galleryExport; - /// No description provided for @galleryExportSuccess. /// /// In en, this message translates to: /// **'Successfully saved in the Gallery.'** String get galleryExportSuccess; - /// No description provided for @gallerySelectAll. - /// - /// In en, this message translates to: - /// **'Select all'** - String get gallerySelectAll; - - /// No description provided for @galleryDeselectAll. - /// - /// In en, this message translates to: - /// **'Deselect all'** - String get galleryDeselectAll; - - /// No description provided for @galleryFavorite. - /// - /// In en, this message translates to: - /// **'Favorite'** - String get galleryFavorite; - - /// No description provided for @galleryUnfavorite. - /// - /// In en, this message translates to: - /// **'Unfavorite'** - String get galleryUnfavorite; - /// No description provided for @galleryCancel. /// /// In en, this message translates to: @@ -1526,12 +1492,6 @@ abstract class AppLocalizations { /// **'Are you sure?'** String get deleteImageTitle; - /// No description provided for @deleteImageBody. - /// - /// In en, this message translates to: - /// **'The image will be irrevocably deleted.'** - String get deleteImageBody; - /// No description provided for @deleteMemoriesBody. /// /// In en, this message translates to: @@ -1598,66 +1558,12 @@ abstract class AppLocalizations { /// **'Backup'** String get settingsBackup; - /// No description provided for @backupPending. - /// - /// In en, this message translates to: - /// **'Pending'** - String get backupPending; - - /// No description provided for @backupFailed. - /// - /// In en, this message translates to: - /// **'Failed'** - String get backupFailed; - - /// No description provided for @backupSuccess. - /// - /// In en, this message translates to: - /// **'Success'** - String get backupSuccess; - - /// No description provided for @backupTwonlySafeDesc. - /// - /// In en, this message translates to: - /// **'Back up your twonly identity, as this is the only way to restore your account if you uninstall the app or lose your phone.'** - String get backupTwonlySafeDesc; - /// No description provided for @backupNoPasswordRecovery. /// /// In en, this message translates to: /// **'Due to twonly\'s security system, there is (currently) no password recovery function. Therefore, you must remember your password or, better yet, write it down.'** String get backupNoPasswordRecovery; - /// No description provided for @backupIdentityHeader. - /// - /// In en, this message translates to: - /// **'Identity'** - String get backupIdentityHeader; - - /// No description provided for @backupArchiveHeader. - /// - /// In en, this message translates to: - /// **'Contacts & Messages'** - String get backupArchiveHeader; - - /// No description provided for @backupLastBackupDate. - /// - /// In en, this message translates to: - /// **'Last backup'** - String get backupLastBackupDate; - - /// No description provided for @backupLastBackupSize. - /// - /// In en, this message translates to: - /// **'Backup size'** - String get backupLastBackupSize; - - /// No description provided for @backupLastBackupResult. - /// - /// In en, this message translates to: - /// **'Result'** - String get backupLastBackupResult; - /// No description provided for @backupInsecurePassword. /// /// In en, this message translates to: @@ -2318,12 +2224,6 @@ abstract class AppLocalizations { /// **'Messages'** String get notificationCategoryMessageTitle; - /// No description provided for @notificationCategoryMessageDesc. - /// - /// In en, this message translates to: - /// **'Messages from other users.'** - String get notificationCategoryMessageDesc; - /// No description provided for @notificationConnectionFallback. /// /// In en, this message translates to: @@ -4382,12 +4282,6 @@ abstract class AppLocalizations { /// **'Local storage'** String get settingsStorageLocal; - /// No description provided for @settingsStorageNoCloudBackupTitle. - /// - /// In en, this message translates to: - /// **'No Cloud Backup'** - String get settingsStorageNoCloudBackupTitle; - /// No description provided for @settingsStorageNoCloudBackupCard. /// /// In en, this message translates to: @@ -4802,12 +4696,6 @@ abstract class AppLocalizations { /// **'Contacts & Messages'** String get backupCloudContactsMessages; - /// No description provided for @backupCloudFreeActive. - /// - /// In en, this message translates to: - /// **'Free Active'** - String get backupCloudFreeActive; - /// No description provided for @backupCloudImagesMedia. /// /// In en, this message translates to: @@ -4934,17 +4822,11 @@ abstract class AppLocalizations { /// **'Already added to this chat'** String get webxdcStoreAlreadyAdded; - /// No description provided for @webxdcChatAppsMenu. + /// No description provided for @webxdcChatApps. /// /// In en, this message translates to: /// **'Chat apps'** - String get webxdcChatAppsMenu; - - /// No description provided for @webxdcChatAppsTitle. - /// - /// In en, this message translates to: - /// **'Chat apps'** - String get webxdcChatAppsTitle; + String get webxdcChatApps; /// No description provided for @webxdcChatAppsEmpty. /// @@ -4988,7 +4870,7 @@ class _AppLocalizationsDelegate @override bool isSupported(Locale locale) => - ['de', 'en'].contains(locale.languageCode); + ['ar', 'de', 'en'].contains(locale.languageCode); @override bool shouldReload(_AppLocalizationsDelegate old) => false; @@ -4997,6 +4879,8 @@ class _AppLocalizationsDelegate AppLocalizations lookupAppLocalizations(Locale locale) { // Lookup logic when only language code is specified. switch (locale.languageCode) { + case 'ar': + return AppLocalizationsAr(); case 'de': return AppLocalizationsDe(); case 'en': diff --git a/lib/src/localization/generated/app_localizations_ar.dart b/lib/src/localization/generated/app_localizations_ar.dart new file mode 100644 index 00000000..e00eb670 --- /dev/null +++ b/lib/src/localization/generated/app_localizations_ar.dart @@ -0,0 +1,2854 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Arabic (`ar`). +class AppLocalizationsAr extends AppLocalizations { + AppLocalizationsAr([String locale = 'ar']) : super(locale); + + @override + String get subscriptionAllApps => 'الوصول إلى جميع التطبيقات'; + + @override + String get registerSlogan => 'ابقَ على تواصل بخصوصية.'; + + @override + String get onboardingWelcomeTitle => 'مرحبًا بك في twonly!'; + + @override + String get onboardingWelcomeBody => + 'اكتشف طريقة خاصة وآمنة للبقاء على تواصل مع أصدقائك من خلال مشاركة الصور الفورية.'; + + @override + String get onboardingE2eTitle => 'مشاركة بلا قلق'; + + @override + String get onboardingE2eBody => + 'بفضل التشفير التام بين الطرفين، اطمئن إلى أن اللحظات التي تشاركها لا يراها أحد سواك وسوى أصدقائك.'; + + @override + String get onboardingFocusTitle => 'ركّز على مشاركة اللحظات'; + + @override + String get onboardingFocusBody => + 'وداعًا للميزات التي تسبب الإدمان! صُمّم twonly لمشاركة اللحظات، بعيدًا عن المشتتات غير المفيدة والإعلانات.'; + + @override + String get onboardingNotProductTitle => 'أنت لست المنتَج!'; + + @override + String get onboardingNotProductBody => + 'يُموَّل twonly من خلال اشتراك اختياري. لن تُباع بياناتك أبدًا.'; + + @override + String get registerUsernameSlogan => 'أنشئ حسابك'; + + @override + String get registerUsernameDecoration => 'اسم المستخدم'; + + @override + String get registerUsernameLimits => '3 أحرف على الأقل.'; + + @override + String get registerProofOfWorkFailed => + 'حدثت مشكلة في اختبار التحقق. يُرجى المحاولة مرة أخرى.'; + + @override + String get registerSubmitButton => 'سجّل الآن!'; + + @override + String get chatsTapToSend => 'اضغط لإرسال صورتك الأولى'; + + @override + String get cameraPreviewSendTo => 'إرسال إلى'; + + @override + String get cameraPermissionsBody => + 'يحتاج twonly إلى الوصول إلى الكاميرا والميكروفون.'; + + @override + String get cameraPermissionsRequestButton => 'طلب الأذونات'; + + @override + String get shareImageTitle => 'مشاركة مع'; + + @override + String get shareImageBestFriends => 'أفضل الأصدقاء'; + + @override + String get shareImagePinnedContacts => 'المثبّتة'; + + @override + String get shareImagedEditorSendImage => 'إرسال'; + + @override + String get shareImagedEditorShareWith => 'مشاركة مع'; + + @override + String get shareImagedEditorSaveImage => 'حفظ'; + + @override + String get shareImagedEditorSavedImage => 'تم الحفظ'; + + @override + String get shareImageSearchAllContacts => 'البحث في جميع جهات الاتصال'; + + @override + String get startNewChatSearchHint => 'الاسم أو اسم المستخدم أو اسم المجموعة'; + + @override + String get shareImagedSelectAll => 'تحديد الكل'; + + @override + String get startNewChatTitle => 'اختر جهة اتصال'; + + @override + String get startNewChatNewContact => 'جهة اتصال جديدة'; + + @override + String get shareImageAllUsers => 'جميع جهات الاتصال'; + + @override + String get shareImageShowArchived => 'إظهار المستخدمين المؤرشفين'; + + @override + String get shareImageSendToWidget => 'إرسال إلى أداة الشاشة الرئيسية'; + + @override + String get shareImageWidgetExplainerTitle => 'إرسال إلى أداة'; + + @override + String get shareImageWidgetExplainerBody => + 'يُرسل الصورة مباشرةً إلى أدوات الشاشة الرئيسية لدى أصدقائك. لا يمكن اختيار سوى الأصدقاء الذين يسمحون بذلك.'; + + @override + String get shareImageWidgetExplainerDismiss => 'فهمت'; + + @override + String get searchUsernameInput => 'اسم المستخدم'; + + @override + String get addFriendTitle => 'إضافة أصدقاء'; + + @override + String get searchUserNamePending => 'الطلب قيد الانتظار'; + + @override + String get searchUsernameNotFound => 'اسم المستخدم غير موجود'; + + @override + String get searchUsernameNewFollowerTitle => 'الطلبات المفتوحة'; + + @override + String get chatListDetailInput => 'اكتب رسالة'; + + @override + String get userDeletedAccount => 'تم حذف حساب هذا المستخدم.'; + + @override + String get contextMenuUserProfile => 'الملف الشخصي'; + + @override + String get contextMenuArchiveUser => 'أرشفة'; + + @override + String get contextMenuUndoArchiveUser => 'إلغاء الأرشفة'; + + @override + String get contextMenuOpenChat => 'فتح المحادثة'; + + @override + String get contextMenuPin => 'تثبيت'; + + @override + String get contextMenuUnpin => 'إلغاء التثبيت'; + + @override + String get contextMenuViewAgain => 'عرض مرة أخرى'; + + @override + String get mediaViewerAuthReason => 'يُرجى المصادقة لعرض رسالة twonly هذه!'; + + @override + String get mediaViewerTwonlyTapToOpen => 'اضغط لفتح رسالة twonly!'; + + @override + String get messageSendState_Received => 'تم الاستلام'; + + @override + String get messageSendState_Opened => 'تم الفتح'; + + @override + String get messageSendState_Send => 'تم الإرسال'; + + @override + String get messageSendState_Delivered => 'تم الاستلام'; + + @override + String get messageSendState_Sending => 'جارٍ الإرسال'; + + @override + String get messageSendState_TapToLoad => 'اضغط للتحميل'; + + @override + String get messageSendState_Loading => 'جارٍ التنزيل'; + + @override + String get messageStoredInGallery => 'حُفظت في المعرض'; + + @override + String get messageReopened => 'أُعيد فتحها'; + + @override + String get imageEditorDrawOk => 'اعتماد الرسم'; + + @override + String get settingsTitle => 'الإعدادات'; + + @override + String get settingsChats => 'المحادثات'; + + @override + String get settingsPreSelectedReactions => 'رموز التفاعل المحددة مسبقًا'; + + @override + String get settingsAutomaticallyMarkEqualMediaFilesAsOpenedTitle => + 'تمييز المكررات كمفتوحة'; + + @override + String get settingsAutomaticallyMarkEqualMediaFilesAsOpenedSubtitle => + 'إذا استلمت الوسائط نفسها في عدة محادثات، فإن فتحها في إحداها يميّزها كمفتوحة في البقية.'; + + @override + String get settingsPreSelectedReactionsError => + 'يمكن اختيار 12 تفاعلًا كحد أقصى.'; + + @override + String get settingsProfile => 'الملف الشخصي'; + + @override + String get settingsStorageData => 'الذكريات والتخزين'; + + @override + String get memoryLocationTitle => 'حفظ الموقع في الذكريات'; + + @override + String get memoryLocationSubtitle => + 'الموقع للذكريات وللتصدير إلى المعرض. لا يُرسل أبدًا إلى جهات الاتصال.'; + + @override + String get memoryLocationPermissionDenied => + 'لم يُمنح إذن الوصول إلى الموقع الدقيق. سيبقى هذا الإعداد معطّلًا.'; + + @override + String get settingsStorageDataStoreInGTitle => 'الحفظ في المعرض'; + + @override + String get settingsStorageDataMediaAutoDownload => 'التنزيل التلقائي للوسائط'; + + @override + String get settingsStorageDataAutoDownMobile => 'عند استخدام بيانات الجوال'; + + @override + String get settingsStorageDataAutoDownWifi => 'عند استخدام Wi-Fi'; + + @override + String get settingsStorageManageTitle => 'إدارة التخزين'; + + @override + String get settingsStorageUsed => 'المساحة المستخدمة'; + + @override + String get settingsStorageImages => 'الصور'; + + @override + String get settingsStorageVideos => 'مقاطع الفيديو'; + + @override + String get settingsStorageGifs => 'صور GIF'; + + @override + String get settingsProfileCustomizeAvatar => 'خصّص صورتك الرمزية'; + + @override + String get settingsProfileEditDisplayName => 'الاسم المعروض'; + + @override + String get settingsProfileEditDisplayNameNew => 'اسم معروض جديد'; + + @override + String get settingsAccount => 'الحساب'; + + @override + String get settingsSubscription => 'الاشتراك'; + + @override + String get settingsAppearance => 'المظهر'; + + @override + String get settingsPrivacy => 'الخصوصية والأمان'; + + @override + String get settingsPrivacyBlockUsers => 'حظر جهات الاتصال'; + + @override + String get settingsPrivacyBlockUsersDesc => + 'لن تتمكن جهات الاتصال المحظورة من التواصل معك. يمكنك إلغاء حظر أي جهة اتصال في أي وقت.'; + + @override + String settingsPrivacyBlockUsersCount(Object len) { + return 'جهات الاتصال: $len'; + } + + @override + String get settingsNotification => 'الإشعارات'; + + @override + String get settingsNotifyPermission => 'أذونات الإشعارات'; + + @override + String get settingsNotifyPermissionDesc => + 'افتح إعدادات النظام للسماح بالإشعارات الفورية.'; + + @override + String get settingsNotifyResetTitle => 'إعادة تعيين رموز الإشعارات'; + + @override + String get settingsNotifyResetTitleSubtitle => + 'أعد تعيين رموز الإشعارات إذا واجهت مشكلات في استلام الإشعارات الفورية.'; + + @override + String get settingsNotifyResetTitleReset => + 'تمت إعادة تعيين رموز الإشعارات الخاصة بك.'; + + @override + String get settingsNotifyResetTitleResetDesc => + 'إذا استمرت المشكلة، يُرجى إرسال سجل تصحيح الأخطاء إلينا عبر الإعدادات > المساعدة حتى نتمكن من فحص المشكلة.'; + + @override + String get settingsHelp => 'المساعدة'; + + @override + String get settingsHelpDiagnostics => 'سجل التشخيص'; + + @override + String get settingsHelpFAQ => 'الأسئلة الشائعة'; + + @override + String get settingsHelpContactUs => 'اتصل بنا'; + + @override + String get settingsHelpVersion => 'الإصدار'; + + @override + String get settingsHelpLicenses => 'التراخيص (الشيفرة المصدرية)'; + + @override + String get settingsHelpCredits => 'التراخيص (الصور)'; + + @override + String get settingsHelpImprint => 'بيانات الناشر وسياسة الخصوصية'; + + @override + String get contactUsFaq => 'هل قرأت الأسئلة الشائعة؟'; + + @override + String get contactUsEmojis => 'كيف تشعر؟ (اختياري)'; + + @override + String get contactUsSelectOption => 'يُرجى اختيار أحد الخيارات'; + + @override + String get contactUsReason => 'أخبرنا عن سبب تواصلك معنا'; + + @override + String get contactUsMessage => + 'إذا كنت ترغب في تلقي رد، يُرجى إضافة عنوان بريدك الإلكتروني حتى نتمكن من التواصل معك.'; + + @override + String get contactUsYourMessage => 'رسالتك'; + + @override + String get contactUsMessageTitle => 'أخبرنا بما يحدث'; + + @override + String get contactUsReasonNotWorking => 'شيء ما لا يعمل'; + + @override + String get contactUsReasonFeatureRequest => 'طلب ميزة'; + + @override + String get contactUsReasonQuestion => 'سؤال'; + + @override + String get contactUsReasonFeedback => 'ملاحظات'; + + @override + String get contactUsReasonOther => 'أخرى'; + + @override + String get contactUsIncludeLog => 'إرفاق سجل تصحيح الأخطاء'; + + @override + String get contactUsWhatsThat => 'ما هذا؟'; + + @override + String get contactUsLastWarning => + 'هذه هي المعلومات التي سيتم إرسالها إلينا. يُرجى مراجعتها ثم الضغط على إرسال.'; + + @override + String get contactUsSuccess => 'تم إرسال ملاحظاتك بنجاح!'; + + @override + String get hideNewsIcon => 'إخفاء أيقونة الأخبار'; + + @override + String get settingsHelpTerms => 'شروط الخدمة'; + + @override + String get settingsAppearanceTheme => 'السمة'; + + @override + String get settingsAppearanceLanguage => 'اللغة'; + + @override + String get settingsAccountDeleteAccount => 'حذف الحساب'; + + @override + String get settingsAccountDeleteAccountNoBallance => + 'بمجرد حذف حسابك، لا يمكن التراجع عن ذلك.'; + + @override + String get settingsAccountDeleteModalTitle => 'هل أنت متأكد؟'; + + @override + String get settingsAccountDeleteModalBody => + 'سيتم حذف حسابك، ولن تكون هناك أي إمكانية لاستعادته.'; + + @override + String get contactVerifyNumberTitle => 'التحقق من جهات الاتصال'; + + @override + String verifyUserIdentity(Object username) { + return 'التحقق من هوية $username'; + } + + @override + String get contactVerifyNumberSubtitle => + 'تحقق من هوية جهات اتصالك للتأكد من أنك تراسل الشخص الصحيح.'; + + @override + String get inChatContactNotVerified => 'جهة الاتصال غير موثّقة.'; + + @override + String groupMembersNotVerified(Object count) { + return 'أعضاء غير موثّقين: $count.'; + } + + @override + String get userVerifiedTitle => 'تم التحقق من جهة الاتصال'; + + @override + String scanUserQrCode(Object username) { + return 'امسح رمز QR الخاص بـ $username'; + } + + @override + String get openOwnQrCode => 'افتح رمز QR الخاص بك'; + + @override + String contactVerifiedBy(Object username) { + return 'تم التحقق بواسطة $username'; + } + + @override + String get contactSharedByUnknown => + 'تمت مشاركتها من جهة اتصال موثّقة (اسم المستخدم غير متاح)'; + + @override + String get verificationTypeQrScanned => 'لقد مسحت رمز QR الخاص بجهة الاتصال.'; + + @override + String verificationTypeSecretQrToken(Object username) { + return 'تم مسح رمز QR الخاص بك من قِبل $username.'; + } + + @override + String get verificationTypeLink => 'تم التحقق عبر رابط.'; + + @override + String get verificationTypeManual => 'تمت مقارنة رقم الأمان يدويًا.'; + + @override + String get verificationTypeMigratedFromOldVersion => + 'تم النقل من إصدار قديم.'; + + @override + String get contactViewMessage => 'رسالة'; + + @override + String get contactNickname => 'الاسم المستعار'; + + @override + String get contactNicknameNew => 'اسم مستعار جديد'; + + @override + String get contactBlock => 'حظر'; + + @override + String get contactTroubleshoot => 'استكشاف مشكلات الاتصال وإصلاحها'; + + @override + String contactResetSessionTitle(String username) { + return 'إعادة تعيين الاتصال مع $username؟'; + } + + @override + String get contactResetSessionBody => + 'سيزيل twonly الاتصال المشفّر الحالي وينشئ اتصالًا جديدًا. قد تُفقد الرسائل التي لا تزال قيد الإرسال.'; + + @override + String get contactResetSessionConfirm => 'إعادة تعيين الاتصال'; + + @override + String contactResetSessionSuccess(String username) { + return 'تمت إعادة إنشاء الاتصال المشفّر مع $username.'; + } + + @override + String get contactResetSessionFailed => + 'تعذّرت إعادة تعيين الاتصال. تحقق من اتصالك بالإنترنت وحاول مرة أخرى.'; + + @override + String contactBlockTitle(Object username) { + return 'حظر $username'; + } + + @override + String get contactBlockBody => + 'لن تتمكن جهة الاتصال المحظورة من إرسال رسائل إليك بعد الآن، وسيتم إخفاء ملفها الشخصي. لإلغاء حظر جهة اتصال، انتقل إلى الإعدادات > الخصوصية > جهات الاتصال المحظورة.'; + + @override + String get contactRemove => 'إزالة جهة الاتصال'; + + @override + String contactRemoveTitle(Object username) { + return 'إزالة $username'; + } + + @override + String get contactRemoveBody => + 'إزالة جهة الاتصال نهائيًا. إذا حاولت جهة الاتصال إرسال رسالة جديدة إليك، فسيتعيّن عليك قبولها مرة أخرى أولًا.'; + + @override + String get undo => 'تراجع'; + + @override + String get redo => 'إعادة'; + + @override + String get next => 'التالي'; + + @override + String get finishSetup => 'إكمال الإعداد'; + + @override + String get submit => 'إرسال'; + + @override + String get close => 'إغلاق'; + + @override + String get disable => 'تعطيل'; + + @override + String get enable => 'تفعيل'; + + @override + String get cancel => 'إلغاء'; + + @override + String get now => 'الآن'; + + @override + String get you => 'أنت'; + + @override + String get minutesShort => 'د'; + + @override + String get image => 'صورة'; + + @override + String get video => 'فيديو'; + + @override + String get react => 'تفاعل'; + + @override + String get reply => 'رد'; + + @override + String get copy => 'نسخ'; + + @override + String get edit => 'تعديل'; + + @override + String get delete => 'حذف'; + + @override + String get info => 'معلومات'; + + @override + String get ok => 'حسنًا'; + + @override + String get switchFrontAndBackCamera => + 'التبديل بين الكاميرا الأمامية والخلفية.'; + + @override + String get addTextItem => 'نص'; + + @override + String get protectAsARealTwonly => 'إرسال كرسالة twonly حقيقية!'; + + @override + String get addDrawing => 'رسم'; + + @override + String get addEmoji => 'رمز تعبيري'; + + @override + String get toggleFlashLight => 'تشغيل الفلاش أو إيقافه'; + + @override + String userFound(Object username) { + return 'تم العثور على $username'; + } + + @override + String userFoundBody(String username) { + return 'هل تريد التواصل مع $username؟'; + } + + @override + String get errorInternalError => + 'الخادم غير متاح حاليًا. يُرجى المحاولة لاحقًا.'; + + @override + String get errorInvalidInvitationCode => + 'رمز الدعوة الذي أدخلته غير صالح. يُرجى التحقق من الرمز والمحاولة مرة أخرى.'; + + @override + String get errorUsernameAlreadyTaken => 'اسم المستخدم مستخدم بالفعل.'; + + @override + String get errorUsernameNotValid => + 'اسم المستخدم الذي أدخلته لا يستوفي المعايير المطلوبة. يُرجى اختيار اسم مستخدم صالح.'; + + @override + String get errorNotEnoughCredit => 'ليس لديك رصيد twonly كافٍ.'; + + @override + String get errorVoucherInvalid => 'رمز القسيمة الذي أدخلته غير صالح.'; + + @override + String get errorPlanLimitReached => 'لقد وصلت إلى حد خطتك. يُرجى ترقية خطتك.'; + + @override + String get errorPlanNotAllowed => 'هذه الميزة غير متاحة في خطتك الحالية.'; + + @override + String get errorPlanUpgradeNotYearly => + 'يجب دفع قيمة ترقية الخطة سنويًا، لأن الخطة الحالية تُدفع سنويًا أيضًا.'; + + @override + String upgradeToPaidPlanButton(Object planId, Object sufix) { + return 'الترقية إلى $planId$sufix'; + } + + @override + String get partOfPaidPlanFrom => 'ضمن الخطة المدفوعة لـ'; + + @override + String get subscriptionCurrentPlanBadge => 'الحالية'; + + @override + String get year => 'سنة'; + + @override + String get yearly => 'سنويًا'; + + @override + String get month => 'شهر'; + + @override + String get monthly => 'شهريًا'; + + @override + String get proFeature1 => 'رفع غير محدود لملفات الوسائط'; + + @override + String get proFeature2 => 'مستخدم Plus إضافي واحد'; + + @override + String get proFeature3 => 'مساحة 25 GB لتخزين الذكريات'; + + @override + String get proFeature4 => 'استعادة الشعلات'; + + @override + String get familyFeature1 => 'رفع غير محدود لملفات الوسائط'; + + @override + String get familyFeature2 => '4 مستخدمين Plus إضافيين'; + + @override + String get familyFeature3 => 'مساحة 50 GB لتخزين الذكريات'; + + @override + String get familyFeature4 => 'استعادة الشعلات'; + + @override + String get freeFeature1 => 'رفع 10 ملفات وسائط يوميًا'; + + @override + String get plusFeature1 => 'رفع غير محدود لملفات الوسائط'; + + @override + String get plusFeature2 => 'ميزات إضافية (قريبًا)'; + + @override + String get manageAdditionalUsers => 'إدارة المستخدمين الإضافيين'; + + @override + String get open => 'فتح'; + + @override + String get buy => 'شراء'; + + @override + String get autoRenewal => 'التجديد التلقائي'; + + @override + String get additionalUsersList => 'مستخدموك الإضافيون'; + + @override + String get galleryExportSuccess => 'تم الحفظ في المعرض بنجاح.'; + + @override + String get galleryCancel => 'إلغاء'; + + @override + String get memoriesEmpty => + 'بمجرد حفظ الصور أو مقاطع الفيديو، ستظهر هنا في ذكرياتك.'; + + @override + String get deleteTitle => 'هل أنت متأكد؟'; + + @override + String get deleteOkBtnForAll => 'حذف لدى الجميع'; + + @override + String memoriesDeleteSnackbarSuccess(num count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: 'تم حذف $count عنصر بنجاح', + many: 'تم حذف $count عنصرًا بنجاح', + few: 'تم حذف $count عناصر بنجاح', + two: 'تم حذف عنصرين بنجاح', + one: 'تم حذف عنصر واحد بنجاح', + ); + return '$_temp0'; + } + + @override + String get deleteOkBtnForMe => 'حذف لديّ'; + + @override + String get deleteOnlyForMe => 'حذف لديّ فقط'; + + @override + String get deleteImageTitle => 'هل أنت متأكد؟'; + + @override + String deleteMemoriesBody(num count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: 'سيتم حذف $count صورة نهائيًا.', + many: 'سيتم حذف $count صورة نهائيًا.', + few: 'سيتم حذف $count صور نهائيًا.', + two: 'سيتم حذف الصورتين نهائيًا.', + one: 'سيتم حذف الصورة نهائيًا.', + ); + return '$_temp0'; + } + + @override + String get deleteMemoriesLocalOnly => 'محليًا فقط'; + + @override + String get deleteMemoriesCompletely => 'بالكامل'; + + @override + String memoriesSelectedCount(num count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: '$count عنصر', + many: '$count عنصرًا', + few: '$count عناصر', + two: 'عنصران', + one: 'عنصر واحد', + ); + return '$_temp0'; + } + + @override + String get memoriesMenuSelectAll => 'تحديد الكل'; + + @override + String get memoriesMenuDeselectAll => 'إلغاء تحديد الكل'; + + @override + String get memoriesMenuExport => 'تصدير إلى المعرض'; + + @override + String get memoriesMenuFavorite => 'إضافة إلى المفضلة'; + + @override + String get memoriesMenuDelete => 'حذف بالكامل'; + + @override + String get memoriesMenuDeleteLocal => 'حذف محليًا'; + + @override + String get settingsBackup => 'النسخ الاحتياطي'; + + @override + String get backupNoPasswordRecovery => + 'بسبب نظام الأمان في twonly، لا توجد (حاليًا) وظيفة لاستعادة كلمة المرور. لذلك يجب عليك تذكّر كلمة مرورك، أو الأفضل من ذلك أن تدوّنها.'; + + @override + String get backupInsecurePassword => 'كلمة مرور غير آمنة'; + + @override + String get backupInsecurePasswordDesc => + 'كلمة المرور المختارة غير آمنة إطلاقًا، ويمكن للمهاجمين تخمينها بسهولة. يُرجى اختيار كلمة مرور آمنة.'; + + @override + String get backupInsecurePasswordOk => 'المتابعة على أي حال'; + + @override + String get backupInsecurePasswordCancel => 'حاول مرة أخرى'; + + @override + String get backupTwonlySafeLongDesc => + 'لا يستخدم twonly أي حسابات مستخدمين مركزية. عند التثبيت يتم إنشاء زوج مفاتيح يتكوّن من مفتاح عام ومفتاح خاص. يُخزَّن المفتاح الخاص على جهازك فقط لحمايته من الوصول غير المصرّح به. أما المفتاح العام فيُرفع إلى الخادم ويُربط باسم المستخدم الذي اخترته حتى يتمكن الآخرون من العثور عليك.\n\nيُنشئ النسخ الاحتياطي في twonly بانتظام نسخة احتياطية مشفّرة ومجهولة الهوية من مفتاحك الخاص مع جهات اتصالك وإعداداتك. يكفي اسم المستخدم وكلمة المرور التي اخترتها لاستعادة هذه البيانات على جهاز آخر.'; + + @override + String get backupSelectStrongPassword => + 'اختر كلمة مرور آمنة. يُنصح بإنشائها وحفظها باستخدام مدير كلمات المرور الذي تفضّله.'; + + @override + String get password => 'كلمة المرور'; + + @override + String get passwordRepeated => 'أعد إدخال كلمة المرور'; + + @override + String get passwordRepeatedNotEqual => 'كلمتا المرور غير متطابقتين.'; + + @override + String get backupPasswordRequirement => + 'يجب أن تتكوّن كلمة المرور من 10 أحرف على الأقل.'; + + @override + String get backupEnableBackup => 'تفعيل النسخ الاحتياطي بكلمة مرور'; + + @override + String get backupTwonlySaveNow => 'احفظ الآن'; + + @override + String get backupChangePassword => 'تغيير كلمة المرور'; + + @override + String get backupChangePasswordAuthReason => + 'تغيير كلمة مرور النسخ الاحتياطي'; + + @override + String get backupChangePasswordAuthFailed => + 'لا يمكنك تغيير كلمة المرور إلا بعد المصادقة!'; + + @override + String get backupFreeSpaceWithCloud => + 'وفّر المساحة بالنسخ الاحتياطي السحابي'; + + @override + String get backupMemoriesNotEnabled => 'غير مفعّل'; + + @override + String get backupMemoriesUpgradeRequired => 'يتطلب ترقية'; + + @override + String todayAt(Object time) { + return 'اليوم الساعة $time'; + } + + @override + String yesterdayAt(Object time) { + return 'أمس الساعة $time'; + } + + @override + String get twonlySafeRecoverTitle => 'استعادة النسخة الاحتياطية'; + + @override + String get twonlySafeRecoverBtn => 'استعادة النسخة الاحتياطية'; + + @override + String get inviteFriends => 'ادعُ أصدقاءك'; + + @override + String get inviteFriendsShareBtn => 'مشاركة'; + + @override + String inviteFriendsShareText(Object url) { + return 'لننتقل إلى twonly: $url'; + } + + @override + String get appOutdated => 'إصدار twonly لديك قديم.'; + + @override + String get appOutdatedBtn => 'حدّث الآن'; + + @override + String get doubleClickToReopen => 'انقر مرتين\nلفتحها مجددًا'; + + @override + String get uploadLimitReached => + 'تم الوصول إلى حد الرفع.\nقم بالترقية إلى Pro\nأو انتظر حتى الغد.'; + + @override + String get fileLimitReached => 'تم تجاوز الحد الأقصى\nلحجم الملف'; + + @override + String get retransmissionRequested => 'تم طلب إعادة الإرسال'; + + @override + String reportUserTitle(Object username) { + return 'الإبلاغ عن $username'; + } + + @override + String get reportUserReason => 'سبب الإبلاغ'; + + @override + String get reportUser => 'الإبلاغ عن جهة الاتصال'; + + @override + String get newDeviceRegistered => + 'لقد سجّلت الدخول على جهاز آخر، لذلك تم تسجيل خروجك هنا.'; + + @override + String get tabToRemoveEmoji => 'اضغط للإزالة'; + + @override + String get quotedMessageWasDeleted => 'تم حذف الرسالة المقتبسة.'; + + @override + String get messageWasDeleted => 'تم حذف الرسالة.'; + + @override + String get messageWasDeletedShort => 'محذوفة'; + + @override + String get sent => 'تم التسليم'; + + @override + String get sentTo => 'تم التسليم إلى'; + + @override + String get received => 'تم الاستلام'; + + @override + String get opened => 'تم الفتح'; + + @override + String get waitingForInternet => 'في انتظار الاتصال بالإنترنت'; + + @override + String get editHistory => 'سجل التعديلات'; + + @override + String get fileSize => 'حجم الملف'; + + @override + String get archivedChats => 'المحادثات المؤرشفة'; + + @override + String get durationShortSecond => 'ث'; + + @override + String get durationShortMinute => 'د'; + + @override + String get durationShortHour => 'س'; + + @override + String durationShortDays(num count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: '$count يوم', + many: '$count يومًا', + few: '$count أيام', + two: 'يومان', + one: 'يوم واحد', + ); + return '$_temp0'; + } + + @override + String get contacts => 'جهات الاتصال'; + + @override + String get groups => 'المجموعات'; + + @override + String get newGroup => 'مجموعة جديدة'; + + @override + String get selectMembers => 'اختر الأعضاء'; + + @override + String get selectGroupName => 'اختر اسم المجموعة'; + + @override + String get groupNameInput => 'اسم المجموعة'; + + @override + String get groupMembers => 'الأعضاء'; + + @override + String get addMember => 'إضافة عضو'; + + @override + String get createGroup => 'إنشاء مجموعة'; + + @override + String get leaveGroup => 'مغادرة المجموعة'; + + @override + String get createContactRequest => 'إنشاء طلب تواصل'; + + @override + String get contactRequestSend => 'تم إرسال طلب التواصل'; + + @override + String get makeAdmin => 'تعيين كمشرف'; + + @override + String get removeAdmin => 'إزالة من الإشراف'; + + @override + String get removeFromGroup => 'إزالة من المجموعة'; + + @override + String get admin => 'مشرف'; + + @override + String revokeAdminRightsTitle(Object username) { + return 'سحب صلاحيات الإشراف من $username؟'; + } + + @override + String get revokeAdminRightsOkBtn => 'إزالة من الإشراف'; + + @override + String makeAdminRightsTitle(Object username) { + return 'تعيين $username مشرفًا؟'; + } + + @override + String makeAdminRightsBody(Object username) { + return 'سيتمكن $username من تعديل هذه المجموعة وأعضائها.'; + } + + @override + String get makeAdminRightsOkBtn => 'تعيين كمشرف'; + + @override + String get updateGroup => 'تحديث المجموعة'; + + @override + String get alreadyInGroup => 'موجود في المجموعة بالفعل'; + + @override + String get contactNotVerified => 'غير موثّق'; + + @override + String removeContactFromGroupTitle(Object username) { + return 'إزالة $username من هذه المجموعة؟'; + } + + @override + String youChangedGroupName(Object newGroupName) { + return 'لقد غيّرت اسم المجموعة إلى «$newGroupName».'; + } + + @override + String makerChangedGroupName(Object maker, Object newGroupName) { + return 'غيّر $maker اسم المجموعة إلى «$newGroupName».'; + } + + @override + String get youCreatedGroup => 'لقد أنشأت المجموعة.'; + + @override + String makerCreatedGroup(Object maker) { + return 'أنشأ $maker المجموعة.'; + } + + @override + String youRemovedMember(Object affected) { + return 'لقد أزلت $affected من المجموعة.'; + } + + @override + String makerRemovedMember(Object affected, Object maker) { + return 'أزال $maker $affected من المجموعة.'; + } + + @override + String youAddedMember(Object affected) { + return 'لقد أضفت $affected إلى المجموعة.'; + } + + @override + String makerAddedMember(Object affected, Object maker) { + return 'أضاف $maker $affected إلى المجموعة.'; + } + + @override + String youMadeAdmin(Object affected) { + return 'لقد منحت $affected صلاحيات الإشراف.'; + } + + @override + String makerMadeAdmin(Object affected, Object maker) { + return 'منح $maker $affected صلاحيات الإشراف.'; + } + + @override + String youRevokedAdminRights(Object affectedR) { + return 'لقد سحبت صلاحيات الإشراف من $affectedR.'; + } + + @override + String makerRevokedAdminRights(Object affectedR, Object maker) { + return 'سحب $maker صلاحيات الإشراف من $affectedR.'; + } + + @override + String get youLeftGroup => 'لقد غادرت المجموعة.'; + + @override + String makerLeftGroup(Object maker) { + return 'غادر $maker المجموعة.'; + } + + @override + String get groupActionYou => 'حسابك'; + + @override + String get groupActionYour => 'حسابك'; + + @override + String get notificationFillerIn => 'في'; + + @override + String notificationText(Object inGroup) { + return 'رسالة جديدة$inGroup.'; + } + + @override + String notificationTwonly(Object inGroup) { + return 'رسالة twonly جديدة$inGroup.'; + } + + @override + String notificationVideo(Object inGroup) { + return 'فيديو جديد$inGroup.'; + } + + @override + String notificationImage(Object inGroup) { + return 'صورة جديدة$inGroup.'; + } + + @override + String notificationAudio(Object inGroup) { + return 'رسالة صوتية جديدة$inGroup.'; + } + + @override + String notificationAddedToGroup(Object groupname) { + return 'تمت إضافتك إلى «$groupname»'; + } + + @override + String get notificationContactRequest => 'طلب جديد للتواصل معك.'; + + @override + String get notificationAcceptRequest => 'أنتما متصلان الآن.'; + + @override + String get notificationStoredMediaFile => 'تم حفظ صورتك.'; + + @override + String get notificationReaction => 'تفاعل جديد مع صورتك.'; + + @override + String get notificationReopenedMedia => 'تمت إعادة فتح صورتك.'; + + @override + String notificationReactionToVideo(Object reaction) { + return 'تفاعل $reaction مع الفيديو الخاص بك.'; + } + + @override + String notificationReactionToText(Object reaction) { + return 'تفاعل $reaction مع رسالتك.'; + } + + @override + String notificationReactionToImage(Object reaction) { + return 'تفاعل $reaction مع صورتك.'; + } + + @override + String notificationReactionToAudio(Object reaction) { + return 'تفاعل $reaction مع رسالتك الصوتية.'; + } + + @override + String notificationResponse(Object inGroup) { + return 'رد جديد$inGroup.'; + } + + @override + String get notificationCategoryMessageTitle => 'الرسائل'; + + @override + String get notificationConnectionFallback => 'قد تكون لديك رسائل جديدة.'; + + @override + String get groupContextMenuDeleteGroup => + 'سيؤدي هذا إلى حذف جميع الرسائل في هذه المحادثة نهائيًا.'; + + @override + String get groupYouAreNowLongerAMember => 'لم تعد عضوًا في هذه المجموعة.'; + + @override + String get groupNetworkIssue => 'مشكلة في الشبكة. حاول مرة أخرى لاحقًا.'; + + @override + String get leaveGroupSelectOtherAdminTitle => 'اختر مشرفًا آخر'; + + @override + String get leaveGroupSelectOtherAdminBody => + 'لمغادرة المجموعة، يجب عليك أولًا اختيار مشرف جديد.'; + + @override + String get leaveGroupSureTitle => 'مغادرة المجموعة'; + + @override + String get leaveGroupSureBody => 'هل تريد حقًا مغادرة المجموعة؟'; + + @override + String get leaveGroupSureOkBtn => 'مغادرة المجموعة'; + + @override + String changeDisplayMaxTime(Object time, Object username) { + return 'سيتم الآن حذف المحادثات بعد $time ($username).'; + } + + @override + String youChangedDisplayMaxTime(Object time) { + return 'سيتم الآن حذف المحادثات بعد $time.'; + } + + @override + String get userGotReported => 'تم الإبلاغ عن المستخدم.'; + + @override + String get deleteChatAfter => 'حذف المحادثة بعد...'; + + @override + String get deleteChatAfterAnHour => 'ساعة واحدة.'; + + @override + String get deleteChatAfterADay => 'يوم واحد.'; + + @override + String get deleteChatAfterAWeek => 'أسبوع واحد.'; + + @override + String get deleteChatAfterAMonth => 'شهر واحد.'; + + @override + String get deleteChatAfterAYear => 'سنة واحدة.'; + + @override + String get yourTwonlyScore => 'نقاط twonly الخاصة بك'; + + @override + String get registrationClosed => + 'نظرًا للعدد الكبير من التسجيلات حاليًا، أوقفنا التسجيل مؤقتًا لضمان استمرار موثوقية الخدمة. يُرجى المحاولة مرة أخرى بعد بضعة أيام.'; + + @override + String get dialogAskDeleteMediaFilePopTitle => + 'هل أنت متأكد أنك تريد حذف تحفتك الفنية؟'; + + @override + String get dialogAskDeleteMediaFilePopDelete => 'حذف'; + + @override + String get allowErrorTracking => 'شارك الأخطاء والأعطال معنا'; + + @override + String get allowErrorTrackingSubtitle => + 'إذا تعطّل twonly أو حدثت أخطاء، يتم الإبلاغ عنها تلقائيًا إلى خادم Glitchtip الذي نستضيفه بأنفسنا. لا يتم رفع البيانات الشخصية مثل الرسائل أو الصور أبدًا.'; + + @override + String get avatarSaveChanges => 'هل تريد حفظ التغييرات؟'; + + @override + String get avatarSaveChangesStore => 'حفظ'; + + @override + String get avatarSaveChangesDiscard => 'تجاهل'; + + @override + String get inProcess => 'قيد المعالجة'; + + @override + String get draftMessage => 'مسودة'; + + @override + String get voiceMessageSlideToCancel => 'اسحب للإلغاء'; + + @override + String get voiceMessageCancel => 'إلغاء'; + + @override + String get shareYourProfile => 'شارك ملفك الشخصي'; + + @override + String get scanOtherProfile => 'امسح ملفًا شخصيًا آخر'; + + @override + String get openYourOwnQRcode => 'افتح رمز QR الخاص بك'; + + @override + String get addContactQrSheetSubtext => 'اطلب من صديقك مسح رمز QR هذا لإضافتك'; + + @override + String letUserScanQrCode(Object username) { + return 'اطلب من $username مسح رمز QR هذا'; + } + + @override + String get finishSetupCardTitle => 'أكمل ملفك الشخصي'; + + @override + String get finishSetupCardDesc => + 'أوشكت على الانتهاء! أكمل إعداد حسابك للاستفادة القصوى من twonly.'; + + @override + String get finishSetupCardAction => 'متابعة الإعداد'; + + @override + String get missingBackupCardTitle => 'إعداد النسخ الاحتياطي'; + + @override + String get missingBackupCardDesc => + 'لقد حسّنّا آلية النسخ الاحتياطي، ولهذا يلزمك إعدادها من جديد.'; + + @override + String get missingBackupCardAction => 'أعدّه الآن'; + + @override + String get onboardingFinishLater => 'الإكمال لاحقًا'; + + @override + String get onboardingProfileTitle => 'اختر مظهرك'; + + @override + String get onboardingProfileBody => + 'اختر صورة رمزية واسمًا معروضًا سيراه أصدقاؤك.'; + + @override + String get onboardingBackupBody => + 'احتفظ بنسخة احتياطية من هويتك في twonly، فهذه هي الطريقة الوحيدة لاستعادة حسابك إذا ألغيت تثبيت التطبيق أو فقدت هاتفك.'; + + @override + String get onboardingUserDiscoveryShareFriends => 'شارك أصدقاءك'; + + @override + String get onboardingUserDiscoveryShareFriendsDesc => + 'شارك مع أصدقائك من تعرفهم ومن تحققت منهم. لا يمكن للأصدقاء *رؤية سوى الأصدقاء المشتركين* من قائمة أصدقائك. يمكنك تغيير رأيك *في أي وقت* أو *إخفاء أشخاص محددين*.'; + + @override + String get onboardingUserDiscoveryContactsVerifiedBadge => + 'تحصل جهات الاتصال التي تحقق منها أصدقاؤك على شارة'; + + @override + String get onboardingUserDiscoveryWhoIsRequesting => 'اعرف من يرسل الطلب'; + + @override + String get userDiscoverySettingsManualApproval => + 'اسألني في كل مرة قبل المشاركة'; + + @override + String get userDiscoverySettingsManualApprovalDesc => + 'سيُطلب منك التأكيد في كل مرة قبل مشاركة أحد أصدقائك.'; + + @override + String get onboardingUserDiscoveryLetFriendsFindYou => + 'دع أصدقاءك يعثرون عليك'; + + @override + String get onboardingUserDiscoveryBeRecommended => 'اظهر في اقتراحات الآخرين'; + + @override + String get onboardingUserDiscoveryWhatOthersSee => 'ما سيراه الآخرون'; + + @override + String get onboardingUserDiscoveryWhatYouSee => 'هذا ما ستراه عند الطلب'; + + @override + String get onboardingAddContactsTitle => 'أضف جهات اتصال جديدة'; + + @override + String get onboardingAddContactsAcceptDesc => + 'في twonly، يجب قبول كل جهة اتصال أولًا قبل أن تتمكن من التواصل.'; + + @override + String get onboardingAddContactsMethodHeading => 'إضافة جهات اتصال'; + + @override + String get onboardingAddContactsMethodScan => + 'امسح رمز QR الخاص بجهة الاتصال.'; + + @override + String get onboardingAddContactsMethodSearch => 'ابحث عن اسم المستخدم.'; + + @override + String get onboardingAddContactsMethodShare => 'شارك جهة اتصال في المحادثات.'; + + @override + String linkFromUsername(Object username) { + return 'هل الرابط من $username؟'; + } + + @override + String get linkFromUsernameLong => + 'إذا تلقيت الرابط من صديقك، يمكنك تمييز المستخدم كموثّق، لأن المفتاح العام في الرابط يطابق المفتاح العام المحفوظ مسبقًا لهذا المستخدم.'; + + @override + String get gotLinkFromFriend => 'نعم، حصلت على الرابط من صديقي!'; + + @override + String couldNotVerifyUsername(Object username) { + return 'تعذّر التحقق من $username'; + } + + @override + String get linkPubkeyDoesNotMatch => + 'المفتاح العام في الرابط لا يطابق المفتاح العام المحفوظ لجهة الاتصال هذه. حاول مقابلة صديقك شخصيًا ومسح رمز QR مباشرةً!'; + + @override + String get verificationMissingSession => + 'لا توجد جلسة آمنة محفوظة لجهة الاتصال هذه بعد. تبادلا رسالة أولًا، ثم اطلب من الشخص مشاركة رابط تحقق جديد.'; + + @override + String get verificationLinkInvalidTitle => 'رابط تحقق غير صالح'; + + @override + String get verificationLinkInvalid => + 'رابط التحقق غير مكتمل أو غير صالح. اطلب من الشخص مشاركته مرة أخرى من ملفه الشخصي في twonly.'; + + @override + String get verificationLinkErrorTitle => 'التحقق غير متاح'; + + @override + String get verificationLinkError => + 'تعذّر على twonly فحص رابط التحقق هذا. يُرجى المحاولة مرة أخرى.'; + + @override + String get verificationOwnProfileTitle => 'هذا ملفك الشخصي'; + + @override + String get verificationOwnProfile => 'لقد مسحت رمز التحقق الخاص بك.'; + + @override + String get startWithCameraOpen => 'البدء والكاميرا مفتوحة'; + + @override + String get showImagePreviewWhenSending => + 'عرض معاينة الصورة عند اختيار المستلمين'; + + @override + String verifiedPublicKey(Object username) { + return 'تم التحقق من هوية $username بنجاح.'; + } + + @override + String get memoriesAYearAgo => 'قبل عام'; + + @override + String memoriesXYearsAgo(Object years) { + return 'قبل $years سنوات'; + } + + @override + String migrationOfMemories(Object open) { + return 'نقل ملفات الوسائط: $open بانتظار المعالجة.'; + } + + @override + String get autoStoreAllSendUnlimitedMediaFiles => 'حفظ جميع الوسائط المرسلة'; + + @override + String get autoStoreAllSendUnlimitedMediaFilesSubtitle => + 'إذا فعّلت هذا الخيار، فسيتم حفظ جميع الصور التي ترسلها، طالما أُرسلت بعدّ تنازلي غير محدود وليس في وضع twonly.'; + + @override + String get termsOfService => 'شروط الخدمة'; + + @override + String get privacyPolicy => 'سياسة الخصوصية'; + + @override + String additionalUserAddError(Object username) { + return 'تعذّرت إضافة $username، يُرجى المحاولة لاحقًا.'; + } + + @override + String additionalUserAddErrorNotInFreePlan(Object username) { + return 'تعذّرت إضافة $username لأن الحساب مشترك بالفعل في خطة مدفوعة.'; + } + + @override + String additionalUserAddButton(Object limit, Object used) { + return 'إضافة مستخدم إضافي ($used/$limit)'; + } + + @override + String get additionalUserRemoveTitle => 'إزالة هذا المستخدم الإضافي'; + + @override + String get additionalUserRemoveDesc => + 'بعد الإزالة، سيُنقل المستخدم الإضافي تلقائيًا إلى الخطة المجانية، ويمكنك إضافة شخص آخر.'; + + @override + String get additionalUserSelectTitle => 'اختر مستخدمين إضافيين'; + + @override + String additionalUserSelectButton(Object limit, Object used) { + return 'اختر المستخدمين ($used/$limit)'; + } + + @override + String get storeAsDefault => 'حفظ كافتراضي'; + + @override + String get deleteUserErrorMessage => + 'لا يمكنك حذف جهة الاتصال إلا بعد حذف المحادثة المباشرة، وعندما لا تعود جهة الاتصال عضوًا في أي مجموعة.'; + + @override + String groupSizeLimitError(Object size) { + return 'الحد الأقصى لعدد أعضاء المجموعة حاليًا: $size!'; + } + + @override + String get authRequestReopenImage => 'يجب عليك المصادقة لإعادة فتح الصورة.'; + + @override + String get shareContactsMenu => 'جهة اتصال'; + + @override + String get shareContactsTitle => 'اختر جهات الاتصال'; + + @override + String get shareContactsSubmit => 'شارك الآن'; + + @override + String get updateTwonlyMessage => + 'لرؤية هذه الرسالة، تحتاج إلى تحديث twonly.'; + + @override + String get verificationBadgeNote => + 'يمكنك التحقق من أصدقائك عن طريق مسح رمز QR العام الخاص بهم. اضغط لمعرفة المزيد.'; + + @override + String get verificationBadgeTitle => 'التحقق'; + + @override + String get verificationBadgeGeneralDesc => + 'تمنحك الشارة راحة البال بأنك تراسل *الشخص الصحيح* وأن *رسائلك تبقى سرية*.'; + + @override + String get verificationBadgeGreenDesc => + 'جهة اتصال *تحققت منها بنفسك* باستخدام رمز QR.'; + + @override + String get verificationBadgeYellowDesc => + 'جهة اتصال تحقق منها واحد على الأقل من *جهات اتصالك*.'; + + @override + String get verificationBadgeRedDesc => + 'جهة اتصال *لم* يتم التحقق من هويتها بعد.'; + + @override + String get manualVerificationOpen => 'أرقام الأمان'; + + @override + String get manualVerificationTitle => 'مقارنة رقم الأمان'; + + @override + String get manualVerificationMarkVerified => 'تمييز كموثّق'; + + @override + String manualVerificationDescription(String username) { + return 'قارن رقم الأمان هذا مع $username. إذا عرض الجهازان الرقم نفسه، فهذا يعني أنه تم التحقق من تشفيرك التام بين الطرفين.'; + } + + @override + String get manualVerificationMissingKeyTitle => 'لا يتوفر رقم أمان'; + + @override + String manualVerificationMissingKeyDescription(String username) { + return 'لا توجد جلسة Signal مع $username بعد. تبادلا رسالة أولًا، ثم حاول مرة أخرى.'; + } + + @override + String get manualVerificationLoadFailed => 'تعذّر تحميل رقم الأمان.'; + + @override + String get manualVerificationRetry => 'حاول مرة أخرى'; + + @override + String manualVerificationSuccess(String username) { + return 'لقد قارنت رقم الأمان مع $username، وتم تأكيد الهوية.'; + } + + @override + String get scanNow => 'امسح الآن'; + + @override + String get qrScannerVerifyHint => + 'للتحقق، يجب على الشخص الآخر فتح رمز QR الخاص به (قائمة المحادثات > زر رمز QR في الأسفل)'; + + @override + String qrScannerVerifyUserHint(Object username) { + return 'للتحقق، يجب على $username فتح رمز QR (قائمة المحادثات > زر رمز QR في الأسفل)'; + } + + @override + String get openQrCode => 'فتح رمز QR'; + + @override + String get letFriendScanQrToVerify => + 'اطلب من صديقك مسح رمز QR هذا للتحقق منك'; + + @override + String get deleteVerificationTitle => 'حذف التحقق؟'; + + @override + String get deleteVerificationBody => 'هل أنت متأكد أنك تريد حذف هذا التحقق؟'; + + @override + String secretQrTokenVerifiedSnackbar(Object username) { + return 'تم مسح رمز QR الخاص بك من قِبل $username. أصبحت جهة الاتصال موثّقة الآن.'; + } + + @override + String askForFriendPromotionsPrompt(Object username) { + return 'ساعد $username في العثور على وجوه مألوفة على twonly من خلال مشاركة الأصدقاء المشتركين.'; + } + + @override + String mutualGroupsTitle(num count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: '$count مجموعة مشتركة', + many: '$count مجموعة مشتركة', + few: '$count مجموعات مشتركة', + two: 'مجموعتان مشتركتان', + one: 'مجموعة مشتركة واحدة', + ); + return '$_temp0'; + } + + @override + String chatEntryFlameRestored(Object count) { + return 'تمت استعادة الشعلات: $count'; + } + + @override + String requestedUserToastText(Object username) { + return 'تم إرسال طلب إلى $username بنجاح.'; + } + + @override + String get profileYourQrCode => 'رمز QR الخاص بك'; + + @override + String get settingsScreenLock => 'قفل الشاشة'; + + @override + String get settingsScreenLockSubtitle => + 'لفتح twonly، ستحتاج إلى استخدام ميزة فتح القفل في هاتفك.'; + + @override + String get settingsScreenLockAuthMessageEnable => + 'استخدام قفل الشاشة في twonly.'; + + @override + String get settingsScreenLockAuthMessageDisable => + 'تعطيل قفل الشاشة في twonly.'; + + @override + String get unlockTwonly => 'فتح قفل twonly'; + + @override + String get unlockTwonlyTryAgain => 'حاول مرة أخرى'; + + @override + String get unlockTwonlyDesc => + 'استخدم إعدادات فتح القفل في هاتفك لفتح twonly'; + + @override + String get settingsTypingIndication => 'مؤشرات الكتابة'; + + @override + String get settingsTypingIndicationSubtitle => + 'عند إيقاف مؤشر الكتابة، لن تتمكن من رؤية متى يكتب الآخرون رسالة.'; + + @override + String get contactActionBlock => 'حظر'; + + @override + String get contactActionAccept => 'قبول'; + + @override + String get userDiscoverySettingsMutualFriends => + 'اختر عدد الأصدقاء المشتركين الذين يجب أن يكونوا لدى الشخص حتى يتم اقتراحك عليه.'; + + @override + String get userDiscoverySettingsApply => 'تطبيق التغييرات'; + + @override + String get userDiscoveryEnabledDisableWarning => + 'إذا عطّلت ميزة «الأصدقاء المشتركون»، فلن ترى الاقتراحات بعد الآن. كما ستتوقف عن مشاركة أصدقائك مع جهات الاتصال الجديدة.'; + + @override + String get userDiscoveryEnabledChangeSettings => 'تغيير الإعدادات'; + + @override + String get userDiscoveryEnabledFaq => + 'نشرح في الأسئلة الشائعة كيف تعمل ميزة «الأصدقاء المشتركون».'; + + @override + String get userDiscoveryDisabledIntro => + 'لا يجمع twonly رقم هاتفك *ولا* يحتاج إلى الوصول إلى جهات اتصالك. بدلًا من ذلك، يمكن لـ twonly *العثور على أصدقائك من خلال الأصدقاء المشتركين*.'; + + @override + String get userDiscoverySettingsTitle => 'الأصدقاء المشتركون'; + + @override + String get userDiscoveryFeatureOffers => 'مزاياك في لمحة'; + + @override + String get userDiscoveryDisabledLearnMore => 'اعرف المزيد'; + + @override + String get userDiscoveryEnabledDialogTitle => 'هل تريد التعطيل حقًا؟'; + + @override + String get userDiscoveryEnabledFriendsShared => 'الأصدقاء الذين تشاركهم'; + + @override + String get userDiscoveryEnabledFriendsSharedDesc => + 'أنت تشارك فقط الأصدقاء الذين فعّلوا هذه الميزة أيضًا ووصلوا إلى الحد الذي حددته.'; + + @override + String get userDiscoverySettingsCurrentlyDisabled => + 'ميزة «الأصدقاء المشتركون» معطّلة حاليًا.'; + + @override + String get userDiscoveryEnabledNoFriendsShared => 'أنت لا تشارك أحدًا بعد.'; + + @override + String get userDiscoveryActionDisable => 'تعطيل'; + + @override + String get friendSuggestionsTitle => 'اقتراحات الأصدقاء'; + + @override + String get andWord => 'و'; + + @override + String friendSuggestionsFriendsWith(Object friends) { + return 'أصدقاء مشتركون: $friends.'; + } + + @override + String friendSuggestionsGroupMemberIn(Object groups) { + return ' مجموعات مشتركة: $groups.'; + } + + @override + String get friendSuggestionsRequest => 'إرسال طلب'; + + @override + String get friendSuggestionsAskFriend => 'اسأل أصدقاءك'; + + @override + String askFriendsDialogTitle(Object username) { + return 'اسأل عن $username'; + } + + @override + String get askFriendsDialogDescription => + 'اختر الأصدقاء الذين تريد سؤالهم عن هذا المستخدم:'; + + @override + String get askFriendsDialogConfirm => 'اسأل'; + + @override + String get askFriendsDialogCancel => 'إلغاء'; + + @override + String get chatAskAFriendReceivedDescription => + 'تلقّى صديقك هذا الاقتراح للتو ويريد أن يعرف ما إذا كان يعرف هذا الشخص.'; + + @override + String get chatAskAFriendAddedDescription => + 'لقد أضفت هذا المستخدم إلى جهات اتصالك.'; + + @override + String get chatAskAFriendHide => 'إخفاء'; + + @override + String get chatAskAFriendRequest => 'إرسال طلب'; + + @override + String chatAskAFriendUnknownUser(Object userId) { + return 'المستخدم $userId'; + } + + @override + String contactUserDiscoveryImagesLeft(Object imagesLeft, Object username) { + return 'الصور المتبقية قبل مشاركة أصدقائك مع $username: $imagesLeft.'; + } + + @override + String userDiscoveryEnabledVersion(Object version) { + return 'الإصدار: $version'; + } + + @override + String userDiscoveryEnabledYourVersion(Object version) { + return 'إصدارك: $version'; + } + + @override + String get userDiscoveryEnabledStopSharing => 'إيقاف المشاركة'; + + @override + String userDiscoveryManualApprovalReachedThreshold(Object username) { + return 'هل ترغب في مشاركة $username مع أصدقائك؟'; + } + + @override + String get userDiscoveryManualApprovalHideContact => 'إخفاء جهة الاتصال'; + + @override + String get userDiscoveryManualApprovalShareContact => 'مشاركة جهة الاتصال'; + + @override + String onboardingSetupCompleteTitle(Object username) { + return 'كل شيء جاهز يا $username!'; + } + + @override + String get onboardingSetupCompleteDesc => + 'يمكنك الآن مشاركة لحظاتك مع أصدقائك بأمان ودون مشتتات مثل الإعلانات.'; + + @override + String get contactUserDiscoveryManualApprovalPending => + 'لم تتم الموافقة على جهة الاتصال يدويًا بعد.'; + + @override + String get contactUserDiscoveryManualApprovalApprove => 'موافقة'; + + @override + String get exampleUserName1 => 'ahmed'; + + @override + String get exampleUserName2 => 'fatima'; + + @override + String get exampleUserName3 => 'omar'; + + @override + String get exampleUserName4 => 'layla'; + + @override + String get exampleUserName5 => 'youssef'; + + @override + String get exampleUserName6 => 'mariam'; + + @override + String get exampleUserName7 => 'khaled'; + + @override + String get exampleUserName8 => 'nour'; + + @override + String get exampleUserName9 => 'hassan'; + + @override + String get exampleUserName10 => 'lena'; + + @override + String get exampleUserName11 => 'david'; + + @override + String get exampleJane => 'sara'; + + @override + String get back => 'رجوع'; + + @override + String makerChangedUsername(Object maker, Object oldName, Object newName) { + return 'غيّر $maker اسم المستخدم من $oldName إلى $newName.'; + } + + @override + String makerChangedDisplayName(Object maker, Object oldName, Object newName) { + return 'غيّر $maker الاسم المعروض من $oldName إلى $newName.'; + } + + @override + String get recoverErrorNoInternet => + 'لا يوجد اتصال بالإنترنت. يُرجى التحقق من الشبكة والمحاولة مرة أخرى.'; + + @override + String get recoverErrorUsernameNotValid => + 'اسم المستخدم المُدخل غير صالح أو غير موجود.'; + + @override + String get recoverErrorPasswordInvalid => 'كلمة المرور المُدخلة غير صحيحة.'; + + @override + String get recoverErrorTryAgainLater => + 'الخادم غير متاح حاليًا. يُرجى المحاولة لاحقًا.'; + + @override + String get recoverErrorUnknown => + 'حدث خطأ غير معروف. يُرجى المحاولة مرة أخرى.'; + + @override + String get recoverSuccessTitle => 'تمت استعادة النسخة الاحتياطية بنجاح.'; + + @override + String get recoverSuccessBody => 'اضغط هنا لفتح التطبيق مرة أخرى'; + + @override + String get recoverProgressTitle => 'جارٍ استعادة نسختك الاحتياطية'; + + @override + String get recoverProgressSubtitle => + 'قد يستغرق هذا بعض الوقت، يُرجى إبقاء التطبيق مفتوحًا.'; + + @override + String get recoverProgressResolvingAccount => 'جارٍ التحقق من الحساب'; + + @override + String get recoverProgressRestoringIdentity => 'جارٍ استعادة هويتك'; + + @override + String get recoverProgressDownloadingArchive => + 'جارٍ تنزيل الرسائل وجهات الاتصال وفك تشفيرها'; + + @override + String get recoverProgressExtractingData => 'جارٍ استخراج البيانات'; + + @override + String get recoverProgressFinishing => 'جارٍ الإنهاء'; + + @override + String get iosRecoveryWelcomeBack => 'مرحبًا بعودتك'; + + @override + String get iosRecoveryPrompt => + 'اكتشفنا هوية twonly مؤمّنة سابقًا على هذا الجهاز. هل تريد تنزيل جهات اتصالك ورسائلك وإعداداتك واستعادتها تلقائيًا من أرشيفك السحابي؟'; + + @override + String iosRecoveryNoBackupFound(Object error) { + return 'تعذّر استرداد أي أرشيف نسخ احتياطي من الخادم لهذا الجهاز.\n\nالخطأ: $error\n\nيُرجى المتابعة لتسجيل حساب twonly جديد.'; + } + + @override + String get registerNewAccount => 'تسجيل حساب جديد'; + + @override + String get tryRestoreAgain => 'إعادة محاولة الاستعادة'; + + @override + String get registeringNewAccount => 'جارٍ تسجيل حساب جديد'; + + @override + String get selectEmoji => 'اختر رمزًا تعبيريًا'; + + @override + String get subscriptionPledgeSecureTitle => 'آمن من حيث التصميم'; + + @override + String get subscriptionPledgeSecureDesc => + 'رسائلك ولحظاتك المشتركة مشفّرة بالكامل بين الطرفين.'; + + @override + String get subscriptionPledgeNoAdsTitle => 'لا إعلانات ولا بيع للبيانات'; + + @override + String get subscriptionPledgeNoAdsDesc => + 'لن يعرض twonly إعلانات أبدًا ولن يبيع بياناتك الخاصة.'; + + @override + String get subscriptionPledgeSubtitle => 'بلا إعلانات. خصوصية تامة.'; + + @override + String get subscriptionManage => 'إدارة الاشتراك'; + + @override + String get dragToZoom => 'اسحب للتكبير'; + + @override + String get onboardingProfileSelectionTitle => 'اختر طريقة الإعداد'; + + @override + String get onboardingProfileSelectionSubtitle => + 'اختر كيف تريد ضبط إعدادات الأمان والخصوصية.'; + + @override + String get onboardingProfileSelectionDefaultTitle => 'افتراضي'; + + @override + String get onboardingProfileSelectionDefaultDesc => + 'يطبّق الإعدادات الموصى بها فورًا لتبدأ باستخدام التطبيق.'; + + @override + String get onboardingProfileSelectionDefaultBadge => 'إعداد سريع'; + + @override + String get onboardingProfileSelectionCustomizeTitle => 'تخصيص'; + + @override + String get onboardingProfileSelectionCustomizeDesc => + 'إعداد خطوة بخطوة لتقرر بنفسك.'; + + @override + String get replyFlameRestored => 'تمت استعادة الشعلات'; + + @override + String get replyAskAFriend => 'اسأل صديقًا'; + + @override + String get unverifiedWarningButton => 'تحقق الآن'; + + @override + String get today => 'اليوم'; + + @override + String get yesterday => 'أمس'; + + @override + String get galleryDisableWarningTitle => 'تعطيل الحفظ في المعرض؟'; + + @override + String get galleryDisableWarningBody => + 'إذا عطّلت هذا الخيار، فلن تُحفظ ملفات الوسائط في معرضك، وقد تُفقد نهائيًا إذا أُزيل twonly أو واجه مشكلة، لأن ملفات الوسائط لا تُنسخ احتياطيًا بعد.'; + + @override + String get galleryDisableWarningConfirm => 'تعطيل'; + + @override + String get settingsStorageScanGalleryTitle => 'استيراد من المعرض'; + + @override + String get importGalleryDeselectAll => 'إلغاء تحديد الكل'; + + @override + String get importGallerySelectAll => 'تحديد الكل'; + + @override + String get importGalleryPermissionRequired => + 'يلزم إذن الوصول إلى معرضك لاستيراد ملفات وسائط twonly السابقة.'; + + @override + String importGalleryPermissionError(Object error) { + return 'حدث خطأ أثناء طلب الإذن: $error'; + } + + @override + String importGalleryLoadError(Object error) { + return 'تعذّر تحميل الملفات: $error'; + } + + @override + String importGalleryImportingOf(Object current, Object total) { + return 'جارٍ استيراد $current من $total...'; + } + + @override + String get importGalleryStarting => 'جارٍ بدء الاستيراد...'; + + @override + String importGalleryComplete(Object imported, Object failed) { + return 'اكتمل الاستيراد: نجح استيراد $imported وفشل $failed.'; + } + + @override + String get importGalleryGrantAccess => 'منح الوصول'; + + @override + String get importGalleryOpenSettings => 'فتح الإعدادات'; + + @override + String get importGalleryPermissionDenied => 'تم رفض إذن الوصول إلى المعرض.'; + + @override + String get importGalleryTryAgain => 'حاول مرة أخرى'; + + @override + String get importGalleryAlbumNotFound => 'لم يتم العثور على ألبوم «twonly»'; + + @override + String get importGalleryAlbumNotFoundDesc => + 'إذا لم يكن لديك هذا الألبوم بعد، يمكنك أيضًا إنشاؤه لاستيراد الصور إلى twonly.'; + + @override + String get importGalleryNoImagesFound => 'لم يتم العثور على صور'; + + @override + String get importGalleryNoImagesFoundDesc => 'لا توجد صور على جهازك.'; + + @override + String get importGalleryFilterTwonly => 'عرض ألبوم twonly فقط'; + + @override + String get importGalleryRefresh => 'تحديث'; + + @override + String get importGallerySelectToImport => 'اختر العناصر المراد استيرادها'; + + @override + String importGalleryImportCount(num count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: 'استيراد $count عنصر', + many: 'استيراد $count عنصرًا', + few: 'استيراد $count عناصر', + two: 'استيراد عنصرين', + one: 'استيراد عنصر واحد', + ); + return '$_temp0'; + } + + @override + String get emptyChatListTitle => 'ابحث عن صديقك الأول'; + + @override + String get emptyChatListDesc => + 'اطلب من أصدقائك مسح رمز QR الخاص بك، أو شارك ملفك الشخصي معهم.'; + + @override + String get emptyChatListShareBtn => 'شارك ملفك الشخصي'; + + @override + String get emptyChatListScanBtn => 'رمز QR'; + + @override + String get emptyChatListAddUsernameBtn => 'باسم المستخدم'; + + @override + String get avatarCustomizeRandomize => 'عشوائي'; + + @override + String get avatarCustomizeReset => 'إعادة تعيين'; + + @override + String get passwordlessRecovery => 'الأصدقاء الموثوقون'; + + @override + String get passwordlessRecoveryNotConfigured => 'غير مُعدّ'; + + @override + String get passwordlessRecoveryTestPin => 'اختبار رمز PIN'; + + @override + String get passwordlessRecoveryTestPinTitle => 'اختبار رمز PIN'; + + @override + String get passwordlessRecoveryTestPinHint => 'أدخل رمز PIN'; + + @override + String get passwordlessRecoveryTestPinCorrect => 'رمز PIN صحيح!'; + + @override + String get passwordlessRecoveryTestPinIncorrect => 'رمز PIN غير صحيح.'; + + @override + String get passwordlessRecoveryTest => 'اختبار'; + + @override + String get passwordlessRecoverySecondFactorNone => 'لا شيء'; + + @override + String passwordlessRecoverySecondFactorEmailLabel(Object email) { + return 'البريد الإلكتروني ($email)'; + } + + @override + String get passwordlessRecoverySecondFactorPin => 'رمز PIN'; + + @override + String get passwordlessRecoverySecondFactorEmail => 'البريد الإلكتروني'; + + @override + String get passwordlessRecoveryModify => 'تعديل إعدادات الاستعادة'; + + @override + String get passwordlessRecoveryModifyDesc => + 'حدّث إعدادات الاستعادة الخاصة بك'; + + @override + String get passwordlessRecoverySecondFactor => 'العامل الثاني'; + + @override + String get passwordlessRecoveryNoFriendsFound => + 'لم يتم العثور على أصدقاء موثوقين.'; + + @override + String get passwordlessRecoveryActiveFriends => 'الأصدقاء النشطون'; + + @override + String get passwordlessRecoveryActiveFriendsDesc => + 'هؤلاء الأصدقاء الموثوقون يستخدمون twonly بنشاط، ومن المرجح أن يتمكنوا من مساعدتك في استعادة حسابك.'; + + @override + String get passwordlessRecoveryInactiveFriends => 'الأصدقاء غير النشطين'; + + @override + String get passwordlessRecoveryInactiveFriendsDesc => + 'هؤلاء الأصدقاء إما لم يتلقوا حصتهم بعد أو لم يعودوا يستخدمون twonly بنشاط، مما قد يعني أنهم لا يستطيعون المساعدة.'; + + @override + String get passwordlessRecoveryEnableSuccess => + 'تم إعداد الأصدقاء الموثوقين بنجاح!'; + + @override + String get passwordlessRecoveryEnterPin => 'يُرجى إدخال رمز PIN.'; + + @override + String get passwordlessRecoveryEnterEmail => + 'يُرجى إدخال عنوان بريد إلكتروني.'; + + @override + String get passwordlessRecoveryEnableBtn => 'تفعيل الأصدقاء الموثوقين'; + + @override + String get passwordlessRecoveryRecoverBtn => + 'الاستعادة عبر الأصدقاء الموثوقين'; + + @override + String get passwordlessRecoveryModifyBtn => 'تعديل الأصدقاء الموثوقين'; + + @override + String passwordlessRecoveryStatusEnabled(num count) { + return 'مفعّل • الأصدقاء الموثوقون: $count'; + } + + @override + String get passwordlessRecoveryInfoHowItWorks => 'كيف يعمل'; + + @override + String get passwordlessRecoveryInfoHowItWorksDesc => + 'يعمل twonly دون أرقام هواتف، ولهذا نعتمد على آلية استعادة لامركزية. باستخدام مخطط شامير لتقاسم الأسرار (Shamir\'s Secret Sharing)، تُقسَّم هويتك التشفيرية إلى حصص مستقلة وتُوزَّع على أصدقائك الموثوقين. لاستعادة الوصول، يجب أن يجمع عدد محدد مسبقًا من هؤلاء الأصدقاء حصصهم.'; + + @override + String get passwordlessRecoveryInfoWhySecondFactor => 'لماذا العامل الثاني؟'; + + @override + String get passwordlessRecoveryInfoWhySecondFactorDesc => + 'يوفّر العامل الثاني (البريد الإلكتروني أو رمز PIN) حماية إضافية مهمة. فحتى لو اتفق أصدقاؤك سرًّا فيما بينهم، فلن يتمكنوا من الوصول إلى حسابك دون هذا العامل الثاني.'; + + @override + String get passwordlessRecoveryInfoGotIt => 'فهمت'; + + @override + String get passwordlessRecoveryMethod => 'طريقة العامل الثاني'; + + @override + String get passwordlessRecoveryMethodNoneDesc => + 'بدون عامل ثانٍ، يمكن لأصدقائك التعاون لاستعادة حسابك. لذلك يُنصح بإعداد عامل ثانٍ.'; + + @override + String get passwordlessRecoveryMethodPinHint => 'أدخل رمز PIN'; + + @override + String get passwordlessRecoveryMethodEmailHint => + 'أدخل البريد الإلكتروني للاستعادة'; + + @override + String get passwordlessRecoveryMethodEmailDesc => + 'لا يتم *تخزين عنوان بريدك الإلكتروني على الخادم أبدًا*، ولا يُرسل إليه إلا في حالة الاستعادة.'; + + @override + String passwordlessRecoveryThresholdDesc(num count) { + return 'لاستعادة حسابك، تحتاج إلى $count من أصدقائك الموثوقين المختارين.'; + } + + @override + String get passwordlessRecoveryThresholdTitle => + 'عدد الأصدقاء الموثوقين المطلوب للاستعادة'; + + @override + String passwordlessRecoverySelectFriendsNeeded(num count) { + return 'اختر الأصدقاء (المتبقي: $count)'; + } + + @override + String get passwordlessRecoverySelectFriends => 'اختر الأصدقاء الموثوقين'; + + @override + String get passwordlessRecoveryTrustedFriends => 'استعادة الحساب'; + + @override + String passwordlessRecoveryDoneBtn(num count) { + return 'تم ($count)'; + } + + @override + String get passwordlessRecoveryNoFriendsSelected => + 'لم يتم اختيار أي أصدقاء موثوقين بعد'; + + @override + String get passwordlessRecoverySubtitle => 'استعد هويتك دون كلمة مرور.'; + + @override + String get recoverPasswordlessExplanation => + 'عند التفعيل، يمكنك استعادة حسابك بطلب المساعدة من أصدقائك المختارين.'; + + @override + String get recoverPasswordlessNotificationCardTitle => 'تفعيل الإشعارات'; + + @override + String get recoverPasswordlessNotificationCardSubtitle => + 'احصل على إشعار عندما يساعدك صديق في استعادة حسابك.'; + + @override + String get recoverPasswordlessNotificationCardBtn => + 'تفعيل الإشعارات الفورية'; + + @override + String get recoverPasswordlessQrInstructions => + 'اطلب من أصدقائك مسح رمز QR أو شارك الرابط.'; + + @override + String get recoverPasswordlessShareBtn => 'مشاركة رابط الاستعادة'; + + @override + String get recoverPasswordlessCopyBtn => 'نسخ'; + + @override + String get recoverPasswordlessCopiedSnackbar => 'تم نسخ الرابط إلى الحافظة!'; + + @override + String get passwordlessRecoveryNoShareStored => + 'لا توجد حصة استعادة محفوظة لجهة الاتصال هذه.'; + + @override + String get passwordlessRecoveryEmailSent => 'تم إرسال بريد الاستعادة!'; + + @override + String get passwordlessRecoveryShareSent => 'تم إرسال حصة الاستعادة!'; + + @override + String get passwordlessRecoveryAuthReason => + 'قم بالمصادقة لإرسال حصة الاستعادة الخاصة بك'; + + @override + String get passwordlessRecoveryAuthFailed => + 'فشلت المصادقة، ولم يتم إرسال حصة الاستعادة.'; + + @override + String get passwordlessRecoveryNetworkError => + 'خطأ في الشبكة، يُرجى التأكد من اتصالك بالإنترنت'; + + @override + String get passwordlessRecoveryInvalidEmail => + 'عنوان البريد الإلكتروني غير صالح.'; + + @override + String get passwordlessRecoveryResendEmail => 'إعادة إرسال بريد الاستعادة'; + + @override + String get passwordlessRecoveryHelpAFriend => 'ساعد صديقًا'; + + @override + String get passwordlessRecoverySelectContactDesc => + 'يُرجى اختيار جهة الاتصال التي طلبت الاستعادة أدناه لإرسال مفتاح الاستعادة إليها.'; + + @override + String get passwordlessRecoverySearchContacts => 'البحث في جهات الاتصال...'; + + @override + String get passwordlessRecoveryNoContactsFound => + 'لم يتم العثور على جهات اتصال'; + + @override + String get passwordlessRecoveryCantHelpHim => 'لا يمكنك مساعدة هذا الشخص'; + + @override + String passwordlessRecoveryDoesAskedYou(Object username) { + return 'هل وصلك هذا الطلب من $username؟'; + } + + @override + String get passwordlessRecoveryVerifySourceDesc => + 'يُرجى التأكد من أن هذا الطلب جاء فعلًا من هذا الشخص. إذا كنت في شك، فاسأله عبر وسيلة أخرى!'; + + @override + String get passwordlessRecoveryNo => 'لا'; + + @override + String get passwordlessRecoveryYes => 'نعم'; + + @override + String passwordlessRecoveryYesWithTimer(Object seconds) { + return 'نعم ($seconds ث)'; + } + + @override + String recoverPasswordlessSharesReceived(Object received, Object threshold) { + return 'الأصدقاء الذين ساعدوك: $received من $threshold'; + } + + @override + String get recoverPasswordlessRecoverNowBtn => 'استعد الآن'; + + @override + String get missingRecoveryContactsCardTitle => 'استعادة الحساب'; + + @override + String get missingRecoveryContactsCardDesc => + 'اختر جهات اتصال للاستعادة لتساعدك في استعادة حسابك دون الحاجة إلى كلمة مرور.'; + + @override + String get missingRecoveryContactsCardAction => 'اختر جهات الاتصال'; + + @override + String get memoriesBackupLimitReached => + 'تم الوصول إلى حد النسخ الاحتياطي السحابي! يُرجى ترقية خطتك أو إخلاء بعض المساحة.'; + + @override + String get settingsStorageLocal => 'التخزين المحلي'; + + @override + String get settingsStorageNoCloudBackupCard => + 'انسخ ذكرياتك احتياطيًا إلى السحابة لتوفير المساحة المحلية وضمان عدم فقدان صورك أبدًا. كل ذلك بتشفير تام بين الطرفين.'; + + @override + String get galleryActionSave => 'حفظ'; + + @override + String get galleryActionExport => 'تصدير'; + + @override + String get galleryActionFavorite => 'إضافة إلى المفضلة'; + + @override + String get galleryActionUnfavorite => 'إزالة من المفضلة'; + + @override + String get galleryActionDelete => 'حذف'; + + @override + String get galleryActionShare => 'مشاركة'; + + @override + String get settingsStorageHidePromo => 'إخفاء'; + + @override + String get memoriesBackupTitle => 'النسخ الاحتياطي للذكريات'; + + @override + String get settingsHelpNews => 'الأخبار'; + + @override + String get settingsStorageContents => 'إخلاء مساحة'; + + @override + String get settingsStorageSortStorage => 'المساحة المشغولة'; + + @override + String get settingsStorageSortNewest => 'الأحدث'; + + @override + String get settingsStorageSortOldest => 'الأقدم'; + + @override + String get settingsStorageNoContents => + 'لم يتم العثور على محتويات في التخزين.'; + + @override + String get settingsStorageCloudOnly => 'في السحابة فقط'; + + @override + String get settingsStorageLocalAndCloud => 'محليًا وفي السحابة'; + + @override + String get settingsStorageLocalOnly => 'محليًا فقط'; + + @override + String get settingsStorageByChat => 'التخزين حسب المحادثة'; + + @override + String get settingsStorageTemporaryMediaDescription => + 'ملفات الوسائط المؤقتة هي ملفات وسائط مرسلة لم يتم حفظها. تبقى على جهازك حتى يستلمها المستلم ويفتحها.'; + + @override + String get settingsStorageNoChats => 'لم يتم العثور على محادثات.'; + + @override + String settingsStorageStoredMediaCount(num count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: '$count ملف محفوظ', + many: '$count ملفًا محفوظًا', + few: '$count ملفات محفوظة', + two: 'ملفان محفوظان', + one: 'ملف محفوظ واحد', + ); + return '$_temp0'; + } + + @override + String settingsStorageTemporaryMediaCount(num count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: '$count ملف مؤقت', + many: '$count ملفًا مؤقتًا', + few: '$count ملفات مؤقتة', + two: 'ملفان مؤقتان', + one: 'ملف مؤقت واحد', + ); + return '$_temp0'; + } + + @override + String get settingsStorageDisableBackupTitle => 'تعطيل النسخ الاحتياطي؟'; + + @override + String settingsStorageDisableBackupBody(num count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: 'توجد *$count صورة* مخزّنة في السحابة فقط وستُفقد *نهائيًا*.', + many: 'توجد *$count صورة* مخزّنة في السحابة فقط وستُفقد *نهائيًا*.', + few: 'توجد *$count صور* مخزّنة في السحابة فقط وستُفقد *نهائيًا*.', + two: 'توجد *صورتان* مخزّنتان في السحابة فقط وستُفقدان *نهائيًا*.', + one: 'توجد *صورة واحدة* مخزّنة في السحابة فقط وستُفقد *نهائيًا*.', + ); + return 'سيؤدي هذا إلى حذف جميع الذكريات المنسوخة احتياطيًا من السحابة. $_temp0 هل أنت متأكد أنك تريد المتابعة؟'; + } + + @override + String get settingsStorageDisableBackupBtn => 'تعطيل'; + + @override + String get settingsStorageDisableBackupAction => + 'تعطيل النسخ الاحتياطي السحابي'; + + @override + String get settingsStorageSyncNow => 'مزامنة الآن'; + + @override + String get settingsStorageSyncUpToDate => 'جميع الذكريات محدّثة.'; + + @override + String restoreLostFlames(int count) { + return 'استعد شعلاتك المفقودة ($count)'; + } + + @override + String get settingsShowRestoreFlameTitle => 'إظهار تحذير استعادة الشعلات'; + + @override + String get contactGroupsTitle => 'مجموعات جهات الاتصال'; + + @override + String get contactGroupsSubtitleEmpty => + 'لم يتم اختيار مجموعات جهات اتصال ظاهرة'; + + @override + String get createContactGroup => 'إنشاء مجموعة جهات اتصال'; + + @override + String get editContactGroup => 'تعديل مجموعة جهات الاتصال'; + + @override + String get deleteContactGroup => 'حذف مجموعة جهات الاتصال'; + + @override + String get deleteContactGroupConfirmation => + 'هل أنت متأكد أنك تريد حذف مجموعة جهات الاتصال هذه؟ ستتم إزالتها من جميع جهات الاتصال والمجموعات.'; + + @override + String get contactGroupTextColor => 'لون النص'; + + @override + String get contactGroupBackgroundColor => 'لون الخلفية'; + + @override + String get contactGroupNoBackground => 'بلا خلفية'; + + @override + String get contactGroupFeatures => 'الميزات'; + + @override + String get contactGroupShowAsLabel => 'إظهار كتسمية ظاهرة'; + + @override + String get contactGroupShowAsLabelSubtitle => + 'عرض مجموعة جهات الاتصال هذه بجوار جهات الاتصال التابعة لها.'; + + @override + String get contactGroupShowAsShortcut => 'إظهار كاختصار'; + + @override + String get contactGroupShowAsShortcutSubtitle => + 'استخدم رمزها التعبيري لتحديد جميع الأعضاء عند المشاركة.'; + + @override + String get contactGroupShortcutNeedsEmoji => + 'اختر رمزًا تعبيريًا قبل تفعيل الاختصار.'; + + @override + String get contactGroupMembers => 'الأعضاء'; + + @override + String get contactGroupLabelFeature => 'تسمية ظاهرة'; + + @override + String get contactGroupShortcutFeature => 'اختصار'; + + @override + String get contactGroupSettings => 'تعديل مجموعة جهات الاتصال'; + + @override + String get save => 'حفظ'; + + @override + String get customColor => 'لون مخصص'; + + @override + String get hue => 'درجة اللون'; + + @override + String get saturation => 'التشبع'; + + @override + String get brightness => 'السطوع'; + + @override + String get settingsAppearancePrimaryColor => 'اللون الأساسي'; + + @override + String get themeSystemDefault => 'افتراضي النظام'; + + @override + String get themeLight => 'فاتح'; + + @override + String get themeDark => 'داكن'; + + @override + String get backupRecoverySectionTitle => 'وصول آمن إلى الحساب'; + + @override + String get backupRecoverySectionDescNone => + 'يجب عليك اختيار خيار استعادة واحد على الأقل، وإلا فلن يتمكن أحد من مساعدتك إذا فقدت الوصول إلى جهازك.'; + + @override + String get backupRecoverySectionDescSome => + 'يمكنك استعادة حسابك باستخدام خيارات الاستعادة أدناه. يُنصح بتفعيل الخيارين كليهما.'; + + @override + String get backupRecoveryOptionAFriends => 'عبر الأصدقاء الموثوقين'; + + @override + String get backupRecoveryOptionAMicrocopy => + 'اختر أصدقاء يمكنهم مساعدتك في استعادة حسابك.'; + + @override + String get backupRecoveryOptionBPassword => 'كلمة مرور تقليدية'; + + @override + String get backupRecoveryOptionBMicrocopy => + 'عيّن كلمة مرور للوصول إلى حسابك.'; + + @override + String get backupCloudSectionTitle => 'انسخ بياناتك احتياطيًا'; + + @override + String get backupCloudSectionDesc => 'انسخ محتواك احتياطيًا حتى لا يضيع شيء.'; + + @override + String get backupCloudContactsMessages => 'جهات الاتصال والرسائل'; + + @override + String get backupCloudImagesMedia => 'الصور والوسائط'; + + @override + String get backupCloudProBadge => 'Pro'; + + @override + String get memoriesAddingToFavorites => 'جارٍ الإضافة إلى المفضلة...'; + + @override + String get memoriesRemovingFromFavorites => 'جارٍ الإزالة من المفضلة...'; + + @override + String get memoriesDeletingProgress => 'جارٍ حذف الذكريات...'; + + @override + String get memoriesExportingProgress => 'جارٍ تصدير الذكريات...'; + + @override + String get fileLimitReachedTitle => 'الملف أكبر من أن يُرسل'; + + @override + String fileLimitReachedDetail(String size, String limit) { + return 'حجم هذا الملف $size، لكن خطتك تسمح بـ $limit كحد أقصى لكل إرسال.'; + } + + @override + String fileLimitReachedDetailNoSize(String limit) { + return 'تسمح خطتك بـ $limit كحد أقصى لكل إرسال.'; + } + + @override + String get fileLimitReachedHint => 'سجّل فيديو أقصر وأرسله مرة أخرى.'; + + @override + String get fileLimitReachedHintFree => + 'سجّل فيديو أقصر، أو قم بترقية خطتك لإرسال ملفات أكبر.'; + + @override + String get fileLimitReachedUpgrade => 'ترقية الخطة'; + + @override + String get contactGroupUsedByWidget => 'أداة الشاشة الرئيسية'; + + @override + String contactGroupUsedByWidgetSubtitle(int count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: 'تعرض $count أداة على شاشتك الرئيسية صورًا من هذه المجموعة.', + many: 'تعرض $count أداةً على شاشتك الرئيسية صورًا من هذه المجموعة.', + few: 'تعرض $count أدوات على شاشتك الرئيسية صورًا من هذه المجموعة.', + two: 'تعرض أداتان على شاشتك الرئيسية صورًا من هذه المجموعة.', + one: 'تعرض أداة على شاشتك الرئيسية صورًا من هذه المجموعة.', + ); + return '$_temp0'; + } + + @override + String contactGroupDeleteBlockedByWidget(int count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: + 'تستخدم $count أداة على شاشتك الرئيسية مجموعة جهات الاتصال هذه. أزل تلك الأدوات أولًا، ثم يمكنك حذف المجموعة.', + many: + 'تستخدم $count أداةً على شاشتك الرئيسية مجموعة جهات الاتصال هذه. أزل تلك الأدوات أولًا، ثم يمكنك حذف المجموعة.', + few: + 'تستخدم $count أدوات على شاشتك الرئيسية مجموعة جهات الاتصال هذه. أزل تلك الأدوات أولًا، ثم يمكنك حذف المجموعة.', + two: + 'تستخدم أداتان على شاشتك الرئيسية مجموعة جهات الاتصال هذه. أزل هاتين الأداتين أولًا، ثم يمكنك حذف المجموعة.', + one: + 'تستخدم أداةٌ على شاشتك الرئيسية مجموعة جهات الاتصال هذه. أزل تلك الأداة أولًا، ثم يمكنك حذف المجموعة.', + ); + return '$_temp0'; + } + + @override + String get webxdcStoreMenu => 'تطبيق'; + + @override + String get webxdcStoreTitle => 'إضافة تطبيق'; + + @override + String get webxdcStoreEmpty => 'لا توجد تطبيقات متاحة بعد.'; + + @override + String get webxdcStoreOffline => + 'تعذّر تحميل قائمة التطبيقات. تحقق من اتصالك وحاول مرة أخرى.'; + + @override + String get webxdcStoreFailed => 'تعذّرت إضافة التطبيق إلى هذه المحادثة.'; + + @override + String get webxdcStoreAlreadyAdded => 'مضاف بالفعل إلى هذه المحادثة'; + + @override + String get webxdcChatApps => 'تطبيقات المحادثة'; + + @override + String get webxdcChatAppsEmpty => + 'لم تتم إضافة أي تطبيقات لمرة واحدة إلى هذه المحادثة بعد.'; + + @override + String get webxdcUnavailable => 'تعذّر تحميل هذا التطبيق.'; + + @override + String get webxdcDeleteConfirm => + 'سيتم حذف كل ما حفظه هذا التطبيق على هذا الجهاز. يحتفظ الأعضاء الآخرون بنسختهم الخاصة.'; + + @override + String get webxdcExternalLinkTitle => 'مغادرة twonly؟'; + + @override + String get webxdcExternalLinkBody => + 'يُفتح هذا الرابط خارج twonly، في متصفحك:'; +} diff --git a/lib/src/localization/generated/app_localizations_de.dart b/lib/src/localization/generated/app_localizations_de.dart index 5d3e1cae..65ec9b15 100644 --- a/lib/src/localization/generated/app_localizations_de.dart +++ b/lib/src/localization/generated/app_localizations_de.dart @@ -339,9 +339,6 @@ class AppLocalizationsDe extends AppLocalizations { @override String get settingsHelpFAQ => 'FAQ'; - @override - String get feedbackTooltip => 'Feedback zur Verbesserung von twonly geben.'; - @override String get settingsHelpContactUs => 'Kontaktiere uns'; @@ -416,6 +413,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get settingsAppearanceTheme => 'Theme'; + @override + String get settingsAppearanceLanguage => 'Sprache'; + @override String get settingsAccountDeleteAccount => 'Konto löschen'; @@ -743,27 +743,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get additionalUsersList => 'Deine zusätzlichen Benutzer'; - @override - String get galleryDelete => 'Datei löschen'; - - @override - String get galleryExport => 'In Galerie exportieren'; - @override String get galleryExportSuccess => 'Erfolgreich in der Gallery gespeichert.'; - @override - String get gallerySelectAll => 'Alle auswählen'; - - @override - String get galleryDeselectAll => 'Auswahl aufheben'; - - @override - String get galleryFavorite => 'Als Favorit markieren'; - - @override - String get galleryUnfavorite => 'Favorit entfernen'; - @override String get galleryCancel => 'Abbrechen'; @@ -797,9 +779,6 @@ class AppLocalizationsDe extends AppLocalizations { @override String get deleteImageTitle => 'Bist du dir sicher?'; - @override - String get deleteImageBody => 'Das Bild wird unwiderruflich gelöscht.'; - @override String deleteMemoriesBody(num count) { String _temp0 = intl.Intl.pluralLogic( @@ -849,38 +828,10 @@ class AppLocalizationsDe extends AppLocalizations { @override String get settingsBackup => 'Backup'; - @override - String get backupPending => 'Ausstehend'; - - @override - String get backupFailed => 'Fehlgeschlagen'; - - @override - String get backupSuccess => 'Erfolgreich'; - - @override - String get backupTwonlySafeDesc => - 'Sichere deine twonly-Identität, da dies die einzige Möglichkeit ist, dein Konto wiederherzustellen, wenn du die App deinstallierst oder dein Handy verlierst.'; - @override String get backupNoPasswordRecovery => 'Aufgrund des Sicherheitssystems von twonly gibt es (derzeit) keine Funktion zur Wiederherstellung des Passworts. Daher musst du dir dein Passwort merken oder, besser noch, aufschreiben.'; - @override - String get backupIdentityHeader => 'Identität'; - - @override - String get backupArchiveHeader => 'Kontakte & Nachrichten'; - - @override - String get backupLastBackupDate => 'Letztes Backup'; - - @override - String get backupLastBackupSize => 'Backup-Größe'; - - @override - String get backupLastBackupResult => 'Ergebnis'; - @override String get backupInsecurePassword => 'Unsicheres Passwort'; @@ -1290,10 +1241,6 @@ class AppLocalizationsDe extends AppLocalizations { @override String get notificationCategoryMessageTitle => 'Nachrichten'; - @override - String get notificationCategoryMessageDesc => - 'Nachrichten von anderen Benutzern.'; - @override String get notificationConnectionFallback => 'Du könntest neue Nachrichten haben.'; @@ -2546,9 +2493,6 @@ class AppLocalizationsDe extends AppLocalizations { @override String get settingsStorageLocal => 'Lokaler Speicherplatz'; - @override - String get settingsStorageNoCloudBackupTitle => 'Kein Cloud-Backup'; - @override String get settingsStorageNoCloudBackupCard => 'Sichere deine Erinnerungen in der Cloud, um lokalen Speicherplatz freizugeben und deine Bilder nicht zu verlieren. Alles Ende-zu-Ende verschlüsselt.'; @@ -2796,9 +2740,6 @@ class AppLocalizationsDe extends AppLocalizations { @override String get backupCloudContactsMessages => 'Kontakte & Nachrichten'; - @override - String get backupCloudFreeActive => 'Kostenlos aktiv'; - @override String get backupCloudImagesMedia => 'Bilder & Medien'; @@ -2891,10 +2832,7 @@ class AppLocalizationsDe extends AppLocalizations { String get webxdcStoreAlreadyAdded => 'Bereits zu diesem Chat hinzugefügt'; @override - String get webxdcChatAppsMenu => 'Chat-Apps'; - - @override - String get webxdcChatAppsTitle => 'Chat-Apps'; + String get webxdcChatApps => 'Chat-Apps'; @override String get webxdcChatAppsEmpty => diff --git a/lib/src/localization/generated/app_localizations_en.dart b/lib/src/localization/generated/app_localizations_en.dart index 2b83bc1b..08d56b89 100644 --- a/lib/src/localization/generated/app_localizations_en.dart +++ b/lib/src/localization/generated/app_localizations_en.dart @@ -336,9 +336,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get settingsHelpFAQ => 'FAQ'; - @override - String get feedbackTooltip => 'Give Feedback to improve twonly.'; - @override String get settingsHelpContactUs => 'Contact us'; @@ -413,6 +410,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get settingsAppearanceTheme => 'Theme'; + @override + String get settingsAppearanceLanguage => 'Language'; + @override String get settingsAccountDeleteAccount => 'Delete account'; @@ -739,27 +739,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get additionalUsersList => 'Your additional users'; - @override - String get galleryDelete => 'Delete file'; - - @override - String get galleryExport => 'Export to gallery'; - @override String get galleryExportSuccess => 'Successfully saved in the Gallery.'; - @override - String get gallerySelectAll => 'Select all'; - - @override - String get galleryDeselectAll => 'Deselect all'; - - @override - String get galleryFavorite => 'Favorite'; - - @override - String get galleryUnfavorite => 'Unfavorite'; - @override String get galleryCancel => 'Cancel'; @@ -793,9 +775,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get deleteImageTitle => 'Are you sure?'; - @override - String get deleteImageBody => 'The image will be irrevocably deleted.'; - @override String deleteMemoriesBody(num count) { String _temp0 = intl.Intl.pluralLogic( @@ -845,38 +824,10 @@ class AppLocalizationsEn extends AppLocalizations { @override String get settingsBackup => 'Backup'; - @override - String get backupPending => 'Pending'; - - @override - String get backupFailed => 'Failed'; - - @override - String get backupSuccess => 'Success'; - - @override - String get backupTwonlySafeDesc => - 'Back up your twonly identity, as this is the only way to restore your account if you uninstall the app or lose your phone.'; - @override String get backupNoPasswordRecovery => 'Due to twonly\'s security system, there is (currently) no password recovery function. Therefore, you must remember your password or, better yet, write it down.'; - @override - String get backupIdentityHeader => 'Identity'; - - @override - String get backupArchiveHeader => 'Contacts & Messages'; - - @override - String get backupLastBackupDate => 'Last backup'; - - @override - String get backupLastBackupSize => 'Backup size'; - - @override - String get backupLastBackupResult => 'Result'; - @override String get backupInsecurePassword => 'Insecure password'; @@ -1284,9 +1235,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get notificationCategoryMessageTitle => 'Messages'; - @override - String get notificationCategoryMessageDesc => 'Messages from other users.'; - @override String get notificationConnectionFallback => 'You may have new messages.'; @@ -2523,9 +2471,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get settingsStorageLocal => 'Local storage'; - @override - String get settingsStorageNoCloudBackupTitle => 'No Cloud Backup'; - @override String get settingsStorageNoCloudBackupCard => 'Back up your memories to the cloud to free up local space and ensure you never lose your pictures. All end-to-end encrypted.'; @@ -2770,9 +2715,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get backupCloudContactsMessages => 'Contacts & Messages'; - @override - String get backupCloudFreeActive => 'Free Active'; - @override String get backupCloudImagesMedia => 'Images & Media'; @@ -2862,10 +2804,7 @@ class AppLocalizationsEn extends AppLocalizations { String get webxdcStoreAlreadyAdded => 'Already added to this chat'; @override - String get webxdcChatAppsMenu => 'Chat apps'; - - @override - String get webxdcChatAppsTitle => 'Chat apps'; + String get webxdcChatApps => 'Chat apps'; @override String get webxdcChatAppsEmpty => diff --git a/lib/src/localization/translations/ar.arb b/lib/src/localization/translations/ar.arb new file mode 100644 index 00000000..e8b2278a --- /dev/null +++ b/lib/src/localization/translations/ar.arb @@ -0,0 +1,796 @@ +{ + "subscriptionAllApps": "الوصول إلى جميع التطبيقات", + "@@locale": "ar", + "registerSlogan": "ابقَ على تواصل بخصوصية.", + "onboardingWelcomeTitle": "مرحبًا بك في twonly!", + "onboardingWelcomeBody": "اكتشف طريقة خاصة وآمنة للبقاء على تواصل مع أصدقائك من خلال مشاركة الصور الفورية.", + "onboardingE2eTitle": "مشاركة بلا قلق", + "onboardingE2eBody": "بفضل التشفير التام بين الطرفين، اطمئن إلى أن اللحظات التي تشاركها لا يراها أحد سواك وسوى أصدقائك.", + "onboardingFocusTitle": "ركّز على مشاركة اللحظات", + "onboardingFocusBody": "وداعًا للميزات التي تسبب الإدمان! صُمّم twonly لمشاركة اللحظات، بعيدًا عن المشتتات غير المفيدة والإعلانات.", + "onboardingNotProductTitle": "أنت لست المنتَج!", + "onboardingNotProductBody": "يُموَّل twonly من خلال اشتراك اختياري. لن تُباع بياناتك أبدًا.", + "registerUsernameSlogan": "أنشئ حسابك", + "registerUsernameDecoration": "اسم المستخدم", + "registerUsernameLimits": "3 أحرف على الأقل.", + "registerProofOfWorkFailed": "حدثت مشكلة في اختبار التحقق. يُرجى المحاولة مرة أخرى.", + "registerSubmitButton": "سجّل الآن!", + "chatsTapToSend": "اضغط لإرسال صورتك الأولى", + "cameraPreviewSendTo": "إرسال إلى", + "cameraPermissionsBody": "يحتاج twonly إلى الوصول إلى الكاميرا والميكروفون.", + "cameraPermissionsRequestButton": "طلب الأذونات", + "shareImageTitle": "مشاركة مع", + "shareImageBestFriends": "أفضل الأصدقاء", + "shareImagePinnedContacts": "المثبّتة", + "shareImagedEditorSendImage": "إرسال", + "shareImagedEditorShareWith": "مشاركة مع", + "shareImagedEditorSaveImage": "حفظ", + "shareImagedEditorSavedImage": "تم الحفظ", + "shareImageSearchAllContacts": "البحث في جميع جهات الاتصال", + "startNewChatSearchHint": "الاسم أو اسم المستخدم أو اسم المجموعة", + "shareImagedSelectAll": "تحديد الكل", + "startNewChatTitle": "اختر جهة اتصال", + "startNewChatNewContact": "جهة اتصال جديدة", + "shareImageAllUsers": "جميع جهات الاتصال", + "shareImageShowArchived": "إظهار المستخدمين المؤرشفين", + "shareImageSendToWidget": "إرسال إلى أداة الشاشة الرئيسية", + "shareImageWidgetExplainerTitle": "إرسال إلى أداة", + "shareImageWidgetExplainerBody": "يُرسل الصورة مباشرةً إلى أدوات الشاشة الرئيسية لدى أصدقائك. لا يمكن اختيار سوى الأصدقاء الذين يسمحون بذلك.", + "shareImageWidgetExplainerDismiss": "فهمت", + "searchUsernameInput": "اسم المستخدم", + "addFriendTitle": "إضافة أصدقاء", + "searchUserNamePending": "الطلب قيد الانتظار", + "searchUsernameNotFound": "اسم المستخدم غير موجود", + "searchUsernameNewFollowerTitle": "الطلبات المفتوحة", + "chatListDetailInput": "اكتب رسالة", + "userDeletedAccount": "تم حذف حساب هذا المستخدم.", + "contextMenuUserProfile": "الملف الشخصي", + "contextMenuArchiveUser": "أرشفة", + "contextMenuUndoArchiveUser": "إلغاء الأرشفة", + "contextMenuOpenChat": "فتح المحادثة", + "contextMenuPin": "تثبيت", + "contextMenuUnpin": "إلغاء التثبيت", + "contextMenuViewAgain": "عرض مرة أخرى", + "mediaViewerAuthReason": "يُرجى المصادقة لعرض رسالة twonly هذه!", + "mediaViewerTwonlyTapToOpen": "اضغط لفتح رسالة twonly!", + "messageSendState_Received": "تم الاستلام", + "messageSendState_Opened": "تم الفتح", + "messageSendState_Send": "تم الإرسال", + "messageSendState_Delivered": "تم الاستلام", + "messageSendState_Sending": "جارٍ الإرسال", + "messageSendState_TapToLoad": "اضغط للتحميل", + "messageSendState_Loading": "جارٍ التنزيل", + "messageStoredInGallery": "حُفظت في المعرض", + "messageReopened": "أُعيد فتحها", + "imageEditorDrawOk": "اعتماد الرسم", + "settingsTitle": "الإعدادات", + "settingsChats": "المحادثات", + "settingsPreSelectedReactions": "رموز التفاعل المحددة مسبقًا", + "settingsAutomaticallyMarkEqualMediaFilesAsOpenedTitle": "تمييز المكررات كمفتوحة", + "settingsAutomaticallyMarkEqualMediaFilesAsOpenedSubtitle": "إذا استلمت الوسائط نفسها في عدة محادثات، فإن فتحها في إحداها يميّزها كمفتوحة في البقية.", + "settingsPreSelectedReactionsError": "يمكن اختيار 12 تفاعلًا كحد أقصى.", + "settingsProfile": "الملف الشخصي", + "settingsStorageData": "الذكريات والتخزين", + "memoryLocationTitle": "حفظ الموقع في الذكريات", + "memoryLocationSubtitle": "الموقع للذكريات وللتصدير إلى المعرض. لا يُرسل أبدًا إلى جهات الاتصال.", + "memoryLocationPermissionDenied": "لم يُمنح إذن الوصول إلى الموقع الدقيق. سيبقى هذا الإعداد معطّلًا.", + "settingsStorageDataStoreInGTitle": "الحفظ في المعرض", + "settingsStorageDataMediaAutoDownload": "التنزيل التلقائي للوسائط", + "settingsStorageDataAutoDownMobile": "عند استخدام بيانات الجوال", + "settingsStorageDataAutoDownWifi": "عند استخدام Wi-Fi", + "settingsStorageManageTitle": "إدارة التخزين", + "settingsStorageUsed": "المساحة المستخدمة", + "settingsStorageImages": "الصور", + "settingsStorageVideos": "مقاطع الفيديو", + "settingsStorageGifs": "صور GIF", + "settingsProfileCustomizeAvatar": "خصّص صورتك الرمزية", + "settingsProfileEditDisplayName": "الاسم المعروض", + "settingsProfileEditDisplayNameNew": "اسم معروض جديد", + "settingsAccount": "الحساب", + "settingsSubscription": "الاشتراك", + "settingsAppearance": "المظهر", + "settingsPrivacy": "الخصوصية والأمان", + "settingsPrivacyBlockUsers": "حظر جهات الاتصال", + "settingsPrivacyBlockUsersDesc": "لن تتمكن جهات الاتصال المحظورة من التواصل معك. يمكنك إلغاء حظر أي جهة اتصال في أي وقت.", + "settingsPrivacyBlockUsersCount": "جهات الاتصال: {len}", + "settingsNotification": "الإشعارات", + "settingsNotifyPermission": "أذونات الإشعارات", + "settingsNotifyPermissionDesc": "افتح إعدادات النظام للسماح بالإشعارات الفورية.", + "settingsNotifyResetTitle": "إعادة تعيين رموز الإشعارات", + "settingsNotifyResetTitleSubtitle": "أعد تعيين رموز الإشعارات إذا واجهت مشكلات في استلام الإشعارات الفورية.", + "settingsNotifyResetTitleReset": "تمت إعادة تعيين رموز الإشعارات الخاصة بك.", + "settingsNotifyResetTitleResetDesc": "إذا استمرت المشكلة، يُرجى إرسال سجل تصحيح الأخطاء إلينا عبر الإعدادات > المساعدة حتى نتمكن من فحص المشكلة.", + "settingsHelp": "المساعدة", + "settingsHelpDiagnostics": "سجل التشخيص", + "settingsHelpFAQ": "الأسئلة الشائعة", + "settingsHelpContactUs": "اتصل بنا", + "settingsHelpVersion": "الإصدار", + "settingsHelpLicenses": "التراخيص (الشيفرة المصدرية)", + "settingsHelpCredits": "التراخيص (الصور)", + "settingsHelpImprint": "بيانات الناشر وسياسة الخصوصية", + "contactUsFaq": "هل قرأت الأسئلة الشائعة؟", + "contactUsEmojis": "كيف تشعر؟ (اختياري)", + "contactUsSelectOption": "يُرجى اختيار أحد الخيارات", + "contactUsReason": "أخبرنا عن سبب تواصلك معنا", + "contactUsMessage": "إذا كنت ترغب في تلقي رد، يُرجى إضافة عنوان بريدك الإلكتروني حتى نتمكن من التواصل معك.", + "contactUsYourMessage": "رسالتك", + "contactUsMessageTitle": "أخبرنا بما يحدث", + "contactUsReasonNotWorking": "شيء ما لا يعمل", + "contactUsReasonFeatureRequest": "طلب ميزة", + "contactUsReasonQuestion": "سؤال", + "contactUsReasonFeedback": "ملاحظات", + "contactUsReasonOther": "أخرى", + "contactUsIncludeLog": "إرفاق سجل تصحيح الأخطاء", + "contactUsWhatsThat": "ما هذا؟", + "contactUsLastWarning": "هذه هي المعلومات التي سيتم إرسالها إلينا. يُرجى مراجعتها ثم الضغط على إرسال.", + "contactUsSuccess": "تم إرسال ملاحظاتك بنجاح!", + "hideNewsIcon": "إخفاء أيقونة الأخبار", + "settingsHelpTerms": "شروط الخدمة", + "settingsAppearanceTheme": "السمة", + "settingsAppearanceLanguage": "اللغة", + "settingsAccountDeleteAccount": "حذف الحساب", + "settingsAccountDeleteAccountNoBallance": "بمجرد حذف حسابك، لا يمكن التراجع عن ذلك.", + "settingsAccountDeleteModalTitle": "هل أنت متأكد؟", + "settingsAccountDeleteModalBody": "سيتم حذف حسابك، ولن تكون هناك أي إمكانية لاستعادته.", + "contactVerifyNumberTitle": "التحقق من جهات الاتصال", + "verifyUserIdentity": "التحقق من هوية {username}", + "contactVerifyNumberSubtitle": "تحقق من هوية جهات اتصالك للتأكد من أنك تراسل الشخص الصحيح.", + "inChatContactNotVerified": "جهة الاتصال غير موثّقة.", + "groupMembersNotVerified": "أعضاء غير موثّقين: {count}.", + "userVerifiedTitle": "تم التحقق من جهة الاتصال", + "scanUserQrCode": "امسح رمز QR الخاص بـ {username}", + "openOwnQrCode": "افتح رمز QR الخاص بك", + "contactVerifiedBy": "تم التحقق بواسطة {username}", + "contactSharedByUnknown": "تمت مشاركتها من جهة اتصال موثّقة (اسم المستخدم غير متاح)", + "verificationTypeQrScanned": "لقد مسحت رمز QR الخاص بجهة الاتصال.", + "verificationTypeSecretQrToken": "تم مسح رمز QR الخاص بك من قِبل {username}.", + "verificationTypeLink": "تم التحقق عبر رابط.", + "verificationTypeManual": "تمت مقارنة رقم الأمان يدويًا.", + "verificationTypeMigratedFromOldVersion": "تم النقل من إصدار قديم.", + "contactViewMessage": "رسالة", + "contactNickname": "الاسم المستعار", + "contactNicknameNew": "اسم مستعار جديد", + "contactBlock": "حظر", + "contactTroubleshoot": "استكشاف مشكلات الاتصال وإصلاحها", + "contactResetSessionTitle": "إعادة تعيين الاتصال مع {username}؟", + "contactResetSessionBody": "سيزيل twonly الاتصال المشفّر الحالي وينشئ اتصالًا جديدًا. قد تُفقد الرسائل التي لا تزال قيد الإرسال.", + "contactResetSessionConfirm": "إعادة تعيين الاتصال", + "contactResetSessionSuccess": "تمت إعادة إنشاء الاتصال المشفّر مع {username}.", + "contactResetSessionFailed": "تعذّرت إعادة تعيين الاتصال. تحقق من اتصالك بالإنترنت وحاول مرة أخرى.", + "contactBlockTitle": "حظر {username}", + "contactBlockBody": "لن تتمكن جهة الاتصال المحظورة من إرسال رسائل إليك بعد الآن، وسيتم إخفاء ملفها الشخصي. لإلغاء حظر جهة اتصال، انتقل إلى الإعدادات > الخصوصية > جهات الاتصال المحظورة.", + "contactRemove": "إزالة جهة الاتصال", + "contactRemoveTitle": "إزالة {username}", + "contactRemoveBody": "إزالة جهة الاتصال نهائيًا. إذا حاولت جهة الاتصال إرسال رسالة جديدة إليك، فسيتعيّن عليك قبولها مرة أخرى أولًا.", + "undo": "تراجع", + "redo": "إعادة", + "next": "التالي", + "finishSetup": "إكمال الإعداد", + "submit": "إرسال", + "close": "إغلاق", + "disable": "تعطيل", + "enable": "تفعيل", + "cancel": "إلغاء", + "now": "الآن", + "you": "أنت", + "minutesShort": "د", + "image": "صورة", + "video": "فيديو", + "react": "تفاعل", + "reply": "رد", + "copy": "نسخ", + "edit": "تعديل", + "delete": "حذف", + "info": "معلومات", + "ok": "حسنًا", + "switchFrontAndBackCamera": "التبديل بين الكاميرا الأمامية والخلفية.", + "addTextItem": "نص", + "protectAsARealTwonly": "إرسال كرسالة twonly حقيقية!", + "addDrawing": "رسم", + "addEmoji": "رمز تعبيري", + "toggleFlashLight": "تشغيل الفلاش أو إيقافه", + "userFound": "تم العثور على {username}", + "userFoundBody": "هل تريد التواصل مع {username}؟", + "errorInternalError": "الخادم غير متاح حاليًا. يُرجى المحاولة لاحقًا.", + "errorInvalidInvitationCode": "رمز الدعوة الذي أدخلته غير صالح. يُرجى التحقق من الرمز والمحاولة مرة أخرى.", + "errorUsernameAlreadyTaken": "اسم المستخدم مستخدم بالفعل.", + "errorUsernameNotValid": "اسم المستخدم الذي أدخلته لا يستوفي المعايير المطلوبة. يُرجى اختيار اسم مستخدم صالح.", + "errorNotEnoughCredit": "ليس لديك رصيد twonly كافٍ.", + "errorVoucherInvalid": "رمز القسيمة الذي أدخلته غير صالح.", + "errorPlanLimitReached": "لقد وصلت إلى حد خطتك. يُرجى ترقية خطتك.", + "errorPlanNotAllowed": "هذه الميزة غير متاحة في خطتك الحالية.", + "errorPlanUpgradeNotYearly": "يجب دفع قيمة ترقية الخطة سنويًا، لأن الخطة الحالية تُدفع سنويًا أيضًا.", + "upgradeToPaidPlanButton": "الترقية إلى {planId}{sufix}", + "partOfPaidPlanFrom": "ضمن الخطة المدفوعة لـ", + "subscriptionCurrentPlanBadge": "الحالية", + "year": "سنة", + "yearly": "سنويًا", + "month": "شهر", + "monthly": "شهريًا", + "proFeature1": "رفع غير محدود لملفات الوسائط", + "proFeature2": "مستخدم Plus إضافي واحد", + "proFeature3": "مساحة 25 GB لتخزين الذكريات", + "proFeature4": "استعادة الشعلات", + "familyFeature1": "رفع غير محدود لملفات الوسائط", + "familyFeature2": "4 مستخدمين Plus إضافيين", + "familyFeature3": "مساحة 50 GB لتخزين الذكريات", + "familyFeature4": "استعادة الشعلات", + "freeFeature1": "رفع 10 ملفات وسائط يوميًا", + "plusFeature1": "رفع غير محدود لملفات الوسائط", + "plusFeature2": "ميزات إضافية (قريبًا)", + "manageAdditionalUsers": "إدارة المستخدمين الإضافيين", + "open": "فتح", + "buy": "شراء", + "autoRenewal": "التجديد التلقائي", + "additionalUsersList": "مستخدموك الإضافيون", + "galleryExportSuccess": "تم الحفظ في المعرض بنجاح.", + "galleryCancel": "إلغاء", + "memoriesEmpty": "بمجرد حفظ الصور أو مقاطع الفيديو، ستظهر هنا في ذكرياتك.", + "deleteTitle": "هل أنت متأكد؟", + "deleteOkBtnForAll": "حذف لدى الجميع", + "memoriesDeleteSnackbarSuccess": "{count, plural, =1{تم حذف عنصر واحد بنجاح} =2{تم حذف عنصرين بنجاح} few{تم حذف {count} عناصر بنجاح} many{تم حذف {count} عنصرًا بنجاح} other{تم حذف {count} عنصر بنجاح}}", + "deleteOkBtnForMe": "حذف لديّ", + "deleteOnlyForMe": "حذف لديّ فقط", + "deleteImageTitle": "هل أنت متأكد؟", + "deleteMemoriesBody": "{count, plural, =1{سيتم حذف الصورة نهائيًا.} =2{سيتم حذف الصورتين نهائيًا.} few{سيتم حذف {count} صور نهائيًا.} many{سيتم حذف {count} صورة نهائيًا.} other{سيتم حذف {count} صورة نهائيًا.}}", + "deleteMemoriesLocalOnly": "محليًا فقط", + "deleteMemoriesCompletely": "بالكامل", + "memoriesSelectedCount": "{count, plural, =1{عنصر واحد} =2{عنصران} few{{count} عناصر} many{{count} عنصرًا} other{{count} عنصر}}", + "memoriesMenuSelectAll": "تحديد الكل", + "memoriesMenuDeselectAll": "إلغاء تحديد الكل", + "memoriesMenuExport": "تصدير إلى المعرض", + "memoriesMenuFavorite": "إضافة إلى المفضلة", + "memoriesMenuDelete": "حذف بالكامل", + "memoriesMenuDeleteLocal": "حذف محليًا", + "settingsBackup": "النسخ الاحتياطي", + "backupNoPasswordRecovery": "بسبب نظام الأمان في twonly، لا توجد (حاليًا) وظيفة لاستعادة كلمة المرور. لذلك يجب عليك تذكّر كلمة مرورك، أو الأفضل من ذلك أن تدوّنها.", + "backupInsecurePassword": "كلمة مرور غير آمنة", + "backupInsecurePasswordDesc": "كلمة المرور المختارة غير آمنة إطلاقًا، ويمكن للمهاجمين تخمينها بسهولة. يُرجى اختيار كلمة مرور آمنة.", + "backupInsecurePasswordOk": "المتابعة على أي حال", + "backupInsecurePasswordCancel": "حاول مرة أخرى", + "backupTwonlySafeLongDesc": "لا يستخدم twonly أي حسابات مستخدمين مركزية. عند التثبيت يتم إنشاء زوج مفاتيح يتكوّن من مفتاح عام ومفتاح خاص. يُخزَّن المفتاح الخاص على جهازك فقط لحمايته من الوصول غير المصرّح به. أما المفتاح العام فيُرفع إلى الخادم ويُربط باسم المستخدم الذي اخترته حتى يتمكن الآخرون من العثور عليك.\n\nيُنشئ النسخ الاحتياطي في twonly بانتظام نسخة احتياطية مشفّرة ومجهولة الهوية من مفتاحك الخاص مع جهات اتصالك وإعداداتك. يكفي اسم المستخدم وكلمة المرور التي اخترتها لاستعادة هذه البيانات على جهاز آخر.", + "backupSelectStrongPassword": "اختر كلمة مرور آمنة. يُنصح بإنشائها وحفظها باستخدام مدير كلمات المرور الذي تفضّله.", + "password": "كلمة المرور", + "passwordRepeated": "أعد إدخال كلمة المرور", + "passwordRepeatedNotEqual": "كلمتا المرور غير متطابقتين.", + "backupPasswordRequirement": "يجب أن تتكوّن كلمة المرور من 10 أحرف على الأقل.", + "backupEnableBackup": "تفعيل النسخ الاحتياطي بكلمة مرور", + "backupTwonlySaveNow": "احفظ الآن", + "backupChangePassword": "تغيير كلمة المرور", + "backupChangePasswordAuthReason": "تغيير كلمة مرور النسخ الاحتياطي", + "backupChangePasswordAuthFailed": "لا يمكنك تغيير كلمة المرور إلا بعد المصادقة!", + "backupFreeSpaceWithCloud": "وفّر المساحة بالنسخ الاحتياطي السحابي", + "backupMemoriesNotEnabled": "غير مفعّل", + "backupMemoriesUpgradeRequired": "يتطلب ترقية", + "todayAt": "اليوم الساعة {time}", + "yesterdayAt": "أمس الساعة {time}", + "twonlySafeRecoverTitle": "استعادة النسخة الاحتياطية", + "twonlySafeRecoverBtn": "استعادة النسخة الاحتياطية", + "inviteFriends": "ادعُ أصدقاءك", + "inviteFriendsShareBtn": "مشاركة", + "inviteFriendsShareText": "لننتقل إلى twonly: {url}", + "appOutdated": "إصدار twonly لديك قديم.", + "appOutdatedBtn": "حدّث الآن", + "doubleClickToReopen": "انقر مرتين\nلفتحها مجددًا", + "uploadLimitReached": "تم الوصول إلى حد الرفع.\nقم بالترقية إلى Pro\nأو انتظر حتى الغد.", + "fileLimitReached": "تم تجاوز الحد الأقصى\nلحجم الملف", + "retransmissionRequested": "تم طلب إعادة الإرسال", + "reportUserTitle": "الإبلاغ عن {username}", + "reportUserReason": "سبب الإبلاغ", + "reportUser": "الإبلاغ عن جهة الاتصال", + "newDeviceRegistered": "لقد سجّلت الدخول على جهاز آخر، لذلك تم تسجيل خروجك هنا.", + "tabToRemoveEmoji": "اضغط للإزالة", + "quotedMessageWasDeleted": "تم حذف الرسالة المقتبسة.", + "messageWasDeleted": "تم حذف الرسالة.", + "messageWasDeletedShort": "محذوفة", + "sent": "تم التسليم", + "sentTo": "تم التسليم إلى", + "received": "تم الاستلام", + "opened": "تم الفتح", + "waitingForInternet": "في انتظار الاتصال بالإنترنت", + "editHistory": "سجل التعديلات", + "fileSize": "حجم الملف", + "archivedChats": "المحادثات المؤرشفة", + "durationShortSecond": "ث", + "durationShortMinute": "د", + "durationShortHour": "س", + "durationShortDays": "{count, plural, =1{يوم واحد} =2{يومان} few{{count} أيام} many{{count} يومًا} other{{count} يوم}}", + "contacts": "جهات الاتصال", + "groups": "المجموعات", + "newGroup": "مجموعة جديدة", + "selectMembers": "اختر الأعضاء", + "selectGroupName": "اختر اسم المجموعة", + "groupNameInput": "اسم المجموعة", + "groupMembers": "الأعضاء", + "addMember": "إضافة عضو", + "createGroup": "إنشاء مجموعة", + "leaveGroup": "مغادرة المجموعة", + "createContactRequest": "إنشاء طلب تواصل", + "contactRequestSend": "تم إرسال طلب التواصل", + "makeAdmin": "تعيين كمشرف", + "removeAdmin": "إزالة من الإشراف", + "removeFromGroup": "إزالة من المجموعة", + "admin": "مشرف", + "revokeAdminRightsTitle": "سحب صلاحيات الإشراف من {username}؟", + "revokeAdminRightsOkBtn": "إزالة من الإشراف", + "makeAdminRightsTitle": "تعيين {username} مشرفًا؟", + "makeAdminRightsBody": "سيتمكن {username} من تعديل هذه المجموعة وأعضائها.", + "makeAdminRightsOkBtn": "تعيين كمشرف", + "updateGroup": "تحديث المجموعة", + "alreadyInGroup": "موجود في المجموعة بالفعل", + "contactNotVerified": "غير موثّق", + "removeContactFromGroupTitle": "إزالة {username} من هذه المجموعة؟", + "youChangedGroupName": "لقد غيّرت اسم المجموعة إلى «{newGroupName}».", + "makerChangedGroupName": "غيّر {maker} اسم المجموعة إلى «{newGroupName}».", + "youCreatedGroup": "لقد أنشأت المجموعة.", + "makerCreatedGroup": "أنشأ {maker} المجموعة.", + "youRemovedMember": "لقد أزلت {affected} من المجموعة.", + "makerRemovedMember": "أزال {maker} {affected} من المجموعة.", + "youAddedMember": "لقد أضفت {affected} إلى المجموعة.", + "makerAddedMember": "أضاف {maker} {affected} إلى المجموعة.", + "youMadeAdmin": "لقد منحت {affected} صلاحيات الإشراف.", + "makerMadeAdmin": "منح {maker} {affected} صلاحيات الإشراف.", + "youRevokedAdminRights": "لقد سحبت صلاحيات الإشراف من {affectedR}.", + "makerRevokedAdminRights": "سحب {maker} صلاحيات الإشراف من {affectedR}.", + "youLeftGroup": "لقد غادرت المجموعة.", + "makerLeftGroup": "غادر {maker} المجموعة.", + "groupActionYou": "حسابك", + "groupActionYour": "حسابك", + "notificationFillerIn": "في", + "notificationText": "رسالة جديدة{inGroup}.", + "notificationTwonly": "رسالة twonly جديدة{inGroup}.", + "notificationVideo": "فيديو جديد{inGroup}.", + "notificationImage": "صورة جديدة{inGroup}.", + "notificationAudio": "رسالة صوتية جديدة{inGroup}.", + "notificationAddedToGroup": "تمت إضافتك إلى «{groupname}»", + "notificationContactRequest": "طلب جديد للتواصل معك.", + "notificationAcceptRequest": "أنتما متصلان الآن.", + "notificationStoredMediaFile": "تم حفظ صورتك.", + "notificationReaction": "تفاعل جديد مع صورتك.", + "notificationReopenedMedia": "تمت إعادة فتح صورتك.", + "notificationReactionToVideo": "تفاعل {reaction} مع الفيديو الخاص بك.", + "notificationReactionToText": "تفاعل {reaction} مع رسالتك.", + "notificationReactionToImage": "تفاعل {reaction} مع صورتك.", + "notificationReactionToAudio": "تفاعل {reaction} مع رسالتك الصوتية.", + "notificationResponse": "رد جديد{inGroup}.", + "notificationCategoryMessageTitle": "الرسائل", + "notificationConnectionFallback": "قد تكون لديك رسائل جديدة.", + "groupContextMenuDeleteGroup": "سيؤدي هذا إلى حذف جميع الرسائل في هذه المحادثة نهائيًا.", + "groupYouAreNowLongerAMember": "لم تعد عضوًا في هذه المجموعة.", + "groupNetworkIssue": "مشكلة في الشبكة. حاول مرة أخرى لاحقًا.", + "leaveGroupSelectOtherAdminTitle": "اختر مشرفًا آخر", + "leaveGroupSelectOtherAdminBody": "لمغادرة المجموعة، يجب عليك أولًا اختيار مشرف جديد.", + "leaveGroupSureTitle": "مغادرة المجموعة", + "leaveGroupSureBody": "هل تريد حقًا مغادرة المجموعة؟", + "leaveGroupSureOkBtn": "مغادرة المجموعة", + "changeDisplayMaxTime": "سيتم الآن حذف المحادثات بعد {time} ({username}).", + "youChangedDisplayMaxTime": "سيتم الآن حذف المحادثات بعد {time}.", + "userGotReported": "تم الإبلاغ عن المستخدم.", + "deleteChatAfter": "حذف المحادثة بعد...", + "deleteChatAfterAnHour": "ساعة واحدة.", + "deleteChatAfterADay": "يوم واحد.", + "deleteChatAfterAWeek": "أسبوع واحد.", + "deleteChatAfterAMonth": "شهر واحد.", + "deleteChatAfterAYear": "سنة واحدة.", + "yourTwonlyScore": "نقاط twonly الخاصة بك", + "registrationClosed": "نظرًا للعدد الكبير من التسجيلات حاليًا، أوقفنا التسجيل مؤقتًا لضمان استمرار موثوقية الخدمة. يُرجى المحاولة مرة أخرى بعد بضعة أيام.", + "dialogAskDeleteMediaFilePopTitle": "هل أنت متأكد أنك تريد حذف تحفتك الفنية؟", + "dialogAskDeleteMediaFilePopDelete": "حذف", + "allowErrorTracking": "شارك الأخطاء والأعطال معنا", + "allowErrorTrackingSubtitle": "إذا تعطّل twonly أو حدثت أخطاء، يتم الإبلاغ عنها تلقائيًا إلى خادم Glitchtip الذي نستضيفه بأنفسنا. لا يتم رفع البيانات الشخصية مثل الرسائل أو الصور أبدًا.", + "avatarSaveChanges": "هل تريد حفظ التغييرات؟", + "avatarSaveChangesStore": "حفظ", + "avatarSaveChangesDiscard": "تجاهل", + "inProcess": "قيد المعالجة", + "draftMessage": "مسودة", + "voiceMessageSlideToCancel": "اسحب للإلغاء", + "voiceMessageCancel": "إلغاء", + "shareYourProfile": "شارك ملفك الشخصي", + "scanOtherProfile": "امسح ملفًا شخصيًا آخر", + "openYourOwnQRcode": "افتح رمز QR الخاص بك", + "addContactQrSheetSubtext": "اطلب من صديقك مسح رمز QR هذا لإضافتك", + "letUserScanQrCode": "اطلب من {username} مسح رمز QR هذا", + "finishSetupCardTitle": "أكمل ملفك الشخصي", + "finishSetupCardDesc": "أوشكت على الانتهاء! أكمل إعداد حسابك للاستفادة القصوى من twonly.", + "finishSetupCardAction": "متابعة الإعداد", + "missingBackupCardTitle": "إعداد النسخ الاحتياطي", + "missingBackupCardDesc": "لقد حسّنّا آلية النسخ الاحتياطي، ولهذا يلزمك إعدادها من جديد.", + "missingBackupCardAction": "أعدّه الآن", + "onboardingFinishLater": "الإكمال لاحقًا", + "onboardingProfileTitle": "اختر مظهرك", + "onboardingProfileBody": "اختر صورة رمزية واسمًا معروضًا سيراه أصدقاؤك.", + "onboardingBackupBody": "احتفظ بنسخة احتياطية من هويتك في twonly، فهذه هي الطريقة الوحيدة لاستعادة حسابك إذا ألغيت تثبيت التطبيق أو فقدت هاتفك.", + "onboardingUserDiscoveryShareFriends": "شارك أصدقاءك", + "onboardingUserDiscoveryShareFriendsDesc": "شارك مع أصدقائك من تعرفهم ومن تحققت منهم. لا يمكن للأصدقاء *رؤية سوى الأصدقاء المشتركين* من قائمة أصدقائك. يمكنك تغيير رأيك *في أي وقت* أو *إخفاء أشخاص محددين*.", + "onboardingUserDiscoveryContactsVerifiedBadge": "تحصل جهات الاتصال التي تحقق منها أصدقاؤك على شارة", + "onboardingUserDiscoveryWhoIsRequesting": "اعرف من يرسل الطلب", + "userDiscoverySettingsManualApproval": "اسألني في كل مرة قبل المشاركة", + "userDiscoverySettingsManualApprovalDesc": "سيُطلب منك التأكيد في كل مرة قبل مشاركة أحد أصدقائك.", + "onboardingUserDiscoveryLetFriendsFindYou": "دع أصدقاءك يعثرون عليك", + "onboardingUserDiscoveryBeRecommended": "اظهر في اقتراحات الآخرين", + "onboardingUserDiscoveryWhatOthersSee": "ما سيراه الآخرون", + "onboardingUserDiscoveryWhatYouSee": "هذا ما ستراه عند الطلب", + "onboardingAddContactsTitle": "أضف جهات اتصال جديدة", + "onboardingAddContactsAcceptDesc": "في twonly، يجب قبول كل جهة اتصال أولًا قبل أن تتمكن من التواصل.", + "onboardingAddContactsMethodHeading": "إضافة جهات اتصال", + "onboardingAddContactsMethodScan": "امسح رمز QR الخاص بجهة الاتصال.", + "onboardingAddContactsMethodSearch": "ابحث عن اسم المستخدم.", + "onboardingAddContactsMethodShare": "شارك جهة اتصال في المحادثات.", + "linkFromUsername": "هل الرابط من {username}؟", + "linkFromUsernameLong": "إذا تلقيت الرابط من صديقك، يمكنك تمييز المستخدم كموثّق، لأن المفتاح العام في الرابط يطابق المفتاح العام المحفوظ مسبقًا لهذا المستخدم.", + "gotLinkFromFriend": "نعم، حصلت على الرابط من صديقي!", + "couldNotVerifyUsername": "تعذّر التحقق من {username}", + "linkPubkeyDoesNotMatch": "المفتاح العام في الرابط لا يطابق المفتاح العام المحفوظ لجهة الاتصال هذه. حاول مقابلة صديقك شخصيًا ومسح رمز QR مباشرةً!", + "verificationMissingSession": "لا توجد جلسة آمنة محفوظة لجهة الاتصال هذه بعد. تبادلا رسالة أولًا، ثم اطلب من الشخص مشاركة رابط تحقق جديد.", + "verificationLinkInvalidTitle": "رابط تحقق غير صالح", + "verificationLinkInvalid": "رابط التحقق غير مكتمل أو غير صالح. اطلب من الشخص مشاركته مرة أخرى من ملفه الشخصي في twonly.", + "verificationLinkErrorTitle": "التحقق غير متاح", + "verificationLinkError": "تعذّر على twonly فحص رابط التحقق هذا. يُرجى المحاولة مرة أخرى.", + "verificationOwnProfileTitle": "هذا ملفك الشخصي", + "verificationOwnProfile": "لقد مسحت رمز التحقق الخاص بك.", + "startWithCameraOpen": "البدء والكاميرا مفتوحة", + "showImagePreviewWhenSending": "عرض معاينة الصورة عند اختيار المستلمين", + "verifiedPublicKey": "تم التحقق من هوية {username} بنجاح.", + "memoriesAYearAgo": "قبل عام", + "memoriesXYearsAgo": "قبل {years} سنوات", + "migrationOfMemories": "نقل ملفات الوسائط: {open} بانتظار المعالجة.", + "autoStoreAllSendUnlimitedMediaFiles": "حفظ جميع الوسائط المرسلة", + "autoStoreAllSendUnlimitedMediaFilesSubtitle": "إذا فعّلت هذا الخيار، فسيتم حفظ جميع الصور التي ترسلها، طالما أُرسلت بعدّ تنازلي غير محدود وليس في وضع twonly.", + "termsOfService": "شروط الخدمة", + "privacyPolicy": "سياسة الخصوصية", + "additionalUserAddError": "تعذّرت إضافة {username}، يُرجى المحاولة لاحقًا.", + "additionalUserAddErrorNotInFreePlan": "تعذّرت إضافة {username} لأن الحساب مشترك بالفعل في خطة مدفوعة.", + "additionalUserAddButton": "إضافة مستخدم إضافي ({used}/{limit})", + "additionalUserRemoveTitle": "إزالة هذا المستخدم الإضافي", + "additionalUserRemoveDesc": "بعد الإزالة، سيُنقل المستخدم الإضافي تلقائيًا إلى الخطة المجانية، ويمكنك إضافة شخص آخر.", + "additionalUserSelectTitle": "اختر مستخدمين إضافيين", + "additionalUserSelectButton": "اختر المستخدمين ({used}/{limit})", + "storeAsDefault": "حفظ كافتراضي", + "deleteUserErrorMessage": "لا يمكنك حذف جهة الاتصال إلا بعد حذف المحادثة المباشرة، وعندما لا تعود جهة الاتصال عضوًا في أي مجموعة.", + "groupSizeLimitError": "الحد الأقصى لعدد أعضاء المجموعة حاليًا: {size}!", + "authRequestReopenImage": "يجب عليك المصادقة لإعادة فتح الصورة.", + "shareContactsMenu": "جهة اتصال", + "shareContactsTitle": "اختر جهات الاتصال", + "shareContactsSubmit": "شارك الآن", + "updateTwonlyMessage": "لرؤية هذه الرسالة، تحتاج إلى تحديث twonly.", + "verificationBadgeNote": "يمكنك التحقق من أصدقائك عن طريق مسح رمز QR العام الخاص بهم. اضغط لمعرفة المزيد.", + "verificationBadgeTitle": "التحقق", + "verificationBadgeGeneralDesc": "تمنحك الشارة راحة البال بأنك تراسل *الشخص الصحيح* وأن *رسائلك تبقى سرية*.", + "verificationBadgeGreenDesc": "جهة اتصال *تحققت منها بنفسك* باستخدام رمز QR.", + "verificationBadgeYellowDesc": "جهة اتصال تحقق منها واحد على الأقل من *جهات اتصالك*.", + "verificationBadgeRedDesc": "جهة اتصال *لم* يتم التحقق من هويتها بعد.", + "manualVerificationOpen": "أرقام الأمان", + "manualVerificationTitle": "مقارنة رقم الأمان", + "manualVerificationMarkVerified": "تمييز كموثّق", + "manualVerificationDescription": "قارن رقم الأمان هذا مع {username}. إذا عرض الجهازان الرقم نفسه، فهذا يعني أنه تم التحقق من تشفيرك التام بين الطرفين.", + "manualVerificationMissingKeyTitle": "لا يتوفر رقم أمان", + "manualVerificationMissingKeyDescription": "لا توجد جلسة Signal مع {username} بعد. تبادلا رسالة أولًا، ثم حاول مرة أخرى.", + "manualVerificationLoadFailed": "تعذّر تحميل رقم الأمان.", + "manualVerificationRetry": "حاول مرة أخرى", + "manualVerificationSuccess": "لقد قارنت رقم الأمان مع {username}، وتم تأكيد الهوية.", + "scanNow": "امسح الآن", + "qrScannerVerifyHint": "للتحقق، يجب على الشخص الآخر فتح رمز QR الخاص به (قائمة المحادثات > زر رمز QR في الأسفل)", + "qrScannerVerifyUserHint": "للتحقق، يجب على {username} فتح رمز QR (قائمة المحادثات > زر رمز QR في الأسفل)", + "openQrCode": "فتح رمز QR", + "letFriendScanQrToVerify": "اطلب من صديقك مسح رمز QR هذا للتحقق منك", + "deleteVerificationTitle": "حذف التحقق؟", + "deleteVerificationBody": "هل أنت متأكد أنك تريد حذف هذا التحقق؟", + "secretQrTokenVerifiedSnackbar": "تم مسح رمز QR الخاص بك من قِبل {username}. أصبحت جهة الاتصال موثّقة الآن.", + "askForFriendPromotionsPrompt": "ساعد {username} في العثور على وجوه مألوفة على twonly من خلال مشاركة الأصدقاء المشتركين.", + "mutualGroupsTitle": "{count, plural, =1{مجموعة مشتركة واحدة} =2{مجموعتان مشتركتان} few{{count} مجموعات مشتركة} many{{count} مجموعة مشتركة} other{{count} مجموعة مشتركة}}", + "chatEntryFlameRestored": "تمت استعادة الشعلات: {count}", + "requestedUserToastText": "تم إرسال طلب إلى {username} بنجاح.", + "profileYourQrCode": "رمز QR الخاص بك", + "settingsScreenLock": "قفل الشاشة", + "settingsScreenLockSubtitle": "لفتح twonly، ستحتاج إلى استخدام ميزة فتح القفل في هاتفك.", + "settingsScreenLockAuthMessageEnable": "استخدام قفل الشاشة في twonly.", + "settingsScreenLockAuthMessageDisable": "تعطيل قفل الشاشة في twonly.", + "unlockTwonly": "فتح قفل twonly", + "unlockTwonlyTryAgain": "حاول مرة أخرى", + "unlockTwonlyDesc": "استخدم إعدادات فتح القفل في هاتفك لفتح twonly", + "settingsTypingIndication": "مؤشرات الكتابة", + "settingsTypingIndicationSubtitle": "عند إيقاف مؤشر الكتابة، لن تتمكن من رؤية متى يكتب الآخرون رسالة.", + "contactActionBlock": "حظر", + "contactActionAccept": "قبول", + "userDiscoverySettingsMutualFriends": "اختر عدد الأصدقاء المشتركين الذين يجب أن يكونوا لدى الشخص حتى يتم اقتراحك عليه.", + "userDiscoverySettingsApply": "تطبيق التغييرات", + "userDiscoveryEnabledDisableWarning": "إذا عطّلت ميزة «الأصدقاء المشتركون»، فلن ترى الاقتراحات بعد الآن. كما ستتوقف عن مشاركة أصدقائك مع جهات الاتصال الجديدة.", + "userDiscoveryEnabledChangeSettings": "تغيير الإعدادات", + "userDiscoveryEnabledFaq": "نشرح في الأسئلة الشائعة كيف تعمل ميزة «الأصدقاء المشتركون».", + "userDiscoveryDisabledIntro": "لا يجمع twonly رقم هاتفك *ولا* يحتاج إلى الوصول إلى جهات اتصالك. بدلًا من ذلك، يمكن لـ twonly *العثور على أصدقائك من خلال الأصدقاء المشتركين*.", + "userDiscoverySettingsTitle": "الأصدقاء المشتركون", + "userDiscoveryFeatureOffers": "مزاياك في لمحة", + "userDiscoveryDisabledLearnMore": "اعرف المزيد", + "userDiscoveryEnabledDialogTitle": "هل تريد التعطيل حقًا؟", + "userDiscoveryEnabledFriendsShared": "الأصدقاء الذين تشاركهم", + "userDiscoveryEnabledFriendsSharedDesc": "أنت تشارك فقط الأصدقاء الذين فعّلوا هذه الميزة أيضًا ووصلوا إلى الحد الذي حددته.", + "userDiscoverySettingsCurrentlyDisabled": "ميزة «الأصدقاء المشتركون» معطّلة حاليًا.", + "userDiscoveryEnabledNoFriendsShared": "أنت لا تشارك أحدًا بعد.", + "userDiscoveryActionDisable": "تعطيل", + "friendSuggestionsTitle": "اقتراحات الأصدقاء", + "andWord": "و", + "friendSuggestionsFriendsWith": "أصدقاء مشتركون: {friends}.", + "friendSuggestionsGroupMemberIn": " مجموعات مشتركة: {groups}.", + "friendSuggestionsRequest": "إرسال طلب", + "friendSuggestionsAskFriend": "اسأل أصدقاءك", + "askFriendsDialogTitle": "اسأل عن {username}", + "askFriendsDialogDescription": "اختر الأصدقاء الذين تريد سؤالهم عن هذا المستخدم:", + "askFriendsDialogConfirm": "اسأل", + "askFriendsDialogCancel": "إلغاء", + "chatAskAFriendReceivedDescription": "تلقّى صديقك هذا الاقتراح للتو ويريد أن يعرف ما إذا كان يعرف هذا الشخص.", + "chatAskAFriendAddedDescription": "لقد أضفت هذا المستخدم إلى جهات اتصالك.", + "chatAskAFriendHide": "إخفاء", + "chatAskAFriendRequest": "إرسال طلب", + "chatAskAFriendUnknownUser": "المستخدم {userId}", + "contactUserDiscoveryImagesLeft": "الصور المتبقية قبل مشاركة أصدقائك مع {username}: {imagesLeft}.", + "userDiscoveryEnabledVersion": "الإصدار: {version}", + "userDiscoveryEnabledYourVersion": "إصدارك: {version}", + "userDiscoveryEnabledStopSharing": "إيقاف المشاركة", + "userDiscoveryManualApprovalReachedThreshold": "هل ترغب في مشاركة {username} مع أصدقائك؟", + "userDiscoveryManualApprovalHideContact": "إخفاء جهة الاتصال", + "userDiscoveryManualApprovalShareContact": "مشاركة جهة الاتصال", + "onboardingSetupCompleteTitle": "كل شيء جاهز يا {username}!", + "onboardingSetupCompleteDesc": "يمكنك الآن مشاركة لحظاتك مع أصدقائك بأمان ودون مشتتات مثل الإعلانات.", + "contactUserDiscoveryManualApprovalPending": "لم تتم الموافقة على جهة الاتصال يدويًا بعد.", + "contactUserDiscoveryManualApprovalApprove": "موافقة", + "exampleUserName1": "ahmed", + "exampleUserName2": "fatima", + "exampleUserName3": "omar", + "exampleUserName4": "layla", + "exampleUserName5": "youssef", + "exampleUserName6": "mariam", + "exampleUserName7": "khaled", + "exampleUserName8": "nour", + "exampleUserName9": "hassan", + "exampleUserName10": "lena", + "exampleUserName11": "david", + "exampleJane": "sara", + "back": "رجوع", + "makerChangedUsername": "غيّر {maker} اسم المستخدم من {oldName} إلى {newName}.", + "makerChangedDisplayName": "غيّر {maker} الاسم المعروض من {oldName} إلى {newName}.", + "recoverErrorNoInternet": "لا يوجد اتصال بالإنترنت. يُرجى التحقق من الشبكة والمحاولة مرة أخرى.", + "recoverErrorUsernameNotValid": "اسم المستخدم المُدخل غير صالح أو غير موجود.", + "recoverErrorPasswordInvalid": "كلمة المرور المُدخلة غير صحيحة.", + "recoverErrorTryAgainLater": "الخادم غير متاح حاليًا. يُرجى المحاولة لاحقًا.", + "recoverErrorUnknown": "حدث خطأ غير معروف. يُرجى المحاولة مرة أخرى.", + "recoverSuccessTitle": "تمت استعادة النسخة الاحتياطية بنجاح.", + "recoverSuccessBody": "اضغط هنا لفتح التطبيق مرة أخرى", + "recoverProgressTitle": "جارٍ استعادة نسختك الاحتياطية", + "recoverProgressSubtitle": "قد يستغرق هذا بعض الوقت، يُرجى إبقاء التطبيق مفتوحًا.", + "recoverProgressResolvingAccount": "جارٍ التحقق من الحساب", + "recoverProgressRestoringIdentity": "جارٍ استعادة هويتك", + "recoverProgressDownloadingArchive": "جارٍ تنزيل الرسائل وجهات الاتصال وفك تشفيرها", + "recoverProgressExtractingData": "جارٍ استخراج البيانات", + "recoverProgressFinishing": "جارٍ الإنهاء", + "iosRecoveryWelcomeBack": "مرحبًا بعودتك", + "iosRecoveryPrompt": "اكتشفنا هوية twonly مؤمّنة سابقًا على هذا الجهاز. هل تريد تنزيل جهات اتصالك ورسائلك وإعداداتك واستعادتها تلقائيًا من أرشيفك السحابي؟", + "iosRecoveryNoBackupFound": "تعذّر استرداد أي أرشيف نسخ احتياطي من الخادم لهذا الجهاز.\n\nالخطأ: {error}\n\nيُرجى المتابعة لتسجيل حساب twonly جديد.", + "registerNewAccount": "تسجيل حساب جديد", + "tryRestoreAgain": "إعادة محاولة الاستعادة", + "registeringNewAccount": "جارٍ تسجيل حساب جديد", + "selectEmoji": "اختر رمزًا تعبيريًا", + "subscriptionPledgeSecureTitle": "آمن من حيث التصميم", + "subscriptionPledgeSecureDesc": "رسائلك ولحظاتك المشتركة مشفّرة بالكامل بين الطرفين.", + "subscriptionPledgeNoAdsTitle": "لا إعلانات ولا بيع للبيانات", + "subscriptionPledgeNoAdsDesc": "لن يعرض twonly إعلانات أبدًا ولن يبيع بياناتك الخاصة.", + "subscriptionPledgeSubtitle": "بلا إعلانات. خصوصية تامة.", + "subscriptionManage": "إدارة الاشتراك", + "dragToZoom": "اسحب للتكبير", + "onboardingProfileSelectionTitle": "اختر طريقة الإعداد", + "onboardingProfileSelectionSubtitle": "اختر كيف تريد ضبط إعدادات الأمان والخصوصية.", + "onboardingProfileSelectionDefaultTitle": "افتراضي", + "onboardingProfileSelectionDefaultDesc": "يطبّق الإعدادات الموصى بها فورًا لتبدأ باستخدام التطبيق.", + "onboardingProfileSelectionDefaultBadge": "إعداد سريع", + "onboardingProfileSelectionCustomizeTitle": "تخصيص", + "onboardingProfileSelectionCustomizeDesc": "إعداد خطوة بخطوة لتقرر بنفسك.", + "replyFlameRestored": "تمت استعادة الشعلات", + "replyAskAFriend": "اسأل صديقًا", + "unverifiedWarningButton": "تحقق الآن", + "today": "اليوم", + "yesterday": "أمس", + "galleryDisableWarningTitle": "تعطيل الحفظ في المعرض؟", + "galleryDisableWarningBody": "إذا عطّلت هذا الخيار، فلن تُحفظ ملفات الوسائط في معرضك، وقد تُفقد نهائيًا إذا أُزيل twonly أو واجه مشكلة، لأن ملفات الوسائط لا تُنسخ احتياطيًا بعد.", + "galleryDisableWarningConfirm": "تعطيل", + "settingsStorageScanGalleryTitle": "استيراد من المعرض", + "importGalleryDeselectAll": "إلغاء تحديد الكل", + "importGallerySelectAll": "تحديد الكل", + "importGalleryPermissionRequired": "يلزم إذن الوصول إلى معرضك لاستيراد ملفات وسائط twonly السابقة.", + "importGalleryPermissionError": "حدث خطأ أثناء طلب الإذن: {error}", + "importGalleryLoadError": "تعذّر تحميل الملفات: {error}", + "importGalleryImportingOf": "جارٍ استيراد {current} من {total}...", + "importGalleryStarting": "جارٍ بدء الاستيراد...", + "importGalleryComplete": "اكتمل الاستيراد: نجح استيراد {imported} وفشل {failed}.", + "importGalleryGrantAccess": "منح الوصول", + "importGalleryOpenSettings": "فتح الإعدادات", + "importGalleryPermissionDenied": "تم رفض إذن الوصول إلى المعرض.", + "importGalleryTryAgain": "حاول مرة أخرى", + "importGalleryAlbumNotFound": "لم يتم العثور على ألبوم «twonly»", + "importGalleryAlbumNotFoundDesc": "إذا لم يكن لديك هذا الألبوم بعد، يمكنك أيضًا إنشاؤه لاستيراد الصور إلى twonly.", + "importGalleryNoImagesFound": "لم يتم العثور على صور", + "importGalleryNoImagesFoundDesc": "لا توجد صور على جهازك.", + "importGalleryFilterTwonly": "عرض ألبوم twonly فقط", + "importGalleryRefresh": "تحديث", + "importGallerySelectToImport": "اختر العناصر المراد استيرادها", + "importGalleryImportCount": "{count, plural, =1{استيراد عنصر واحد} =2{استيراد عنصرين} few{استيراد {count} عناصر} many{استيراد {count} عنصرًا} other{استيراد {count} عنصر}}", + "emptyChatListTitle": "ابحث عن صديقك الأول", + "emptyChatListDesc": "اطلب من أصدقائك مسح رمز QR الخاص بك، أو شارك ملفك الشخصي معهم.", + "emptyChatListShareBtn": "شارك ملفك الشخصي", + "emptyChatListScanBtn": "رمز QR", + "emptyChatListAddUsernameBtn": "باسم المستخدم", + "avatarCustomizeRandomize": "عشوائي", + "avatarCustomizeReset": "إعادة تعيين", + "passwordlessRecovery": "الأصدقاء الموثوقون", + "passwordlessRecoveryNotConfigured": "غير مُعدّ", + "passwordlessRecoveryTestPin": "اختبار رمز PIN", + "passwordlessRecoveryTestPinTitle": "اختبار رمز PIN", + "passwordlessRecoveryTestPinHint": "أدخل رمز PIN", + "passwordlessRecoveryTestPinCorrect": "رمز PIN صحيح!", + "passwordlessRecoveryTestPinIncorrect": "رمز PIN غير صحيح.", + "passwordlessRecoveryTest": "اختبار", + "passwordlessRecoverySecondFactorNone": "لا شيء", + "passwordlessRecoverySecondFactorEmailLabel": "البريد الإلكتروني ({email})", + "passwordlessRecoverySecondFactorPin": "رمز PIN", + "passwordlessRecoverySecondFactorEmail": "البريد الإلكتروني", + "passwordlessRecoveryModify": "تعديل إعدادات الاستعادة", + "passwordlessRecoveryModifyDesc": "حدّث إعدادات الاستعادة الخاصة بك", + "passwordlessRecoverySecondFactor": "العامل الثاني", + "passwordlessRecoveryNoFriendsFound": "لم يتم العثور على أصدقاء موثوقين.", + "passwordlessRecoveryActiveFriends": "الأصدقاء النشطون", + "passwordlessRecoveryActiveFriendsDesc": "هؤلاء الأصدقاء الموثوقون يستخدمون twonly بنشاط، ومن المرجح أن يتمكنوا من مساعدتك في استعادة حسابك.", + "passwordlessRecoveryInactiveFriends": "الأصدقاء غير النشطين", + "passwordlessRecoveryInactiveFriendsDesc": "هؤلاء الأصدقاء إما لم يتلقوا حصتهم بعد أو لم يعودوا يستخدمون twonly بنشاط، مما قد يعني أنهم لا يستطيعون المساعدة.", + "passwordlessRecoveryEnableSuccess": "تم إعداد الأصدقاء الموثوقين بنجاح!", + "passwordlessRecoveryEnterPin": "يُرجى إدخال رمز PIN.", + "passwordlessRecoveryEnterEmail": "يُرجى إدخال عنوان بريد إلكتروني.", + "passwordlessRecoveryEnableBtn": "تفعيل الأصدقاء الموثوقين", + "passwordlessRecoveryRecoverBtn": "الاستعادة عبر الأصدقاء الموثوقين", + "passwordlessRecoveryModifyBtn": "تعديل الأصدقاء الموثوقين", + "passwordlessRecoveryStatusEnabled": "مفعّل • الأصدقاء الموثوقون: {count}", + "passwordlessRecoveryInfoHowItWorks": "كيف يعمل", + "passwordlessRecoveryInfoHowItWorksDesc": "يعمل twonly دون أرقام هواتف، ولهذا نعتمد على آلية استعادة لامركزية. باستخدام مخطط شامير لتقاسم الأسرار (Shamir's Secret Sharing)، تُقسَّم هويتك التشفيرية إلى حصص مستقلة وتُوزَّع على أصدقائك الموثوقين. لاستعادة الوصول، يجب أن يجمع عدد محدد مسبقًا من هؤلاء الأصدقاء حصصهم.", + "passwordlessRecoveryInfoWhySecondFactor": "لماذا العامل الثاني؟", + "passwordlessRecoveryInfoWhySecondFactorDesc": "يوفّر العامل الثاني (البريد الإلكتروني أو رمز PIN) حماية إضافية مهمة. فحتى لو اتفق أصدقاؤك سرًّا فيما بينهم، فلن يتمكنوا من الوصول إلى حسابك دون هذا العامل الثاني.", + "passwordlessRecoveryInfoGotIt": "فهمت", + "passwordlessRecoveryMethod": "طريقة العامل الثاني", + "passwordlessRecoveryMethodNoneDesc": "بدون عامل ثانٍ، يمكن لأصدقائك التعاون لاستعادة حسابك. لذلك يُنصح بإعداد عامل ثانٍ.", + "passwordlessRecoveryMethodPinHint": "أدخل رمز PIN", + "passwordlessRecoveryMethodEmailHint": "أدخل البريد الإلكتروني للاستعادة", + "passwordlessRecoveryMethodEmailDesc": "لا يتم *تخزين عنوان بريدك الإلكتروني على الخادم أبدًا*، ولا يُرسل إليه إلا في حالة الاستعادة.", + "passwordlessRecoveryThresholdDesc": "لاستعادة حسابك، تحتاج إلى {count} من أصدقائك الموثوقين المختارين.", + "passwordlessRecoveryThresholdTitle": "عدد الأصدقاء الموثوقين المطلوب للاستعادة", + "passwordlessRecoverySelectFriendsNeeded": "اختر الأصدقاء (المتبقي: {count})", + "passwordlessRecoverySelectFriends": "اختر الأصدقاء الموثوقين", + "passwordlessRecoveryTrustedFriends": "استعادة الحساب", + "passwordlessRecoveryDoneBtn": "تم ({count})", + "passwordlessRecoveryNoFriendsSelected": "لم يتم اختيار أي أصدقاء موثوقين بعد", + "passwordlessRecoverySubtitle": "استعد هويتك دون كلمة مرور.", + "recoverPasswordlessExplanation": "عند التفعيل، يمكنك استعادة حسابك بطلب المساعدة من أصدقائك المختارين.", + "recoverPasswordlessNotificationCardTitle": "تفعيل الإشعارات", + "recoverPasswordlessNotificationCardSubtitle": "احصل على إشعار عندما يساعدك صديق في استعادة حسابك.", + "recoverPasswordlessNotificationCardBtn": "تفعيل الإشعارات الفورية", + "recoverPasswordlessQrInstructions": "اطلب من أصدقائك مسح رمز QR أو شارك الرابط.", + "recoverPasswordlessShareBtn": "مشاركة رابط الاستعادة", + "recoverPasswordlessCopyBtn": "نسخ", + "recoverPasswordlessCopiedSnackbar": "تم نسخ الرابط إلى الحافظة!", + "passwordlessRecoveryNoShareStored": "لا توجد حصة استعادة محفوظة لجهة الاتصال هذه.", + "passwordlessRecoveryEmailSent": "تم إرسال بريد الاستعادة!", + "passwordlessRecoveryShareSent": "تم إرسال حصة الاستعادة!", + "passwordlessRecoveryAuthReason": "قم بالمصادقة لإرسال حصة الاستعادة الخاصة بك", + "passwordlessRecoveryAuthFailed": "فشلت المصادقة، ولم يتم إرسال حصة الاستعادة.", + "passwordlessRecoveryNetworkError": "خطأ في الشبكة، يُرجى التأكد من اتصالك بالإنترنت", + "passwordlessRecoveryInvalidEmail": "عنوان البريد الإلكتروني غير صالح.", + "passwordlessRecoveryResendEmail": "إعادة إرسال بريد الاستعادة", + "passwordlessRecoveryHelpAFriend": "ساعد صديقًا", + "passwordlessRecoverySelectContactDesc": "يُرجى اختيار جهة الاتصال التي طلبت الاستعادة أدناه لإرسال مفتاح الاستعادة إليها.", + "passwordlessRecoverySearchContacts": "البحث في جهات الاتصال...", + "passwordlessRecoveryNoContactsFound": "لم يتم العثور على جهات اتصال", + "passwordlessRecoveryCantHelpHim": "لا يمكنك مساعدة هذا الشخص", + "passwordlessRecoveryDoesAskedYou": "هل وصلك هذا الطلب من {username}؟", + "passwordlessRecoveryVerifySourceDesc": "يُرجى التأكد من أن هذا الطلب جاء فعلًا من هذا الشخص. إذا كنت في شك، فاسأله عبر وسيلة أخرى!", + "passwordlessRecoveryNo": "لا", + "passwordlessRecoveryYes": "نعم", + "passwordlessRecoveryYesWithTimer": "نعم ({seconds} ث)", + "recoverPasswordlessSharesReceived": "الأصدقاء الذين ساعدوك: {received} من {threshold}", + "recoverPasswordlessRecoverNowBtn": "استعد الآن", + "missingRecoveryContactsCardTitle": "استعادة الحساب", + "missingRecoveryContactsCardDesc": "اختر جهات اتصال للاستعادة لتساعدك في استعادة حسابك دون الحاجة إلى كلمة مرور.", + "missingRecoveryContactsCardAction": "اختر جهات الاتصال", + "memoriesBackupLimitReached": "تم الوصول إلى حد النسخ الاحتياطي السحابي! يُرجى ترقية خطتك أو إخلاء بعض المساحة.", + "settingsStorageLocal": "التخزين المحلي", + "settingsStorageNoCloudBackupCard": "انسخ ذكرياتك احتياطيًا إلى السحابة لتوفير المساحة المحلية وضمان عدم فقدان صورك أبدًا. كل ذلك بتشفير تام بين الطرفين.", + "galleryActionSave": "حفظ", + "galleryActionExport": "تصدير", + "galleryActionFavorite": "إضافة إلى المفضلة", + "galleryActionUnfavorite": "إزالة من المفضلة", + "galleryActionDelete": "حذف", + "galleryActionShare": "مشاركة", + "settingsStorageHidePromo": "إخفاء", + "memoriesBackupTitle": "النسخ الاحتياطي للذكريات", + "settingsHelpNews": "الأخبار", + "settingsStorageContents": "إخلاء مساحة", + "settingsStorageSortStorage": "المساحة المشغولة", + "settingsStorageSortNewest": "الأحدث", + "settingsStorageSortOldest": "الأقدم", + "settingsStorageNoContents": "لم يتم العثور على محتويات في التخزين.", + "settingsStorageCloudOnly": "في السحابة فقط", + "settingsStorageLocalAndCloud": "محليًا وفي السحابة", + "settingsStorageLocalOnly": "محليًا فقط", + "settingsStorageByChat": "التخزين حسب المحادثة", + "settingsStorageTemporaryMediaDescription": "ملفات الوسائط المؤقتة هي ملفات وسائط مرسلة لم يتم حفظها. تبقى على جهازك حتى يستلمها المستلم ويفتحها.", + "settingsStorageNoChats": "لم يتم العثور على محادثات.", + "settingsStorageStoredMediaCount": "{count, plural, =1{ملف محفوظ واحد} =2{ملفان محفوظان} few{{count} ملفات محفوظة} many{{count} ملفًا محفوظًا} other{{count} ملف محفوظ}}", + "settingsStorageTemporaryMediaCount": "{count, plural, =1{ملف مؤقت واحد} =2{ملفان مؤقتان} few{{count} ملفات مؤقتة} many{{count} ملفًا مؤقتًا} other{{count} ملف مؤقت}}", + "settingsStorageDisableBackupTitle": "تعطيل النسخ الاحتياطي؟", + "settingsStorageDisableBackupBody": "سيؤدي هذا إلى حذف جميع الذكريات المنسوخة احتياطيًا من السحابة. {count, plural, =1{توجد *صورة واحدة* مخزّنة في السحابة فقط وستُفقد *نهائيًا*.} =2{توجد *صورتان* مخزّنتان في السحابة فقط وستُفقدان *نهائيًا*.} few{توجد *{count} صور* مخزّنة في السحابة فقط وستُفقد *نهائيًا*.} many{توجد *{count} صورة* مخزّنة في السحابة فقط وستُفقد *نهائيًا*.} other{توجد *{count} صورة* مخزّنة في السحابة فقط وستُفقد *نهائيًا*.}} هل أنت متأكد أنك تريد المتابعة؟", + "settingsStorageDisableBackupBtn": "تعطيل", + "settingsStorageDisableBackupAction": "تعطيل النسخ الاحتياطي السحابي", + "settingsStorageSyncNow": "مزامنة الآن", + "settingsStorageSyncUpToDate": "جميع الذكريات محدّثة.", + "restoreLostFlames": "استعد شعلاتك المفقودة ({count})", + "settingsShowRestoreFlameTitle": "إظهار تحذير استعادة الشعلات", + "contactGroupsTitle": "مجموعات جهات الاتصال", + "contactGroupsSubtitleEmpty": "لم يتم اختيار مجموعات جهات اتصال ظاهرة", + "createContactGroup": "إنشاء مجموعة جهات اتصال", + "editContactGroup": "تعديل مجموعة جهات الاتصال", + "deleteContactGroup": "حذف مجموعة جهات الاتصال", + "deleteContactGroupConfirmation": "هل أنت متأكد أنك تريد حذف مجموعة جهات الاتصال هذه؟ ستتم إزالتها من جميع جهات الاتصال والمجموعات.", + "contactGroupTextColor": "لون النص", + "contactGroupBackgroundColor": "لون الخلفية", + "contactGroupNoBackground": "بلا خلفية", + "contactGroupFeatures": "الميزات", + "contactGroupShowAsLabel": "إظهار كتسمية ظاهرة", + "contactGroupShowAsLabelSubtitle": "عرض مجموعة جهات الاتصال هذه بجوار جهات الاتصال التابعة لها.", + "contactGroupShowAsShortcut": "إظهار كاختصار", + "contactGroupShowAsShortcutSubtitle": "استخدم رمزها التعبيري لتحديد جميع الأعضاء عند المشاركة.", + "contactGroupShortcutNeedsEmoji": "اختر رمزًا تعبيريًا قبل تفعيل الاختصار.", + "contactGroupMembers": "الأعضاء", + "contactGroupLabelFeature": "تسمية ظاهرة", + "contactGroupShortcutFeature": "اختصار", + "contactGroupSettings": "تعديل مجموعة جهات الاتصال", + "save": "حفظ", + "customColor": "لون مخصص", + "hue": "درجة اللون", + "saturation": "التشبع", + "brightness": "السطوع", + "settingsAppearancePrimaryColor": "اللون الأساسي", + "themeSystemDefault": "افتراضي النظام", + "themeLight": "فاتح", + "themeDark": "داكن", + "backupRecoverySectionTitle": "وصول آمن إلى الحساب", + "backupRecoverySectionDescNone": "يجب عليك اختيار خيار استعادة واحد على الأقل، وإلا فلن يتمكن أحد من مساعدتك إذا فقدت الوصول إلى جهازك.", + "backupRecoverySectionDescSome": "يمكنك استعادة حسابك باستخدام خيارات الاستعادة أدناه. يُنصح بتفعيل الخيارين كليهما.", + "backupRecoveryOptionAFriends": "عبر الأصدقاء الموثوقين", + "backupRecoveryOptionAMicrocopy": "اختر أصدقاء يمكنهم مساعدتك في استعادة حسابك.", + "backupRecoveryOptionBPassword": "كلمة مرور تقليدية", + "backupRecoveryOptionBMicrocopy": "عيّن كلمة مرور للوصول إلى حسابك.", + "backupCloudSectionTitle": "انسخ بياناتك احتياطيًا", + "backupCloudSectionDesc": "انسخ محتواك احتياطيًا حتى لا يضيع شيء.", + "backupCloudContactsMessages": "جهات الاتصال والرسائل", + "backupCloudImagesMedia": "الصور والوسائط", + "backupCloudProBadge": "Pro", + "memoriesAddingToFavorites": "جارٍ الإضافة إلى المفضلة...", + "memoriesRemovingFromFavorites": "جارٍ الإزالة من المفضلة...", + "memoriesDeletingProgress": "جارٍ حذف الذكريات...", + "memoriesExportingProgress": "جارٍ تصدير الذكريات...", + "fileLimitReachedTitle": "الملف أكبر من أن يُرسل", + "fileLimitReachedDetail": "حجم هذا الملف {size}، لكن خطتك تسمح بـ {limit} كحد أقصى لكل إرسال.", + "fileLimitReachedDetailNoSize": "تسمح خطتك بـ {limit} كحد أقصى لكل إرسال.", + "fileLimitReachedHint": "سجّل فيديو أقصر وأرسله مرة أخرى.", + "fileLimitReachedHintFree": "سجّل فيديو أقصر، أو قم بترقية خطتك لإرسال ملفات أكبر.", + "fileLimitReachedUpgrade": "ترقية الخطة", + "contactGroupUsedByWidget": "أداة الشاشة الرئيسية", + "contactGroupUsedByWidgetSubtitle": "{count, plural, =1{تعرض أداة على شاشتك الرئيسية صورًا من هذه المجموعة.} =2{تعرض أداتان على شاشتك الرئيسية صورًا من هذه المجموعة.} few{تعرض {count} أدوات على شاشتك الرئيسية صورًا من هذه المجموعة.} many{تعرض {count} أداةً على شاشتك الرئيسية صورًا من هذه المجموعة.} other{تعرض {count} أداة على شاشتك الرئيسية صورًا من هذه المجموعة.}}", + "contactGroupDeleteBlockedByWidget": "{count, plural, =1{تستخدم أداةٌ على شاشتك الرئيسية مجموعة جهات الاتصال هذه. أزل تلك الأداة أولًا، ثم يمكنك حذف المجموعة.} =2{تستخدم أداتان على شاشتك الرئيسية مجموعة جهات الاتصال هذه. أزل هاتين الأداتين أولًا، ثم يمكنك حذف المجموعة.} few{تستخدم {count} أدوات على شاشتك الرئيسية مجموعة جهات الاتصال هذه. أزل تلك الأدوات أولًا، ثم يمكنك حذف المجموعة.} many{تستخدم {count} أداةً على شاشتك الرئيسية مجموعة جهات الاتصال هذه. أزل تلك الأدوات أولًا، ثم يمكنك حذف المجموعة.} other{تستخدم {count} أداة على شاشتك الرئيسية مجموعة جهات الاتصال هذه. أزل تلك الأدوات أولًا، ثم يمكنك حذف المجموعة.}}", + "webxdcStoreMenu": "تطبيق", + "webxdcStoreTitle": "إضافة تطبيق", + "webxdcStoreEmpty": "لا توجد تطبيقات متاحة بعد.", + "webxdcStoreOffline": "تعذّر تحميل قائمة التطبيقات. تحقق من اتصالك وحاول مرة أخرى.", + "webxdcStoreFailed": "تعذّرت إضافة التطبيق إلى هذه المحادثة.", + "webxdcStoreAlreadyAdded": "مضاف بالفعل إلى هذه المحادثة", + "webxdcChatApps": "تطبيقات المحادثة", + "webxdcChatAppsEmpty": "لم تتم إضافة أي تطبيقات لمرة واحدة إلى هذه المحادثة بعد.", + "webxdcUnavailable": "تعذّر تحميل هذا التطبيق.", + "webxdcDeleteConfirm": "سيتم حذف كل ما حفظه هذا التطبيق على هذا الجهاز. يحتفظ الأعضاء الآخرون بنسختهم الخاصة.", + "webxdcExternalLinkTitle": "مغادرة twonly؟", + "webxdcExternalLinkBody": "يُفتح هذا الرابط خارج twonly، في متصفحك:" +} diff --git a/lib/src/localization/translations/de.arb b/lib/src/localization/translations/de.arb index 5773b83b..e1481fe6 100644 --- a/lib/src/localization/translations/de.arb +++ b/lib/src/localization/translations/de.arb @@ -111,7 +111,6 @@ "settingsNotifyResetTitleResetDesc": "Sollte das Problem weiterhin bestehen, sende uns bitte dein Debug-Protokoll über „Einstellungen“ > „Hilfe“, damit wir das Problem untersuchen können.", "settingsHelp": "Hilfe", "settingsHelpFAQ": "FAQ", - "feedbackTooltip": "Feedback zur Verbesserung von twonly geben.", "settingsHelpContactUs": "Kontaktiere uns", "contactUsFaq": "FAQ schon gelesen?", "contactUsEmojis": "Wie fühlst du dich? (optional)", @@ -137,6 +136,7 @@ "settingsHelpImprint": "Impressum & Datenschutzrichtlinie", "settingsHelpTerms": "Nutzungsbedingungen", "settingsAppearanceTheme": "Theme", + "settingsAppearanceLanguage": "Sprache", "settingsAccountDeleteAccount": "Konto löschen", "settingsAccountDeleteAccountNoBallance": "Wenn du dein Konto gelöscht hast, gibt es keinen Weg zurück.", "settingsAccountDeleteModalTitle": "Bist du sicher?", @@ -264,13 +264,7 @@ "buy": "Kaufen", "autoRenewal": "Automatische Verlängerung", "additionalUsersList": "Deine zusätzlichen Benutzer", - "galleryDelete": "Datei löschen", - "galleryExport": "In Galerie exportieren", "galleryExportSuccess": "Erfolgreich in der Gallery gespeichert.", - "gallerySelectAll": "Alle auswählen", - "galleryDeselectAll": "Auswahl aufheben", - "galleryFavorite": "Als Favorit markieren", - "galleryUnfavorite": "Favorit entfernen", "galleryCancel": "Abbrechen", "memoriesEmpty": "Sobald du Bilder oder Videos speicherst, landen sie hier in deinen Erinnerungen.", "memoriesDeleteSnackbarSuccess": "{count, plural, =1 {1 Element erfolgreich gelöscht} other {{count} Elemente erfolgreich gelöscht}}", @@ -279,7 +273,6 @@ "deleteOkBtnForMe": "Für mich löschen", "deleteOnlyForMe": "Nur für mich löschen", "deleteImageTitle": "Bist du dir sicher?", - "deleteImageBody": "Das Bild wird unwiderruflich gelöscht.", "deleteMemoriesBody": "{count, plural, =1 {Das Bild wird unwiderruflich gelöscht.} other {Die {count} Bilder werden unwiderruflich gelöscht.}}", "@deleteMemoriesBody": { "placeholders": { @@ -304,15 +297,6 @@ "memoriesMenuFavorite": "Favorisieren", "memoriesMenuDelete": "Löschen", "memoriesMenuDeleteLocal": "Lokal löschen", - "backupPending": "Ausstehend", - "backupFailed": "Fehlgeschlagen", - "backupSuccess": "Erfolgreich", - "backupTwonlySafeDesc": "Sichere deine twonly-Identität, da dies die einzige Möglichkeit ist, dein Konto wiederherzustellen, wenn du die App deinstallierst oder dein Handy verlierst.", - "backupIdentityHeader": "Identität", - "backupArchiveHeader": "Kontakte & Nachrichten", - "backupLastBackupDate": "Letztes Backup", - "backupLastBackupSize": "Backup-Größe", - "backupLastBackupResult": "Ergebnis", "backupNoPasswordRecovery": "Aufgrund des Sicherheitssystems von twonly gibt es (derzeit) keine Funktion zur Wiederherstellung des Passworts. Daher musst du dir dein Passwort merken oder, besser noch, aufschreiben.", "backupInsecurePassword": "Unsicheres Passwort", "backupInsecurePasswordDesc": "Das gewählte Passwort ist sehr unsicher und kann daher leicht von Angreifern erraten werden. Bitte wähle ein sicheres Passwort.", @@ -435,7 +419,6 @@ "notificationReactionToAudio": "hat mit {reaction} auf deine Sprachnachricht reagiert.", "notificationResponse": "hat dir{inGroup} geantwortet.", "notificationCategoryMessageTitle": "Nachrichten", - "notificationCategoryMessageDesc": "Nachrichten von anderen Benutzern.", "notificationConnectionFallback": "Du könntest neue Nachrichten haben.", "groupContextMenuDeleteGroup": "Dadurch werden alle Nachrichten in diesem Chat dauerhaft gelöscht.", "groupYouAreNowLongerAMember": "Du bist nicht mehr Mitglied dieser Gruppe.", @@ -970,7 +953,6 @@ "missingRecoveryContactsCardAction": "Kontakte auswählen", "memoriesBackupLimitReached": "Cloud-Backup-Limit erreicht! Bitte aktualisiere dein Abonnement oder gib Speicherplatz frei.", "settingsStorageLocal": "Lokaler Speicherplatz", - "settingsStorageNoCloudBackupTitle": "Kein Cloud-Backup", "settingsStorageNoCloudBackupCard": "Sichere deine Erinnerungen in der Cloud, um lokalen Speicherplatz freizugeben und deine Bilder nicht zu verlieren. Alles Ende-zu-Ende verschlüsselt.", "galleryActionSave": "Sichern", "galleryActionExport": "Exportieren", @@ -1066,7 +1048,6 @@ "backupCloudSectionTitle": "Deine Daten sichern", "backupCloudSectionDesc": "Sichere deine Inhalte, damit nichts verloren geht.", "backupCloudContactsMessages": "Kontakte & Nachrichten", - "backupCloudFreeActive": "Kostenlos aktiv", "backupCloudImagesMedia": "Bilder & Medien", "backupCloudProBadge": "Pro", "memoriesAddingToFavorites": "Zu Favoriten hinzufügen...", @@ -1119,8 +1100,7 @@ "webxdcStoreOffline": "Die App-Liste konnte nicht geladen werden. Prüfe deine Verbindung und versuche es erneut.", "webxdcStoreFailed": "Die App konnte diesem Chat nicht hinzugefügt werden.", "webxdcStoreAlreadyAdded": "Bereits zu diesem Chat hinzugefügt", - "webxdcChatAppsMenu": "Chat-Apps", - "webxdcChatAppsTitle": "Chat-Apps", + "webxdcChatApps": "Chat-Apps", "webxdcChatAppsEmpty": "Diesem Chat wurden noch keine einmaligen Apps hinzugefügt.", "webxdcUnavailable": "Diese App konnte nicht geladen werden.", "webxdcDeleteConfirm": "Alles, was diese App auf diesem Gerät gespeichert hat, wird gelöscht. Andere Mitglieder behalten ihre eigene Kopie.", diff --git a/lib/src/localization/translations/en.arb b/lib/src/localization/translations/en.arb index c580170d..a4918175 100644 --- a/lib/src/localization/translations/en.arb +++ b/lib/src/localization/translations/en.arb @@ -108,7 +108,6 @@ "settingsHelp": "Help", "settingsHelpDiagnostics": "Diagnostic protocol", "settingsHelpFAQ": "FAQ", - "feedbackTooltip": "Give Feedback to improve twonly.", "settingsHelpContactUs": "Contact us", "settingsHelpVersion": "Version", "settingsHelpLicenses": "Licenses (Source-Code)", @@ -133,6 +132,7 @@ "hideNewsIcon": "Hide News Icon", "settingsHelpTerms": "Terms of Service", "settingsAppearanceTheme": "Theme", + "settingsAppearanceLanguage": "Language", "settingsAccountDeleteAccount": "Delete account", "settingsAccountDeleteAccountNoBallance": "Once you delete your account, there is no going back.", "settingsAccountDeleteModalTitle": "Are you sure?", @@ -274,13 +274,7 @@ "buy": "Buy", "autoRenewal": "Auto renewal", "additionalUsersList": "Your additional users", - "galleryDelete": "Delete file", - "galleryExport": "Export to gallery", "galleryExportSuccess": "Successfully saved in the Gallery.", - "gallerySelectAll": "Select all", - "galleryDeselectAll": "Deselect all", - "galleryFavorite": "Favorite", - "galleryUnfavorite": "Unfavorite", "galleryCancel": "Cancel", "memoriesEmpty": "As soon as you save pictures or videos, they end up here in your memories.", "deleteTitle": "Are you sure?", @@ -289,7 +283,6 @@ "deleteOkBtnForMe": "Delete for me", "deleteOnlyForMe": "Delete only for me", "deleteImageTitle": "Are you sure?", - "deleteImageBody": "The image will be irrevocably deleted.", "deleteMemoriesBody": "{count, plural, =1 {The image will be irrevocably deleted.} other {The {count} images will be irrevocably deleted.}}", "@deleteMemoriesBody": { "placeholders": { @@ -315,16 +308,7 @@ "memoriesMenuDelete": "Delete completely", "memoriesMenuDeleteLocal": "Delete locally", "settingsBackup": "Backup", - "backupPending": "Pending", - "backupFailed": "Failed", - "backupSuccess": "Success", - "backupTwonlySafeDesc": "Back up your twonly identity, as this is the only way to restore your account if you uninstall the app or lose your phone.", "backupNoPasswordRecovery": "Due to twonly's security system, there is (currently) no password recovery function. Therefore, you must remember your password or, better yet, write it down.", - "backupIdentityHeader": "Identity", - "backupArchiveHeader": "Contacts & Messages", - "backupLastBackupDate": "Last backup", - "backupLastBackupSize": "Backup size", - "backupLastBackupResult": "Result", "backupInsecurePassword": "Insecure password", "backupInsecurePasswordDesc": "The chosen password is very insecure and can therefore easily be guessed by attackers. Please choose a secure password.", "backupInsecurePasswordOk": "Continue anyway", @@ -445,7 +429,6 @@ "notificationReactionToAudio": "has reacted with {reaction} to your audio message.", "notificationResponse": "has responded{inGroup}.", "notificationCategoryMessageTitle": "Messages", - "notificationCategoryMessageDesc": "Messages from other users.", "notificationConnectionFallback": "You may have new messages.", "groupContextMenuDeleteGroup": "This will permanently delete all messages in this chat.", "groupYouAreNowLongerAMember": "You are no longer part of this group.", @@ -980,7 +963,6 @@ "missingRecoveryContactsCardAction": "Select Contacts", "memoriesBackupLimitReached": "Cloud backup limit reached! Please upgrade your plan or free up space.", "settingsStorageLocal": "Local storage", - "settingsStorageNoCloudBackupTitle": "No Cloud Backup", "settingsStorageNoCloudBackupCard": "Back up your memories to the cloud to free up local space and ensure you never lose your pictures. All end-to-end encrypted.", "galleryActionSave": "Save", "galleryActionExport": "Export", @@ -1076,7 +1058,6 @@ "backupCloudSectionTitle": "Backup Your Data", "backupCloudSectionDesc": "Backup your content so nothing gets lost.", "backupCloudContactsMessages": "Contacts & Messages", - "backupCloudFreeActive": "Free Active", "backupCloudImagesMedia": "Images & Media", "backupCloudProBadge": "Pro", "memoriesAddingToFavorites": "Adding to favorites...", @@ -1129,8 +1110,7 @@ "webxdcStoreOffline": "The app list could not be loaded. Check your connection and try again.", "webxdcStoreFailed": "The app could not be added to this chat.", "webxdcStoreAlreadyAdded": "Already added to this chat", - "webxdcChatAppsMenu": "Chat apps", - "webxdcChatAppsTitle": "Chat apps", + "webxdcChatApps": "Chat apps", "webxdcChatAppsEmpty": "No one-time apps have been added to this chat yet.", "webxdcUnavailable": "This app could not be loaded.", "webxdcDeleteConfirm": "Everything this app saved on this device is deleted. Other members keep their own copy.", diff --git a/lib/src/providers/settings.provider.dart b/lib/src/providers/settings.provider.dart index 82c95748..466282d2 100644 --- a/lib/src/providers/settings.provider.dart +++ b/lib/src/providers/settings.provider.dart @@ -5,13 +5,25 @@ import 'package:twonly/locator.dart'; import 'package:twonly/src/services/user.service.dart'; import 'package:twonly/src/visual/themes/light.dart'; +/// Languages the app can be shown in. The first one is used when the device +/// language is none of them. +const supportedLocales = [ + Locale('en'), + Locale('de'), + Locale('ar'), +]; + class SettingsChangeProvider with ChangeNotifier, DiagnosticableTreeMixin { late ThemeMode _themeMode; late Color _primaryColor; + Locale? _locale; ThemeMode get themeMode => _themeMode; Color get primaryColor => _primaryColor; + /// The language picked in the settings, or null to follow the device. + Locale? get locale => _locale; + void loadSettings() { if (userService.isUserCreated) { _themeMode = switch (userService.currentUser.themeMode) { @@ -23,10 +35,15 @@ class SettingsChangeProvider with ChangeNotifier, DiagnosticableTreeMixin { _primaryColor = primaryColorValue == null ? defaultPrimaryColor : Color(primaryColorValue); + final language = userService.currentUser.language; + _locale = supportedLocales + .where((locale) => locale.languageCode == language) + .firstOrNull; notifyListeners(); } else { _themeMode = ThemeMode.system; _primaryColor = defaultPrimaryColor; + _locale = null; } } @@ -59,4 +76,16 @@ class SettingsChangeProvider with ChangeNotifier, DiagnosticableTreeMixin { (u) => u.primaryColorValue = newColor.toARGB32(), ); } + + Future updateLocale(Locale? newLocale) async { + if (newLocale == _locale) return; + + _locale = newLocale; + + notifyListeners(); + + await UserService.update( + (u) => u.language = newLocale?.languageCode, + ); + } } diff --git a/lib/src/visual/views/contact/contact.view.dart b/lib/src/visual/views/contact/contact.view.dart index af6f0eb4..c0d64d43 100644 --- a/lib/src/visual/views/contact/contact.view.dart +++ b/lib/src/visual/views/contact/contact.view.dart @@ -266,7 +266,7 @@ class _ContactViewState extends State { ), BetterListTile( icon: FontAwesomeIcons.layerGroup, - text: context.lang.webxdcChatAppsMenu, + text: context.lang.webxdcChatApps, onTap: () => context.navPush( WebxdcChatAppsView( groupId: getUUIDforDirectChat( diff --git a/lib/src/visual/views/groups/group.view.dart b/lib/src/visual/views/groups/group.view.dart index e8eb9a58..2b7bef4e 100644 --- a/lib/src/visual/views/groups/group.view.dart +++ b/lib/src/visual/views/groups/group.view.dart @@ -216,7 +216,7 @@ class _GroupViewState extends State { ), BetterListTile( icon: FontAwesomeIcons.layerGroup, - text: context.lang.webxdcChatAppsMenu, + text: context.lang.webxdcChatApps, onTap: () => context.navPush( WebxdcChatAppsView(groupId: widget.groupId), ), diff --git a/lib/src/visual/views/settings/appearance.view.dart b/lib/src/visual/views/settings/appearance.view.dart index 4b043e75..e42b6cc0 100644 --- a/lib/src/visual/views/settings/appearance.view.dart +++ b/lib/src/visual/views/settings/appearance.view.dart @@ -9,6 +9,14 @@ import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/visual/components/custom_color_picker_dialog.comp.dart'; import 'package:twonly/src/visual/elements/radio_button.element.dart'; +/// Each language is listed in its own name, so it can be found again after +/// switching to a language the user cannot read. +const _languageNames = { + 'en': 'English', + 'de': 'Deutsch', + 'ar': 'العربية', +}; + class AppearanceView extends StatefulWidget { const AppearanceView({super.key}); @@ -73,6 +81,47 @@ class _AppearanceViewState extends State { } } + Future _showSelectLanguage(BuildContext context) async { + final currentLocale = context.read().locale; + + // A record distinguishes picking the system default (null) from dismissing. + final selected = await showDialog<({Locale? locale})>( + context: context, + builder: (context) { + return AlertDialog( + title: Text(context.lang.settingsAppearanceLanguage), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + RadioButton( + value: null, + groupValue: currentLocale, + label: context.lang.themeSystemDefault, + onChanged: (value) { + Navigator.of(context).pop((locale: value)); + }, + ), + for (final locale in supportedLocales) + RadioButton( + value: locale, + groupValue: currentLocale, + label: _languageName(locale), + onChanged: (value) { + Navigator.of(context).pop((locale: value)); + }, + ), + ], + ), + ); + }, + ); + if (selected != null && context.mounted) { + await context.read().updateLocale( + selected.locale, + ); + } + } + Future _showSelectPrimaryColor(BuildContext context) async { const presetColors = [ Color(0xFF57CC99), // Original Twonly Green @@ -221,9 +270,13 @@ class _AppearanceViewState extends State { } } + String _languageName(Locale locale) => + _languageNames[locale.languageCode] ?? locale.languageCode; + @override Widget build(BuildContext context) { final selectedTheme = context.watch().themeMode; + final selectedLocale = context.watch().locale; final primaryColor = context.watch().primaryColor; return Scaffold( @@ -251,6 +304,18 @@ class _AppearanceViewState extends State { await _showSelectThemeMode(context); }, ), + ListTile( + title: Text(context.lang.settingsAppearanceLanguage), + subtitle: Text( + selectedLocale == null + ? context.lang.themeSystemDefault + : _languageName(selectedLocale), + style: const TextStyle(color: Colors.grey), + ), + onTap: () async { + await _showSelectLanguage(context); + }, + ), ListTile( title: Text(context.lang.settingsAppearancePrimaryColor), trailing: Container( diff --git a/lib/src/visual/views/webxdc/webxdc_chat_apps.view.dart b/lib/src/visual/views/webxdc/webxdc_chat_apps.view.dart index d0f8f6d2..60876c3c 100644 --- a/lib/src/visual/views/webxdc/webxdc_chat_apps.view.dart +++ b/lib/src/visual/views/webxdc/webxdc_chat_apps.view.dart @@ -62,7 +62,7 @@ class _WebxdcChatAppsViewState extends State { Widget build(BuildContext context) { final apps = _apps; return Scaffold( - appBar: AppBar(title: Text(context.lang.webxdcChatAppsTitle)), + appBar: AppBar(title: Text(context.lang.webxdcChatApps)), body: apps == null ? const Center(child: CircularProgressIndicator()) : apps.isEmpty diff --git a/rust/src/frb_generated.rs b/rust/src/frb_generated.rs index aff6f3ca..a2053491 100644 --- a/rust/src/frb_generated.rs +++ b/rust/src/frb_generated.rs @@ -7663,6 +7663,7 @@ impl SseDecode for crate::user_config::UserConfig { let mut var_additionalUserInvites = >::sse_decode(deserializer); let mut var_themeMode = ::sse_decode(deserializer); let mut var_primaryColorValue = >::sse_decode(deserializer); + let mut var_language = >::sse_decode(deserializer); let mut var_defaultShowTime = >::sse_decode(deserializer); let mut var_requestedAudioPermission = ::sse_decode(deserializer); let mut var_enableDatabaseLogging = ::sse_decode(deserializer); @@ -7731,6 +7732,7 @@ impl SseDecode for crate::user_config::UserConfig { additional_user_invites: var_additionalUserInvites, theme_mode: var_themeMode, primary_color_value: var_primaryColorValue, + language: var_language, default_show_time: var_defaultShowTime, requested_audio_permission: var_requestedAudioPermission, enable_database_logging: var_enableDatabaseLogging, @@ -8941,6 +8943,7 @@ impl flutter_rust_bridge::IntoDart for crate::user_config::UserConfig { self.additional_user_invites.into_into_dart().into_dart(), self.theme_mode.into_into_dart().into_dart(), self.primary_color_value.into_into_dart().into_dart(), + self.language.into_into_dart().into_dart(), self.default_show_time.into_into_dart().into_dart(), self.requested_audio_permission.into_into_dart().into_dart(), self.enable_database_logging.into_into_dart().into_dart(), @@ -10096,6 +10099,7 @@ impl SseEncode for crate::user_config::UserConfig { >::sse_encode(self.additional_user_invites, serializer); ::sse_encode(self.theme_mode, serializer); >::sse_encode(self.primary_color_value, serializer); + >::sse_encode(self.language, serializer); >::sse_encode(self.default_show_time, serializer); ::sse_encode(self.requested_audio_permission, serializer); ::sse_encode(self.enable_database_logging, serializer); diff --git a/rust/src/native/notifications.rs b/rust/src/native/notifications.rs index 0800e946..52bdcd28 100644 --- a/rust/src/native/notifications.rs +++ b/rust/src/native/notifications.rs @@ -107,9 +107,11 @@ pub unsafe extern "C" fn twonly_notification_process( widget_refresh: true, batch: Some(batch), fallback: Some(notifications::fallback_presentation( - unsafe { required_string(locale, "locale") } - .as_deref() - .unwrap_or("en"), + ¬ifications::app_locale( + unsafe { required_string(locale, "locale") } + .as_deref() + .unwrap_or("en"), + ), )), error: None, }), @@ -118,9 +120,11 @@ pub unsafe extern "C" fn twonly_notification_process( widget_refresh: false, batch: None, fallback: Some(notifications::fallback_presentation( - unsafe { required_string(locale, "locale") } - .as_deref() - .unwrap_or("en"), + ¬ifications::app_locale( + unsafe { required_string(locale, "locale") } + .as_deref() + .unwrap_or("en"), + ), )), error: Some(error), }), diff --git a/rust/src/services/notifications.rs b/rust/src/services/notifications.rs index 31fdc25b..c032c484 100644 --- a/rust/src/services/notifications.rs +++ b/rust/src/services/notifications.rs @@ -20,11 +20,14 @@ use std::sync::{Arc, LazyLock}; const MAX_BATCH_SIZE: i64 = 100; const EN_ARB: &str = include_str!("../../../lib/src/localization/translations/en.arb"); const DE_ARB: &str = include_str!("../../../lib/src/localization/translations/de.arb"); +const AR_ARB: &str = include_str!("../../../lib/src/localization/translations/ar.arb"); static EN_TRANSLATIONS: LazyLock> = LazyLock::new(|| parse_arb(EN_ARB, "en")); static DE_TRANSLATIONS: LazyLock> = LazyLock::new(|| parse_arb(DE_ARB, "de")); +static AR_TRANSLATIONS: LazyLock> = + LazyLock::new(|| parse_arb(AR_ARB, "ar")); /// The announcement a webxdc update carries, if it has one. /// @@ -529,6 +532,7 @@ pub async fn process_wakeup( ) -> Result { Context::init_notification(config).await?; let ctx = Context::get_static()?.clone(); + let locale = &app_locale(locale); // The FCM health check used to be fed by the Dart background isolate, which // no longer runs. Record the wake-up here so both platforms report it. @@ -760,16 +764,26 @@ fn parse_arb(source: &str, locale: &str) -> HashMap { .collect() } +/// The language picked in the app's settings, or the device's locale when the +/// user left it on the system default or the configuration is not readable. +pub fn app_locale(device_locale: &str) -> String { + Context::get_static() + .ok() + .and_then(|ctx| UserConfig::load_from(ctx).ok().flatten()) + .and_then(|user| user.language) + .unwrap_or_else(|| device_locale.to_owned()) +} + fn translation(locale: &str, key: &str) -> &'static str { let language = locale .split(['-', '_']) .next() .unwrap_or("en") .to_ascii_lowercase(); - let translations = if language == "de" { - &*DE_TRANSLATIONS - } else { - &*EN_TRANSLATIONS + let translations = match language.as_str() { + "de" => &*DE_TRANSLATIONS, + "ar" => &*AR_TRANSLATIONS, + _ => &*EN_TRANSLATIONS, }; translations .get(key) @@ -999,6 +1013,7 @@ mod tests { group.conversation_name = Some("Friends".into()); group.is_direct_chat = 0; assert_eq!(localized_body("en", &group), "sent a message in Friends."); + assert_eq!(localized_body("ar", &group), "رسالة جديدة في Friends."); let mut reaction = pending_row("reaction"); reaction.content = Some("🔥".into()); diff --git a/rust/src/user_config.rs b/rust/src/user_config.rs index 053f8249..035365af 100644 --- a/rust/src/user_config.rs +++ b/rust/src/user_config.rs @@ -217,6 +217,10 @@ pub struct UserConfig { pub theme_mode: ThemeMode, #[frb(non_final)] pub primary_color_value: Option, + /// Language code the app is shown in. `None` follows the device. + #[serde(default)] + #[frb(non_final)] + pub language: Option, #[frb(non_final)] pub default_show_time: Option, #[serde(default)] diff --git a/scripts/check_arb.py b/scripts/check_arb.py index a9ecc289..567378f4 100644 --- a/scripts/check_arb.py +++ b/scripts/check_arb.py @@ -4,7 +4,11 @@ import re import sys ARB_DIR = 'lib/src/localization/translations' -LIB_DIR = 'lib' +# (directory, file extension) pairs whose sources may reference ARB keys. +SOURCE_DIRS = [ + ('lib', '.dart'), + ('rust/src', '.rs'), +] def get_arb_data(filepath): try: @@ -34,30 +38,31 @@ def main(): en_keys = get_keys(en_data) print(f"Found {len(en_keys)} keys in en.arb") - # 1. Check for unused keys in Dart files - print("\n--- Checking for unused keys in Dart files ---") - all_dart_text = "" - for root, _, files in os.walk(LIB_DIR): - for file in files: - if file.endswith('.dart'): - filepath = os.path.join(root, file) - if "generated" in str(filepath): - continue - try: - with open(filepath, 'r', encoding='utf-8') as f: - all_dart_text += f.read() + "\n" - except Exception as e: - print(f"Could not read {filepath}: {e}") + # 1. Check for unused keys in Dart and Rust files + print("\n--- Checking for unused keys in Dart and Rust files ---") + all_source_text = "" + for source_dir, extension in SOURCE_DIRS: + for root, _, files in os.walk(source_dir): + for file in files: + if file.endswith(extension): + filepath = os.path.join(root, file) + if "generated" in str(filepath): + continue + try: + with open(filepath, 'r', encoding='utf-8') as f: + all_source_text += f.read() + "\n" + except Exception as e: + print(f"Could not read {filepath}: {e}") unused_keys = set() for key in sorted(en_keys): # Using word boundary to avoid partial matches pattern = r'\b' + re.escape(key) + r'\b' - if not re.search(pattern, all_dart_text): + if not re.search(pattern, all_source_text): unused_keys.add(key) if unused_keys: - print(f"Found {len(unused_keys)} keys in en.arb that do not appear to be used in the Dart code:") + print(f"Found {len(unused_keys)} keys in en.arb that do not appear to be used in the Dart or Rust code:") for k in sorted(unused_keys): print(f" - {k}") @@ -86,7 +91,7 @@ def main(): else: print("\nRun with --fix to automatically delete these unused keys.") else: - print("All keys in en.arb seem to be used in the Dart files.") + print("All keys in en.arb seem to be used in the Dart or Rust files.") # 2. Compare other .arb files with en.arb print("\n--- Comparing other .arb files with en.arb ---")