diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts
index ea0d351d..a2c313b3 100644
--- a/android/app/build.gradle.kts
+++ b/android/app/build.gradle.kts
@@ -65,7 +65,7 @@ android {
// (https://developer.android.com/studio/build/configure-apk-splits#configure-APK-versions)
// You can force using the value of versionCode by specifying the `-P force-version-code-ignoring-abi=true`
// flag during build.
- versionCode = 6
+ versionCode = 7
versionName = flutter.versionName
manifestPlaceholders["busymaxMsalRedirectHost"] = applicationId!!
manifestPlaceholders["busymaxMsalSignatureHash"] = msalSignatureHash
diff --git a/android/app/src/main/res/xml/backup_rules.xml b/android/app/src/main/res/xml/backup_rules.xml
index abf894d5..8fb34de8 100644
--- a/android/app/src/main/res/xml/backup_rules.xml
+++ b/android/app/src/main/res/xml/backup_rules.xml
@@ -2,5 +2,8 @@
+
+
+
diff --git a/android/app/src/main/res/xml/data_extraction_rules.xml b/android/app/src/main/res/xml/data_extraction_rules.xml
index 404dc1d3..776900fd 100644
--- a/android/app/src/main/res/xml/data_extraction_rules.xml
+++ b/android/app/src/main/res/xml/data_extraction_rules.xml
@@ -2,10 +2,16 @@
+
+
+
+
+
+
diff --git a/docs/android_release.md b/docs/android_release.md
index 1ea8265f..5572822a 100644
--- a/docs/android_release.md
+++ b/docs/android_release.md
@@ -9,6 +9,7 @@ For v0.3.0 the public version is `0.3.0` and the package code is `5`, advancing
the v0.2.4 source and retained test-signed artifact code `4`. During preparation
on 2026-10-11, the owner confirmed `4` is the highest distributed base code.
For v0.3.1, code `6` is confirmed available by the owner on 2026-10-11.
+For v0.3.2, the package code advances to `7`.
For subsequent releases, verify the distribution ledger and account for ABI
offsets if split APKs were distributed before choosing the next code.
diff --git a/docs/beta_snap_release.md b/docs/beta_snap_release.md
index 96ebcbaa..c9f853c0 100644
--- a/docs/beta_snap_release.md
+++ b/docs/beta_snap_release.md
@@ -57,7 +57,7 @@ explicit revision, and incorporate fetched `origin/main`.
```bash
tool/release/build_snap_local.sh \
- --version 0.3.1 \
+ --version 0.3.2 \
--revision "$(git rev-parse HEAD)" \
--google-oauth /path/to/google-desktop-download-directory \
--local-config config/release.local.json \
@@ -72,8 +72,8 @@ retained for inspection; choose a new path for another attempt. No reset,
forced worktree removal, or destructive cleanup is used.
The command checks all five version files before compiling; it never rewrites
-them. v0.3.1 uses Android code **6**, confirmed available by the owner on
-2026-10-11. It resolves the lockfile, generates/rechecks localization and Drift,
+them. v0.3.2 uses Android code **7**. It resolves the lockfile,
+generates/rechecks localization and Drift,
runs formatting, analysis, platform boundaries, full Flutter/tool/native tests,
and compiles a fresh configured bundle. It verifies the actual managed Google
configuration in `libapp.so`, runs canonical Snapcraft packaging with
@@ -97,11 +97,11 @@ owner's desktop or uploaded automatically.
Use the exact printed path throughout. In its artifacts directory:
```bash
-sha256sum --check busymax_0.3.1_amd64.snap.sha256
-unsquashfs -cat busymax_0.3.1_amd64.snap meta/snap.yaml
+sha256sum --check busymax_0.3.2_amd64.snap.sha256
+unsquashfs -cat busymax_0.3.2_amd64.snap meta/snap.yaml
```
-Confirm `0.3.1`, `core24`, `devel`, `strict`, the BusyMax launcher, its autostart
+Confirm `0.3.2`, `core24`, `devel`, `strict`, the BusyMax launcher, its autostart
entry, desktop file, icon and AppStream history. Investigate library warnings
using [the library investigation record](snap_runtime_libraries.md); do not add
broad linter suppression. The VM evidence must show no unresolved runtime ELF
@@ -113,7 +113,7 @@ preferences, contacts settings, conflicts and pending offline work. Back up
that test state. Quit BusyMax and upgrade using the exact candidate:
```bash
-sudo snap install --dangerous /absolute/path/busymax_0.3.1_amd64.snap
+sudo snap install --dangerous /absolute/path/busymax_0.3.2_amd64.snap
snap connections busymax
snap run busymax
```
@@ -141,7 +141,7 @@ only the verified checksum. The build command never publishes:
```bash
snapcraft login
snapcraft whoami
-snapcraft upload --release=beta /absolute/path/busymax_0.3.1_amd64.snap
+snapcraft upload --release=beta /absolute/path/busymax_0.3.2_amd64.snap
snapcraft revisions busymax --arch amd64
snapcraft status busymax --arch amd64
```
diff --git a/docs/desktop_oauth_release_checklist.md b/docs/desktop_oauth_release_checklist.md
index 9d4b27a5..2dd7219a 100644
--- a/docs/desktop_oauth_release_checklist.md
+++ b/docs/desktop_oauth_release_checklist.md
@@ -1,6 +1,6 @@
# Desktop OAuth release prerequisites
-v0.3.1 uses the owner's new Google Desktop client as the BusyMax-managed
+v0.3.2 uses the owner's new Google Desktop client as the BusyMax-managed
registration for locally built Linux releases. The download has a valid
`installed` object and project ID `busymax`. Its actual fields are read by the
[local release command](beta_snap_release.md); no credential JSON is committed.
diff --git a/docs/development.md b/docs/development.md
index 241c0d32..f975c21d 100644
--- a/docs/development.md
+++ b/docs/development.md
@@ -177,8 +177,8 @@ their regression coverage.
## Release branches
-Start `release/v0.3.1` from current `main`, commit reviewed changes, and open it
-into `main` with title `release/v0.3.1`. Fetch before final review and incorporate
+Start `release/v0.3.2` from current `main`, commit reviewed changes, and open it
+into `main` with title `release/v0.3.2`. Fetch before final review and incorporate
later `origin/main` commits with a normal merge. The existing
`tool/check_release_pull_request.py` validates the actual PR head, release
metadata and current-main ancestry. A synthetic PR merge does not establish
diff --git a/docs/icalendar_data_model.md b/docs/icalendar_data_model.md
index cd1fa9bb..9804d31d 100644
--- a/docs/icalendar_data_model.md
+++ b/docs/icalendar_data_model.md
@@ -45,6 +45,10 @@ Status, percentage, and completion time change as one state transition.
Completing a parent completes open descendants; reopening a descendant reopens
a closed ancestor. Completing a recurring master records its completed
instance and advances the master when another occurrence exists.
+When an UNTIL rule is exhausted, the wire master remains intact for Nextcloud
+compatibility. Its matching completed history determines the local completed
+state. Explicit reopening updates that same history component; completing it
+again retains its recurrence identity.
The editor supports the daily, weekly, monthly, and yearly recurrence subset
represented by its controls, including supported intervals, selectors, counts,
diff --git a/docs/snap_runtime_libraries.md b/docs/snap_runtime_libraries.md
index 06c9b6c1..2025889f 100644
--- a/docs/snap_runtime_libraries.md
+++ b/docs/snap_runtime_libraries.md
@@ -3,8 +3,9 @@
Checked 2026-10-11 with Snapcraft 9.1.4, core24 revision 2124,
GNOME 46-2404 revision 168 and mesa-2404 revision 1839. The installed BusyMax
v0.3.0 revision x3 was used only to establish runtime provenance; it is not
-v0.3.1 acceptance evidence. The local release command repeats resolution in a
-fresh Ubuntu 24.04 VM against the exact new package and its checksum.
+acceptance evidence for a later release. The local release command repeats
+resolution in a fresh Ubuntu 24.04 VM against the exact new package and its
+checksum.
Inside `snap run --shell busymax`, `ldd` on the runner, Flutter engine and
libgweather resolved the reported libraries as follows:
diff --git a/integration_test/accounts_settings_linux_test.dart b/integration_test/accounts_settings_linux_test.dart
new file mode 100644
index 00000000..61535df6
--- /dev/null
+++ b/integration_test/accounts_settings_linux_test.dart
@@ -0,0 +1,1087 @@
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+import 'dart:ui' show PointerDeviceKind;
+
+import 'package:busymax/src/app/app_bootstrap.dart';
+import 'package:busystack_contacts/busystack_contacts.dart';
+import 'package:busymax/src/app/busymax_yaru_theme.dart';
+import 'package:busymax/src/app/busymax_design.dart';
+import 'package:busymax/src/app/linux/linux_window_host.dart';
+import 'package:busymax/l10n/generated/app_localizations.dart';
+import 'package:busymax/src/core/secrets/secret_store.dart';
+import 'package:busymax/src/db/app_database.dart';
+import 'package:busymax/src/dav/dav_errors.dart';
+import 'package:busymax/src/dav/storage/dav_settings_repository.dart';
+import 'package:busymax/src/google_calendar/google_calendar_api_client.dart';
+import 'package:busymax/src/features/sync/calendar_sync_engine.dart';
+import 'package:busymax/src/features/sync/pending_op_resolution_service.dart';
+import 'package:busymax/src/features/accounts/domain/account_connection_state.dart';
+import 'package:busymax/src/demo/demo_profile.dart';
+import 'package:busymax/src/features/calendar/data/calendar_repository.dart';
+import 'package:busymax/src/calendar_providers/calendar_sync_dto.dart';
+import 'package:busymax/src/calendar_providers/calendar_colors.dart';
+import 'package:busymax/src/features/settings/presentation/settings_screen.dart';
+import 'package:busymax/src/platform/gtk_font_service.dart';
+import 'package:busymax/src/platform/gtk_header_icon_service.dart';
+import 'package:busymax/src/platform/gtk_window_preferences_service.dart';
+import 'package:busymax/src/providers/busy_provider.dart';
+import 'package:busymax/src/microsoft_calendar/microsoft_shared_calendar_address.dart';
+import 'package:drift/drift.dart' show Value, Variable;
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:integration_test/integration_test.dart';
+import 'package:http/http.dart' as http;
+import 'package:http/testing.dart';
+import 'package:yaru/yaru.dart';
+import 'package:ubuntu_localizations/ubuntu_localizations.dart';
+
+import '../test/support/contacts_fixture.dart';
+import '../test/support/dav_contacts_fixture.dart' as carddav;
+import '../test/support/desktop_registration_config.dart';
+
+// Explicit opt-in. Every account, provider response, secret and database below
+// is synthetic; the owner's profile, keyring and providers are never opened.
+void main() {
+ IntegrationTestWidgetsFlutterBinding.ensureInitialized();
+ final output = Platform.environment['BUSYMAX_ACCEPTANCE_ROOT'];
+ testWidgets(
+ 'native Accounts supports multiple identities, themes, narrow windows, keyboard and safe confirmation',
+ (tester) async {
+ expect(Platform.environment['XDG_DATA_HOME'], '$output/data');
+ final semantics = tester.ensureSemantics();
+ await tester.runAsync(YaruWindowTitleBar.ensureInitialized);
+ final f = ContactsFixture(AppDatabase.memoryForTests(), realDav: true);
+ final davContacts = carddav.DavFixture();
+ f.respondDav = davContacts.respond;
+ final secrets = InMemorySecretStore();
+ final icons = GtkHeaderIconService();
+ await tester.runAsync(icons.initialize);
+ final windowPreferences = await tester.runAsync(
+ () => const GtkWindowPreferencesService().load(),
+ );
+ await tester.runAsync(() async {
+ for (final provider in [
+ BusyProvider.google,
+ BusyProvider.microsoft,
+ BusyProvider.nextcloud,
+ ]) {
+ for (var number = 1; number <= 2; number++) {
+ final id = '${provider.name}:qa-$number';
+ await f.accounts.upsertSignedInAccount(
+ id: id,
+ provider: provider,
+ authority: provider == BusyProvider.nextcloud
+ ? 'https://cloud$number.example.test/team'
+ : null,
+ providerAccountId: provider == BusyProvider.nextcloud
+ ? 'qa-user'
+ : 'qa-$number',
+ displayName:
+ 'Quality Assurance $number — a deliberately long synthetic account identity',
+ email: provider == BusyProvider.nextcloud
+ ? null
+ : 'qa-$number@example.test',
+ grantedScopes: '',
+ );
+ if (provider != BusyProvider.nextcloud) {
+ await f.database.customStatement(
+ 'INSERT INTO domain_sync_schedules(account_id,domain,last_successful_pull_utc) VALUES (?, ?, ?)',
+ [id, 'tasks', '2026-10-19T12:00:00Z'],
+ );
+ }
+ if (provider == BusyProvider.nextcloud) {
+ await secrets.saveCredential(
+ id,
+ NextcloudSecretRecord(
+ canonicalServer: Uri.parse(
+ 'https://cloud$number.example.test/team',
+ ),
+ loginName: 'qa-user',
+ appPassword: 'synthetic-only',
+ ),
+ );
+ await f.database
+ .into(f.database.davAccountServices)
+ .insert(
+ DavAccountServicesCompanion.insert(
+ accountId: id,
+ canonicalServiceUri:
+ 'https://cloud$number.example.test/team/remote.php/dav/',
+ canonicalOrigin: 'https://cloud$number.example.test',
+ discoveredAtUtc: '2026-10-19T12:00:00Z',
+ ),
+ );
+ if (number == 1) {
+ for (final mask in [1, 2]) {
+ await f.database
+ .into(f.database.davCollections)
+ .insert(
+ DavCollectionsCompanion.insert(
+ id: '$id:single-$mask',
+ accountId: id,
+ hrefKey: '/team/single-$mask/',
+ requestUri:
+ 'https://cloud$number.example.test/team/single-$mask/',
+ displayName: mask == 1
+ ? 'Personal appointments'
+ : 'Personal tasks',
+ resourceTypesJson: const Value(
+ '["{DAV:}collection","{urn:ietf:params:xml:ns:caldav}calendar"]',
+ ),
+ supportedComponentMask: Value(mask),
+ eventProjectionEnabled: Value(mask == 1),
+ taskProjectionEnabled: Value(mask == 2),
+ currentUserPrivilegesJson: const Value(
+ '["{DAV:}read","{DAV:}write-properties"]',
+ ),
+ createdAtUtc: '2026-10-19T12:00:00Z',
+ updatedAtUtc: '2026-10-19T12:00:00Z',
+ ),
+ );
+ }
+ }
+ await f.database
+ .into(f.database.davCollections)
+ .insert(
+ DavCollectionsCompanion.insert(
+ id: '$id:collection',
+ accountId: id,
+ hrefKey: '/team/calendar/',
+ requestUri:
+ 'https://cloud$number.example.test/team/calendar/',
+ displayName: 'Team events and tasks $number',
+ resourceTypesJson: const Value(
+ '["{DAV:}collection","{urn:ietf:params:xml:ns:caldav}calendar"]',
+ ),
+ supportedComponentMask: const Value(3),
+ eventProjectionEnabled: const Value(true),
+ taskProjectionEnabled: const Value(true),
+ currentUserPrivilegesJson: const Value(
+ '["{DAV:}read","{DAV:}write-properties"]',
+ ),
+ createdAtUtc: '2026-10-19T12:00:00Z',
+ updatedAtUtc: '2026-10-19T12:00:00Z',
+ ),
+ );
+ } else {
+ await CalendarRepository(database: f.database).upsertSource(
+ accountId: id,
+ source: CalendarSourceDto(
+ provider: provider,
+ providerCalendarId: provider == BusyProvider.microsoft
+ ? MicrosoftSharedPrimaryCalendarAddress(
+ owner: 'owner-$number@example.test',
+ graphCalendarId: 'qa',
+ ).sourceCalendarId
+ : 'qa-$number',
+ summary: provider == BusyProvider.google
+ ? 'QA calendar $number'
+ : 'Shared primary QA calendar $number',
+ primaryCalendar:
+ provider == BusyProvider.google && number == 1,
+ readOnly: provider == BusyProvider.microsoft,
+ accessRole: provider == BusyProvider.microsoft
+ ? 'unavailable'
+ : 'owner',
+ rawJson: const {'hidden': false},
+ ),
+ );
+ if (provider == BusyProvider.google && number == 2) {
+ await CalendarRepository(database: f.database).setSourceColor(
+ CalendarRepository.sourceId(
+ accountId: id,
+ provider: provider,
+ providerCalendarId: 'qa-$number',
+ ),
+ calendarColorChoices(provider).last,
+ );
+ await CalendarRepository(database: f.database)
+ .setSourceProviderHidden(
+ CalendarRepository.sourceId(
+ accountId: id,
+ provider: provider,
+ providerCalendarId: 'qa-$number',
+ ),
+ true,
+ );
+ }
+ }
+ }
+ }
+ f.davSecrets['independent-qa'] = jsonEncode({
+ 'version': 1,
+ 'server': 'https://contacts.example.test',
+ 'username': 'independent-qa',
+ 'password': 'synthetic-only',
+ 'readOnly': true,
+ });
+ await f.store.write(
+ (tx) => tx.putAccount(
+ const ContactAccount(
+ id: 'independent-qa',
+ provider: ContactProviderKind.carddav,
+ displayName: 'Independent QA address book',
+ subject: 'independent-qa',
+ enabled: false,
+ grantedScopes: {'carddav:read'},
+ ),
+ ),
+ );
+ });
+ final calendar = CalendarRepository(database: f.database);
+ final revocations = [];
+ final appClient = MockClient((request) async {
+ expect(request.method, 'DELETE');
+ expect(
+ request.url.toString(),
+ 'https://cloud2.example.test/team/ocs/v2.php/core/apppassword',
+ );
+ revocations.add(request);
+ return http.Response('', 204);
+ });
+ final davSettings = DavSettingsRepository(database: f.database);
+ Completer? inventoryGate, syncEntered, syncGate;
+ var googleDenied = true;
+ final patches = [];
+ final googleClient = GoogleCalendarApiClient(
+ baseUri: Uri.parse('https://www.googleapis.com'),
+ httpClient: MockClient((request) async {
+ expect(request.method, 'PATCH');
+ expect(request.url.path, '/calendar/v3/users/me/calendarList/qa-2');
+ patches.add(request);
+ return googleDenied
+ ? http.Response('{"error":{"code":403}}', 403)
+ : http.Response(
+ jsonEncode({
+ 'id': 'qa-2',
+ 'summary': 'QA calendar 2',
+ 'hidden': jsonDecode(request.body)['hidden'],
+ 'backgroundColor': '#9a9cff',
+ 'foregroundColor': '#000000',
+ 'colorId': '1',
+ }),
+ 200,
+ );
+ }),
+ );
+ final googleEngine = CalendarSyncEngine(
+ database: f.database,
+ client: googleClient,
+ accountId: 'google:qa-2',
+ );
+ final googleRecovery = PendingOpResolutionService(
+ database: f.database,
+ accountId: 'google:qa-2',
+ syncTasks: () async =>
+ fail('Calendar recovery must not dispatch Tasks'),
+ syncCalendar: googleEngine.dispatchPendingWrites,
+ );
+ late final ProviderContainer container;
+ container = ProviderContainer(
+ overrides: [
+ databaseProvider.overrideWithValue(f.database),
+ baseHttpClientProvider.overrideWithValue(appClient),
+ gtkHeaderIconServiceProvider.overrideWithValue(icons),
+ initialGtkWindowPreferencesProvider.overrideWithValue(
+ windowPreferences,
+ ),
+ secretStoreProvider.overrideWithValue(secrets),
+ busyMaxContactsStoreProvider.overrideWithValue(f.store),
+ busyMaxContactsControllerProvider.overrideWithValue(f.controller),
+ davSettingsRepositoryProvider.overrideWithValue(davSettings),
+ davCollectionsStreamProvider.overrideWith((ref) async* {
+ await inventoryGate?.future;
+ yield* davSettings.watchCollections();
+ }),
+ pendingOpResolutionServiceForAccountProvider.overrideWith((ref, id) {
+ expect(id, 'google:qa-2');
+ return googleRecovery;
+ }),
+ localSettingsStoreProvider.overrideWithValue(
+ InMemoryLocalSettingsStore(),
+ ),
+ buildConfigProvider.overrideWithValue(syntheticDesktopConfig()),
+ selectedAccountIdProvider.overrideWith((ref) => 'google:qa-1'),
+ signedInSyncRunnerProvider.overrideWithValue((id, _) async {
+ await container.read(accountSyncActivityProvider).run(
+ id,
+ 'dav',
+ () async {
+ syncEntered?.complete();
+ await syncGate?.future;
+ if (id.startsWith('nextcloud:')) {
+ await f.accounts.setConnectionState(
+ id,
+ AccountConnectionState.connected,
+ );
+ await f.database.customUpdate(
+ 'UPDATE dav_account_services SET last_discovery_error_code = NULL WHERE account_id = ?',
+ variables: [Variable(id)],
+ updates: {f.database.davAccountServices},
+ );
+ }
+ },
+ );
+ }),
+ ],
+ );
+ final font = await tester.runAsync(
+ () => const GtkFontService().getGtkFont(),
+ );
+ final textScale = ValueNotifier(1);
+ for (final brightness in [Brightness.light, Brightness.dark]) {
+ await tester.runAsync(
+ () => const GtkThemeService().setPreferDark(
+ brightness == Brightness.dark,
+ ),
+ );
+ await tester.pumpWidget(
+ UncontrolledProviderScope(
+ container: container,
+ child: MaterialApp(
+ debugShowCheckedModeBanner: false,
+ locale: const Locale('en'),
+ localizationsDelegates: const [
+ ...AppLocalizations.localizationsDelegates,
+ ...GlobalUbuntuLocalizations.delegates,
+ ],
+ supportedLocales: AppLocalizations.supportedLocales,
+ theme: BusyMaxYaruTheme.build(
+ brightness: brightness,
+ accentColor: YaruColors.orange,
+ gtkFontFamily: font?.family,
+ gtkFontSize: font?.size,
+ ),
+ builder: (context, child) => ValueListenableBuilder(
+ valueListenable: textScale,
+ child: GtkHeaderIconScope(
+ service: icons,
+ child: LinuxWindowHost(child: child!),
+ ),
+ builder: (context, scale, child) => MediaQuery(
+ data: MediaQuery.of(context)
+ .copyWith(textScaler: TextScaler.linear(scale)),
+ child: child!,
+ ),
+ ),
+ home: const SettingsScreen(initialPage: SettingsPage.accounts),
+ ),
+ ),
+ );
+ await _resize(tester, 1200, 1000);
+ await tester.pumpAndSettle();
+ final theme = brightness.name;
+ for (final provider in ['google', 'microsoft', 'nextcloud']) {
+ for (final number in [1, 2]) {
+ expect(
+ find.byKey(ValueKey('account-entry-$provider:qa-$number')),
+ findsOneWidget,
+ );
+ }
+ }
+ const additions = {
+ 'Add Google account',
+ 'Add Microsoft account',
+ 'Add Nextcloud account',
+ 'Add Apple iCloud Calendar account',
+ };
+ for (final label in additions) {
+ expect(find.text(label), findsOneWidget);
+ }
+ expect(find.text('Personal appointments'), findsNothing);
+ tester
+ .widget(find.byType(SingleChildScrollView))
+ .controller!
+ .jumpTo(0);
+ await _capture(tester, '$output/$theme-overview.png');
+ final origin = find.byKey(
+ const ValueKey('account-entry-nextcloud:qa-1'),
+ );
+ await tester.ensureVisible(origin);
+ final originFocus = tester
+ .widget(
+ find.descendant(
+ of: origin,
+ matching: find.byType(BusyMaxActionRow),
+ ),
+ )
+ .focusNode!;
+ final overviewScroll = tester
+ .widget(find.byType(SingleChildScrollView))
+ .controller!;
+ final originOffset = overviewScroll.offset;
+ await tester.tap(origin);
+ await tester.pumpAndSettle();
+ expect(
+ find.textContaining('qa-user · https://cloud1.example.test/team'),
+ findsOneWidget,
+ );
+ expect(
+ find.byKey(const ValueKey('remove-account-nextcloud:qa-2')),
+ findsNothing,
+ );
+ await _capture(tester, '$output/$theme-nextcloud-healthy.png');
+ await _tap(tester, find.text('Personal appointments'));
+ expect(
+ (await tester.runAsync(
+ () => davSettings.collectionById('nextcloud:qa-1:single-1'),
+ ))!.eventsSelected,
+ isFalse,
+ );
+ await _tap(tester, find.text('Team events and tasks 1'));
+ expect(
+ (await tester.runAsync(
+ () => davSettings.collectionById('nextcloud:qa-1:collection'),
+ ))!.tasksSelected,
+ isTrue,
+ );
+ await _tap(
+ tester,
+ find.byKey(
+ const ValueKey('dav-tasks-toggle-nextcloud:qa-1:collection'),
+ ),
+ );
+ expect(
+ (await tester.runAsync(
+ () => davSettings.collectionById('nextcloud:qa-1:collection'),
+ ))!.tasksSelected,
+ isFalse,
+ );
+ expect(
+ (await tester.runAsync(
+ () => davSettings.collectionById('nextcloud:qa-2:collection'),
+ ))!.tasksSelected,
+ isTrue,
+ );
+ await _tap(tester, find.text('Team events and tasks 1'));
+ await tester.runAsync(() async {
+ await davSettings.setEventsSelected('nextcloud:qa-1:single-1', true);
+ await davSettings.setTasksSelected('nextcloud:qa-1:collection', true);
+ });
+ inventoryGate = Completer();
+ container.invalidate(davCollectionsStreamProvider);
+ await tester.pump();
+ await tester.pump();
+ expect(find.text('Loading collections…'), findsWidgets);
+ await _capture(tester, '$output/$theme-loading.png', settle: false);
+ inventoryGate.complete();
+ inventoryGate = null;
+ await tester.pumpAndSettle();
+ await _networkFailure(tester, container, f);
+ expect(find.text('Could not reach the service'), findsOneWidget);
+ expect(find.textContaining('DavException'), findsNothing);
+ expect(find.textContaining('synthetic-correlation'), findsNothing);
+ await _capture(tester, '$output/$theme-nextcloud-network-error.png');
+ await _resize(tester, 640, 700);
+ await _capture(tester, '$output/$theme-narrow.png');
+ await _resize(tester, 640, 500);
+ await _capture(tester, '$output/$theme-short.png');
+ textScale.value = 1.6;
+ await tester.pumpAndSettle();
+ await _capture(tester, '$output/$theme-enlarged.png');
+ textScale.value = 1;
+ await tester.pumpAndSettle();
+ await _resize(tester, 1200, 1000);
+ syncEntered = Completer();
+ syncGate = Completer();
+ await tester.tap(
+ find.byKey(const ValueKey('account-recovery-nextcloud:qa-1')),
+ );
+ await tester.pump();
+ await tester.runAsync(() => syncEntered!.future);
+ await _back(tester);
+ expect(originFocus.hasFocus, isTrue);
+ expect(overviewScroll.offset, closeTo(originOffset, 1));
+ await tester.sendKeyEvent(LogicalKeyboardKey.enter);
+ await tester.pumpAndSettle();
+ expect(
+ find.byKey(const ValueKey('account-detail-nextcloud:qa-1')),
+ findsOneWidget,
+ );
+ await _back(tester);
+ await _open(tester, 'nextcloud:qa-2');
+ syncGate.complete();
+ await tester.pumpAndSettle();
+ syncGate = syncEntered = null;
+ expect(
+ find.byKey(const ValueKey('account-detail-nextcloud:qa-2')),
+ findsOneWidget,
+ );
+ final secondCollection = (await tester.runAsync(
+ () =>
+ (f.database.select(f.database.davCollections)
+ ..where((r) => r.id.equals('nextcloud:qa-2:collection')))
+ .getSingle(),
+ ))!;
+ await tester.runAsync(
+ () => (f.database.delete(
+ f.database.davCollections,
+ )..where((r) => r.id.equals(secondCollection.id))).go(),
+ );
+ await tester.pumpAndSettle();
+ expect(find.text('No collections found.'), findsOneWidget);
+ await tester.runAsync(() async {
+ await f.database
+ .into(f.database.davCollections)
+ .insert(secondCollection);
+ await f.accounts.setConnectionState(
+ 'nextcloud:qa-2',
+ AccountConnectionState.reauthenticationRequired,
+ );
+ });
+ await tester.pumpAndSettle();
+ await _tap(
+ tester,
+ find.byKey(const ValueKey('account-recovery-nextcloud:qa-2')),
+ );
+ await _tap(tester, find.text('Cancel').last);
+ await tester.runAsync(
+ () => f.accounts.setConnectionState(
+ 'nextcloud:qa-2',
+ AccountConnectionState.connected,
+ ),
+ );
+ await tester.pumpAndSettle();
+ await _back(tester);
+
+ await _open(tester, 'google:qa-2');
+ final sourceId = CalendarRepository.sourceId(
+ accountId: 'google:qa-2',
+ provider: BusyProvider.google,
+ providerCalendarId: 'qa-2',
+ );
+ await tester.runAsync(() async {
+ await calendar.setSourceColor(
+ sourceId,
+ calendarColorChoices(BusyProvider.google).last,
+ );
+ await calendar.setSourceProviderHidden(sourceId, true);
+ });
+ await tester.pumpAndSettle();
+ expect(find.text('Google visibility change pending'), findsOneWidget);
+ await _tap(tester, find.text('QA calendar 2'));
+ googleDenied = true;
+ await tester.runAsync(googleEngine.dispatchPendingWrites);
+ await tester.pumpAndSettle();
+ final retry = find.byKey(
+ ValueKey('settings-calendar-visibility-retry-$sourceId'),
+ );
+ final discard = find.byKey(
+ ValueKey('settings-calendar-visibility-discard-$sourceId'),
+ );
+ expect(tester.getSemantics(retry).label, contains('QA calendar 2'));
+ await _capture(tester, '$output/$theme-google-visibility-recovery.png');
+ await _tap(tester, discard);
+ expect(
+ find.textContaining(
+ '• Calendar color\n• Show in Google’s calendar list',
+ ),
+ findsOneWidget,
+ );
+ expect(find.textContaining('Google Tasks'), findsNothing);
+ await _capture(tester, '$output/$theme-google-discard.png');
+ await _tap(tester, find.text('Cancel').last);
+ googleDenied = false;
+ final beforeRetry = patches.length;
+ await _tap(tester, retry);
+ await _waitForCalendarQueue(tester, f.database);
+ expect(patches.length, beforeRetry + 1);
+ expect(
+ (await tester.runAsync(
+ () => f.database.select(f.database.pendingOps).get(),
+ ))!,
+ isEmpty,
+ );
+ await tester.runAsync(() async {
+ await calendar.setSourceProviderHidden(sourceId, false);
+ await googleEngine.dispatchPendingWrites();
+ await calendar.setSourceColor(
+ sourceId,
+ calendarColorChoices(BusyProvider.google).first,
+ );
+ await calendar.setSourceProviderHidden(sourceId, true);
+ googleDenied = true;
+ await googleEngine.dispatchPendingWrites();
+ });
+ await tester.pumpAndSettle();
+ await _tap(tester, discard);
+ await _tap(tester, find.text('Discard').last);
+ await _waitForCalendarQueue(tester, f.database);
+ expect(
+ (await tester.runAsync(
+ () => f.database.select(f.database.pendingOps).get(),
+ ))!,
+ isEmpty,
+ );
+ final local = find.byKey(
+ ValueKey('settings-calendar-schedule-$sourceId'),
+ );
+ expect(tester.widget(local).onChanged, isNotNull);
+ await _tap(tester, local);
+ expect(
+ (await tester.runAsync(
+ () => calendar.watchSourcesForAccounts(['google:qa-2']).first,
+ ))!.single.selected,
+ isFalse,
+ );
+ await tester.runAsync(() => calendar.setSourceSelected(sourceId, true));
+ await _back(tester);
+
+ await _open(tester, 'google:qa-1');
+ await _tap(tester, find.text('QA calendar 1'));
+ final primaryId = CalendarRepository.sourceId(
+ accountId: 'google:qa-1',
+ provider: BusyProvider.google,
+ providerCalendarId: 'qa-1',
+ );
+ expect(
+ tester
+ .widget(
+ find.byKey(ValueKey('settings-calendar-provider-$primaryId')),
+ )
+ .onChanged,
+ isNull,
+ );
+ expect(
+ find.text('Google’s primary calendar stays in its calendar list.'),
+ findsOneWidget,
+ );
+ if (brightness == Brightness.light) {
+ f.consentSubject = 'qa-1';
+ await tester.runAsync(
+ () => f.controller.enableLinkedContacts('google:qa-1'),
+ );
+ await tester.runAsync(
+ () => f.controller.synchronizeAccount('contacts:google:qa-1'),
+ );
+ await tester.pumpAndSettle();
+ }
+ final source = (await tester.runAsync(f.controller.sourceSettings))!
+ .firstWhere((s) => s.source.accountId == 'contacts:google:qa-1');
+ final sourceSwitch = find.byKey(
+ ValueKey('contact-source-${source.source.key}'),
+ );
+ await _tap(tester, sourceSwitch);
+ expect(
+ (await tester.runAsync(f.controller.sourceSettings))!
+ .firstWhere((s) => s.source.key == source.source.key)
+ .enabled,
+ isFalse,
+ );
+ await _tap(tester, sourceSwitch);
+ await _expand(
+ tester,
+ find.byKey(const ValueKey('account-advanced-google:qa-1')),
+ );
+ await _capture(tester, '$output/$theme-google-advanced.png');
+ await _back(tester);
+
+ await _open(tester, 'microsoft:qa-1');
+ await tester.runAsync(() async {
+ await f.accounts.setConnectionState(
+ 'microsoft:qa-1',
+ AccountConnectionState.temporarilyUnavailable,
+ );
+ try {
+ await container.read(accountSyncActivityProvider).run(
+ 'microsoft:qa-1',
+ 'calendar',
+ () async {
+ throw const DavException(
+ kind: DavErrorKind.network,
+ code: 'DavNetworkFailure',
+ safeMessage: 'Synthetic diagnostic',
+ );
+ },
+ );
+ } on DavException {
+ /* Calendar failed independently of Tasks. */
+ }
+ await container
+ .read(accountSyncActivityProvider)
+ .run('microsoft:qa-1', 'tasks', () async {});
+ });
+ await tester.pumpAndSettle();
+ expect(
+ find.byKey(
+ const ValueKey('account-domain-feedback-calendar-microsoft:qa-1'),
+ ),
+ findsOneWidget,
+ );
+ expect(
+ find.byKey(
+ const ValueKey('account-domain-feedback-tasks-microsoft:qa-1'),
+ ),
+ findsNothing,
+ );
+ expect(
+ find.textContaining('Access unavailable; showing cached data.'),
+ findsWidgets,
+ );
+ await _capture(tester, '$output/$theme-microsoft-unavailable.png');
+ // Opening a new shared calendar retains the existing connection-state
+ // eligibility rule. Keep the failed Calendar outcome visible while
+ // restoring the synthetic account's connection for this dialog check.
+ await tester.runAsync(
+ () => f.accounts.setConnectionState(
+ 'microsoft:qa-1',
+ AccountConnectionState.connected,
+ ),
+ );
+ await tester.pumpAndSettle();
+ await _tap(tester, find.text('Open shared primary calendar'));
+ expect(
+ find.textContaining('Opens the specified owner’s primary calendar.'),
+ findsOneWidget,
+ );
+ await _tap(tester, find.text('Cancel').last);
+ await _back(tester);
+
+ await _tap(
+ tester,
+ find.byKey(const ValueKey('contact-account-entry-independent-qa')),
+ );
+ expect(find.text('https://contacts.example.test'), findsOneWidget);
+ if (brightness == Brightness.light) {
+ final contactReady = Completer();
+ final subscription = f.controller.changes.listen((_) async {
+ final sources = await f.controller.sourceSettings();
+ if (!contactReady.isCompleted &&
+ sources.any((s) => s.source.accountId == 'independent-qa') &&
+ f.davSecrets['independent-qa']!.contains(
+ 'synthetic-replacement-only',
+ )) {
+ contactReady.complete();
+ }
+ });
+ await _tap(
+ tester,
+ find.byKey(
+ const ValueKey('contact-account-recovery-independent-qa'),
+ ),
+ );
+ await tester.enterText(
+ find.byType(TextField).last,
+ 'synthetic-replacement-only',
+ );
+ await tester.pumpAndSettle();
+ final reconnectButton = find.descendant(
+ of: find.byType(BusyMaxPromptDialog),
+ matching: find.widgetWithText(
+ ElevatedButton,
+ 'Reconnect this account',
+ ),
+ );
+ expect(
+ tester.widget(reconnectButton).onPressed,
+ isNotNull,
+ );
+ await _tap(tester, reconnectButton);
+ expect(find.byType(BusyMaxPromptDialog), findsNothing);
+ await tester.runAsync(
+ () => contactReady.future.timeout(const Duration(seconds: 10)),
+ );
+ await subscription.cancel();
+ await tester.runAsync(
+ () => f.controller.synchronizeAccount('independent-qa'),
+ );
+ await tester.pumpAndSettle();
+ expect(
+ f.davSecrets['independent-qa'],
+ contains('synthetic-replacement-only'),
+ );
+ expect(davContacts.requests, isNotEmpty);
+ expect(
+ davContacts.requests.where(
+ (r) => ['PUT', 'DELETE', 'PROPPATCH'].contains(r.method),
+ ),
+ isEmpty,
+ );
+ }
+ await _capture(tester, '$output/$theme-independent-contacts.png');
+ final contactRemoval = find.byKey(
+ const ValueKey('remove-contacts-only-independent-qa'),
+ );
+ await tester.ensureVisible(contactRemoval);
+ FocusManager.instance.primaryFocus?.unfocus();
+ final pointer = await tester.createGesture(
+ kind: PointerDeviceKind.mouse,
+ );
+ await pointer.addPointer(location: Offset.zero);
+ await _capture(tester, '$output/$theme-independent-remove-normal.png');
+ await pointer.moveTo(tester.getCenter(contactRemoval));
+ await _capture(tester, '$output/$theme-independent-remove-hover.png');
+ await pointer.moveTo(Offset.zero);
+ final removeLabel = find.descendant(
+ of: contactRemoval,
+ matching: find.text('Remove'),
+ );
+ Focus.of(tester.element(removeLabel)).requestFocus();
+ await tester.sendKeyEvent(LogicalKeyboardKey.tab);
+ await tester.sendKeyDownEvent(LogicalKeyboardKey.shiftLeft);
+ await tester.sendKeyEvent(LogicalKeyboardKey.tab);
+ await tester.sendKeyUpEvent(LogicalKeyboardKey.shiftLeft);
+ await tester.pumpAndSettle();
+ expect(Focus.of(tester.element(removeLabel)).hasFocus, isTrue);
+ await _capture(tester, '$output/$theme-independent-remove-focus.png');
+ await tester.sendKeyEvent(LogicalKeyboardKey.enter);
+ // The removal operation remains busy while confirmation is open.
+ await tester.pump();
+ await tester.pump(const Duration(milliseconds: 300));
+ expect(find.byType(BusyMaxConfirmDialog), findsOneWidget);
+ await _tap(tester, find.text('Cancel'));
+ expect(
+ await tester.runAsync(
+ () => f.store.read((tx) => tx.account('independent-qa')),
+ ),
+ isNotNull,
+ );
+ await pointer.removePointer();
+ await _back(tester);
+ await _open(tester, 'nextcloud:qa-2');
+ await tester.runAsync(
+ () => f.database.customStatement(
+ "CREATE TEMP TRIGGER reject_qa_removal BEFORE DELETE ON accounts WHEN OLD.id = 'nextcloud:qa-2' BEGIN SELECT RAISE(ABORT, 'synthetic deletion failure'); END",
+ ),
+ );
+ await _tap(
+ tester,
+ find.byKey(const ValueKey('remove-account-nextcloud:qa-2')),
+ );
+ await _capture(tester, '$output/$theme-remove-confirmation.png');
+ await _tap(tester, find.byKey(const Key('confirm-account-removal')));
+ await tester.runAsync(() async {
+ // Join the service's guarded removal while local deletion is blocked
+ // by the fixture trigger, then wait for restoration to finish.
+ await expectLater(
+ container
+ .read(davAccountOnboardingServiceProvider)
+ .removeAccount('nextcloud:qa-2'),
+ throwsA(isNotNull),
+ );
+ });
+ await tester.pumpAndSettle();
+ expect(
+ find.text('Could not finish removing the account. Try again.'),
+ findsOneWidget,
+ );
+ expect(
+ find.byKey(const ValueKey('account-detail-nextcloud:qa-2')),
+ findsOneWidget,
+ );
+ expect(
+ await tester.runAsync(() => f.accounts.accountById('nextcloud:qa-2')),
+ isNotNull,
+ );
+ expect(
+ await tester.runAsync(() => secrets.readCredential('nextcloud:qa-2')),
+ isNotNull,
+ );
+ await tester.runAsync(
+ () => f.database.customStatement('DROP TRIGGER reject_qa_removal'),
+ );
+ await _back(tester);
+ await tester.ensureVisible(find.text('Add Nextcloud account'));
+ Focus.of(tester.element(find.text('Add Nextcloud account')))
+ .requestFocus();
+ await tester.pumpAndSettle();
+ final visited = {'Add Nextcloud account'};
+ for (
+ var step = 0;
+ step < 28 && !visited.containsAll(additions);
+ step++
+ ) {
+ await tester.sendKeyEvent(LogicalKeyboardKey.tab);
+ await tester.pumpAndSettle();
+ final title = FocusManager.instance.primaryFocus?.context
+ ?.findAncestorWidgetOfExactType()
+ ?.title;
+ if (title != null) visited.add(title);
+ }
+ expect(visited, containsAll(additions));
+ await _capture(tester, '$output/$theme-keyboard-focus.png');
+ expect(tester.takeException(), isNull);
+ }
+ await _open(tester, 'nextcloud:qa-2');
+ await _tap(
+ tester,
+ find.byKey(const ValueKey('remove-account-nextcloud:qa-2')),
+ );
+ await _tap(tester, find.byKey(const Key('confirm-account-removal')));
+ await tester.runAsync(
+ () =>
+ (f.database.select(f.database.accounts)
+ ..where((r) => r.id.equals('nextcloud:qa-2')))
+ .watch()
+ .firstWhere((rows) => rows.isEmpty)
+ .timeout(const Duration(seconds: 10)),
+ );
+ await tester.pumpAndSettle();
+ expect(
+ find.byKey(const ValueKey('account-detail-nextcloud:qa-2')),
+ findsNothing,
+ );
+ expect(
+ find.byKey(const ValueKey('account-entry-nextcloud:qa-2')),
+ findsNothing,
+ );
+ expect(
+ find.byKey(const ValueKey('account-entry-nextcloud:qa-1')),
+ findsOneWidget,
+ );
+ expect(
+ await tester.runAsync(() => secrets.readCredential('nextcloud:qa-2')),
+ isNull,
+ );
+ expect(
+ (await tester.runAsync(f.accounts.listVisibleAccounts))!,
+ hasLength(5),
+ );
+ expect(revocations, hasLength(1));
+ expect(FocusManager.instance.primaryFocus?.context, isNotNull);
+ expect(
+ FocusManager.instance.primaryFocus!.debugLabel,
+ startsWith('account-entry-'),
+ );
+ expect(
+ FocusManager.instance.primaryFocus!.debugLabel,
+ isNot(contains('nextcloud:qa-2')),
+ );
+ await tester.pumpWidget(const SizedBox());
+ await tester.pump();
+ container.dispose();
+ icons.dispose();
+ appClient.close();
+ semantics.dispose();
+ textScale.dispose();
+ await tester.runAsync(f.close);
+ },
+ skip: output == null,
+ );
+}
+
+Future _resize(WidgetTester tester, int width, int height) async {
+ await tester.runAsync(() async {
+ final result = await Process.run('/usr/bin/python3', [
+ '-c',
+ r'''import ctypes, re, subprocess, sys
+x = ctypes.CDLL('libX11.so.6')
+x.XOpenDisplay.argtypes = [ctypes.c_char_p]
+x.XOpenDisplay.restype = ctypes.c_void_p
+x.XResizeWindow.argtypes = [ctypes.c_void_p, ctypes.c_ulong, ctypes.c_uint, ctypes.c_uint]
+x.XFlush.argtypes = [ctypes.c_void_p]
+x.XCloseDisplay.argtypes = [ctypes.c_void_p]
+tree = subprocess.check_output(['/usr/bin/xwininfo', '-root', '-tree'], text=True)
+windows = re.findall(r'^\s*(0x[0-9a-f]+) "BusyMax":.*? (\d+)x(\d+)[+-]', tree, re.MULTILINE)
+main = [item for item in windows if int(item[1]) > 300 and int(item[2]) > 300]
+assert len(main) == 1, 'Expected one BusyMax window on the isolated display'
+connection = x.XOpenDisplay(None)
+x.XResizeWindow(connection, int(main[0][0], 16), int(sys.argv[1]), int(sys.argv[2]))
+x.XFlush(connection)
+x.XCloseDisplay(connection)
+''',
+ '$width',
+ '$height',
+ ]);
+ expect(result.exitCode, 0, reason: result.stderr.toString());
+ });
+ await tester.pumpAndSettle();
+}
+
+Future _capture(
+ WidgetTester tester,
+ String path, {
+ bool settle = true,
+}) async {
+ if (settle) await tester.pumpAndSettle();
+ await tester.runAsync(() async {
+ // Capture the isolated application window, including its modal overlays.
+ final result = await Process.run('/usr/bin/python3', [
+ '-c',
+ r'''import sys, gi, re, subprocess
+gi.require_version('Gdk', '3.0')
+from gi.repository import Gdk
+tree = subprocess.check_output(['/usr/bin/xwininfo', '-root', '-tree'], text=True)
+windows = re.findall(r'^\s*(0x[0-9a-f]+) "BusyMax":.*? (\d+)x(\d+)[+-]', tree, re.MULTILINE)
+main = [item for item in windows if int(item[1]) > 300 and int(item[2]) > 300]
+assert len(main) == 1, 'Expected one isolated BusyMax application window'
+info = subprocess.check_output(['/usr/bin/xwininfo', '-id', main[0][0]], text=True)
+def field(name):
+ return int(re.search(name + r':\s*(-?\d+)', info).group(1))
+window = Gdk.get_default_root_window()
+pixbuf = Gdk.pixbuf_get_from_window(window, field('Absolute upper-left X'), field('Absolute upper-left Y'), field('Width'), field('Height'))
+pixbuf.savev(sys.argv[1], 'png', [], [])
+''',
+ path,
+ ]);
+ expect(result.exitCode, 0, reason: result.stderr.toString());
+ });
+}
+
+Future _tap(WidgetTester tester, Finder target) async {
+ await tester.ensureVisible(target);
+ await tester.tap(target);
+ await tester.pumpAndSettle();
+}
+
+Future _waitForCalendarQueue(
+ WidgetTester tester,
+ AppDatabase database,
+) async {
+ await tester.runAsync(
+ () => database
+ .select(database.pendingOps)
+ .watch()
+ .firstWhere((rows) => rows.isEmpty)
+ .timeout(const Duration(seconds: 10)),
+ );
+ await tester.pumpAndSettle();
+}
+
+Future _open(WidgetTester tester, String id) =>
+ _tap(tester, find.byKey(ValueKey('account-entry-$id')));
+
+Future _back(WidgetTester tester) =>
+ _tap(tester, find.byKey(const ValueKey('settings-header-back-button')));
+
+Future _expand(WidgetTester tester, Finder target) => _tap(
+ tester,
+ find.descendant(of: target, matching: find.text('Advanced')).first,
+);
+
+Future _networkFailure(
+ WidgetTester tester,
+ ProviderContainer container,
+ ContactsFixture f,
+) async {
+ await tester.runAsync(() async {
+ try {
+ await container.read(accountSyncActivityProvider).run(
+ 'nextcloud:qa-1',
+ 'dav',
+ () async {
+ throw const DavException(
+ kind: DavErrorKind.network,
+ code: 'DavNetworkFailure',
+ safeMessage: 'Synthetic private diagnostic',
+ correlationId: 'synthetic-correlation',
+ );
+ },
+ );
+ } on DavException {
+ /* The activity store retains the typed failure. */
+ }
+ await f.accounts.setConnectionState(
+ 'nextcloud:qa-1',
+ AccountConnectionState.temporarilyUnavailable,
+ );
+ await f.database.customUpdate(
+ "UPDATE dav_account_services SET last_discovery_error_code = 'DavNetworkFailure' WHERE account_id = 'nextcloud:qa-1'",
+ updates: {f.database.davAccountServices},
+ );
+ });
+ await tester.pumpAndSettle();
+}
diff --git a/lib/l10n/app_ar.arb b/lib/l10n/app_ar.arb
index e3ef90d0..67686f95 100644
--- a/lib/l10n/app_ar.arb
+++ b/lib/l10n/app_ar.arb
@@ -1,4 +1,5 @@
{
+ "contactsAccountsUnavailable": "تعذّر تحميل حسابات جهات الاتصال",
"contactsTitle": "جهات الاتصال",
"contactsLinkedDescription": "استخدم جهات الاتصال المخزنة مؤقتًا من هذا الحساب عند إضافة مدعوين إلى الأحداث.",
"contactsEnableSuggestions": "تفعيل اقتراحات جهات الاتصال",
@@ -1092,7 +1093,7 @@
"windowMinimize": "تصغير",
"windowMaximize": "تكبير",
"windowRestore": "استعادة",
- "openSharedCalendar": "فتح تقويم مشترك",
+ "openSharedCalendar": "فتح تقويم أساسي مشترك",
"calendarOwnerEmail": "البريد الإلكتروني لمالك التقويم",
"addCalendarShare": "إضافة وصول",
"calendarShareFreeBusy": "التوفر فقط",
@@ -1296,5 +1297,49 @@
}
},
"googleIdentityUnverified": "تعذر التحقق من هوية اتصال Google القديم هذا. تم الاحتفاظ بالتقويمات والمهام والتغييرات المعلقة. أضف اتصال Google منفصلًا للمزامنة بأمان. ستبقى التغييرات المعلقة مع الحساب الأصلي.",
- "addSeparateGoogleAccount": "إضافة اتصال Google منفصل"
+ "addSeparateGoogleAccount": "إضافة اتصال Google منفصل",
+ "seriesPatternChangeConfirmation": "قد تؤدي هذه التغييرات في نمط التكرار إلى إضافة تواريخ أو إزالتها.\n\nالحالي: \u2068{before}\u2069\nالمقترح: \u2068{after}\u2069\n\nهل تريد حفظ هذا النمط؟",
+ "connectedAccounts": "الحسابات المتصلة",
+ "contactsIndependentAccounts": "حسابات جهات اتصال مستقلة",
+ "contactsNextcloudReuse": "تعيد اقتراحات جهات الاتصال استخدام بيانات اعتماد هذا الحساب عبر تكامل جهات الاتصال للقراءة فقط.",
+ "contactsRetrySync": "إعادة محاولة مزامنة جهات الاتصال",
+ "contactsPreferenceFailed": "تعذّر حفظ تفضيل مصدر جهات الاتصال.",
+ "contactsSuggestionsDisabled": "اقتراحات جهات الاتصال معطّلة",
+ "contactsSyncFailed": "تعذّرت مزامنة جهات الاتصال. أعد المحاولة أو أعد توصيل الحساب إذا انتهى التفويض.",
+ "registrationRecoveryDescription": "التسجيل المحفوظ أو ارتباط بيانات اعتماده غير متاح أو مرفوض. استبدل تسجيل هذا الحساب صراحةً لاستعادته؛ تُحفظ البيانات الحالية.",
+ "revalidateAccount": "إعادة التحقق من الاتصال",
+ "openSharedPrimaryDescription": "يفتح التقويم الأساسي للمالك المحدد. تُكتشف التقاويم المشتركة الأخرى عبر مزامنة الحساب المعتادة.",
+ "showInBusyMax": "إظهار في BusyMax",
+ "showInGoogleCalendarList": "إظهار في قائمة تقاويم Google",
+ "googleVisibilityHelp": "اختيار BusyMax محلي. يغيّر ظهور Google قائمتك في Google؛ لا يمكن عرض التقاويم المخفية في BusyMax.",
+ "primaryCalendarVisibilityExplanation": "يبقى تقويم Google الأساسي في قائمته.",
+ "accountsLoading": "جارٍ تحميل الحسابات…",
+ "accountsLoadFailed": "تعذّر تحميل الحسابات. أعد فتح الحسابات.",
+ "collectionsLoading": "جارٍ تحميل المجموعات…",
+ "collectionsLoadFailed": "تعذّر تحميل المجموعات. تُحفظ البيانات المخزنة مؤقتًا؛ أعد محاولة المزامنة.",
+ "collectionsNotDiscovered": "لم يكتمل الاكتشاف. حدّث هذا الحساب أو أعد توصيله.",
+ "collectionsEmpty": "لم يُعثر على مجموعات.",
+ "accountReadyToSync": "جاهز للمزامنة",
+ "accountSyncProblem": "تحتاج المزامنة إلى معالجة",
+ "accountServiceUnreachable": "تعذّر الوصول إلى الخدمة",
+ "accountSyncDetails": "تفاصيل المزامنة",
+ "accountReconnectStatus": "يلزم إعادة الاتصال",
+ "accountRegistrationStatus": "يحتاج تسجيل التطبيق إلى معالجة",
+ "collectionsUnavailable": "المجموعات غير متاحة",
+ "accountContactSuggestions": "اقتراح مدعوين من جهات اتصال هذا الحساب",
+ "accountLoadingSignInSettings": "جارٍ تحميل إعدادات تسجيل الدخول…",
+ "contactsNoAddressBooks": "لا توجد دفاتر عناوين متاحة",
+ "contactsSourcesUnavailable": "تعذّر تحميل دفاتر العناوين",
+ "syncNoRecord": "لم يُسجّل وقت مزامنة ناجحة",
+ "cachedSyncError": "فشلت المزامنة؛ قد تكون البيانات المخزنة مؤقتًا قديمة.",
+ "calendarRemotePending": "تغيير ظهور Google قيد الانتظار",
+ "calendarRemoteFailed": "فشل تغيير الظهور في Google. تظهر آخر حالة مؤكدة في Google. ما زال BusyMax يتبع التغيير المعلّق. أعد المحاولة أو تجاهل التغيير.",
+ "discardCalendarSettingsConfirmation": "هل تريد تجاهل التغييرات المعلّقة على «{calendar}»؟\n\nستعود هذه الإعدادات إلى قيمها السابقة:\n{settings}\n\nسيُحتفظ باختيار التقويم في BusyMax. لن تُرسل أي تغييرات إلى Google.",
+ "sharedCalendarUnavailable": "الوصول غير متاح؛ تُعرض البيانات المخزنة مؤقتًا. أعد المزامنة أو أزل التقويم من قائمتك.",
+ "invalidCollectionColor": "أدخل لونًا سداسيًا عشريًا من ست أو ثماني خانات يبدأ بـ #.",
+ "invalidCollectionOrder": "أدخل عددًا صحيحًا.",
+ "invalidCollectionTimezone": "أدخل مستند منطقة زمنية صالحًا للتقويم يحتوي على VTIMEZONE وTZID.",
+ "invalidCollectionValue": "صحّح هذه القيمة قبل الحفظ.",
+ "accountAlreadyConnected": "هذا الحساب متصل بالفعل. استخدم إجراء إعادة توصيله.",
+ "nextcloudRemovalAuthorizationDescription": "سيحاول BusyMax أيضًا إلغاء كلمة مرور تطبيق Nextcloud لهذا الحساب. قد تنجح الإزالة المحلية حتى إذا فشل الإلغاء."
}
diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb
index cd2eec61..aab2c2ba 100644
--- a/lib/l10n/app_de.arb
+++ b/lib/l10n/app_de.arb
@@ -1,4 +1,5 @@
{
+ "contactsAccountsUnavailable": "Kontaktkonten konnten nicht geladen werden",
"contactsTitle": "Kontakte",
"contactsLinkedDescription": "Beim Hinzufügen von Veranstaltungsgästen zwischengespeicherte Kontakte dieses Kontos verwenden.",
"contactsEnableSuggestions": "Kontaktvorschläge aktivieren",
@@ -1155,7 +1156,7 @@
"windowMinimize": "Minimieren",
"windowMaximize": "Maximieren",
"windowRestore": "Wiederherstellen",
- "openSharedCalendar": "Freigegebenen Kalender öffnen",
+ "openSharedCalendar": "Freigegebenen Hauptkalender öffnen",
"calendarOwnerEmail": "E-Mail-Adresse des Kalenderbesitzers",
"addCalendarShare": "Zugriff hinzufügen",
"calendarShareFreeBusy": "Nur Frei/Gebucht",
@@ -1359,5 +1360,49 @@
}
},
"googleIdentityUnverified": "Die Identität dieser älteren Google-Verbindung konnte nicht bestätigt werden. Kalender, Aufgaben und ausstehende Änderungen bleiben erhalten. Fügen Sie eine separate Google-Verbindung hinzu. Ausstehende Änderungen bleiben beim ursprünglichen Konto.",
- "addSeparateGoogleAccount": "Separate Google-Verbindung hinzufügen"
+ "addSeparateGoogleAccount": "Separate Google-Verbindung hinzufügen",
+ "seriesPatternChangeConfirmation": "Dies ändert das Wiederholungsmuster und kann Termine hinzufügen oder entfernen.\n\nAktuell: {before}\nVorschlag: {after}\n\nDieses Muster speichern?",
+ "connectedAccounts": "Verbundene Konten",
+ "contactsIndependentAccounts": "Unabhängige Kontaktkonten",
+ "contactsNextcloudReuse": "Kontaktvorschläge verwenden die vorhandenen Zugangsdaten dieses Kontos über die schreibgeschützte Kontaktintegration.",
+ "contactsRetrySync": "Kontaktsynchronisierung wiederholen",
+ "contactsPreferenceFailed": "Die Kontaktquelleneinstellung konnte nicht gespeichert werden.",
+ "contactsSuggestionsDisabled": "Kontaktvorschläge deaktiviert",
+ "contactsSyncFailed": "Kontakte konnten nicht synchronisiert werden. Erneut versuchen oder bei abgelaufener Autorisierung das Konto neu verbinden.",
+ "registrationRecoveryDescription": "Die gespeicherte Registrierung oder ihre Zugangsdatenbindung fehlt oder wurde abgelehnt. Ersetzen Sie die Registrierung dieses Kontos ausdrücklich, um den Zugriff wiederherzustellen. Vorhandene Daten bleiben erhalten.",
+ "revalidateAccount": "Verbindung erneut prüfen",
+ "openSharedPrimaryDescription": "Öffnet den Hauptkalender des angegebenen Besitzers. Andere freigegebene Kalender werden bei der normalen Kontosynchronisierung gefunden.",
+ "showInBusyMax": "In BusyMax anzeigen",
+ "showInGoogleCalendarList": "In Googles Kalenderliste anzeigen",
+ "googleVisibilityHelp": "Die BusyMax-Auswahl gilt lokal. Die Google-Sichtbarkeit ändert Ihre Kalenderliste bei Google; ausgeblendete Kalender können in BusyMax nicht angezeigt werden.",
+ "primaryCalendarVisibilityExplanation": "Der Google-Hauptkalender bleibt in der Kalenderliste.",
+ "accountsLoading": "Konten werden geladen…",
+ "accountsLoadFailed": "Konten konnten nicht geladen werden. Öffnen Sie Konten erneut.",
+ "collectionsLoading": "Sammlungen werden geladen…",
+ "collectionsLoadFailed": "Sammlungen konnten nicht geladen werden. Zwischengespeicherte Daten bleiben erhalten. Synchronisierung erneut versuchen.",
+ "collectionsNotDiscovered": "Die Erkennung ist noch nicht abgeschlossen. Konto aktualisieren oder neu verbinden.",
+ "collectionsEmpty": "Keine Sammlungen gefunden.",
+ "accountReadyToSync": "Bereit zur Synchronisierung",
+ "accountSyncProblem": "Synchronisierung benötigt Aufmerksamkeit",
+ "accountServiceUnreachable": "Dienst nicht erreichbar",
+ "accountSyncDetails": "Synchronisierungsdetails",
+ "accountReconnectStatus": "Erneute Verbindung erforderlich",
+ "accountRegistrationStatus": "App-Registrierung benötigt Aufmerksamkeit",
+ "collectionsUnavailable": "Sammlungen nicht verfügbar",
+ "accountContactSuggestions": "Gäste aus den Kontakten dieses Kontos vorschlagen",
+ "accountLoadingSignInSettings": "Anmeldeeinstellungen werden geladen…",
+ "contactsNoAddressBooks": "Keine Adressbücher verfügbar",
+ "contactsSourcesUnavailable": "Adressbücher konnten nicht geladen werden",
+ "syncNoRecord": "Kein erfolgreicher Synchronisierungszeitpunkt erfasst",
+ "cachedSyncError": "Synchronisierung fehlgeschlagen; zwischengespeicherte Daten können veraltet sein.",
+ "calendarRemotePending": "Google-Sichtbarkeitsänderung ausstehend",
+ "calendarRemoteFailed": "Die Sichtbarkeitsänderung bei Google ist fehlgeschlagen. Der letzte bestätigte Google-Status wird angezeigt. BusyMax folgt weiterhin der ausstehenden Änderung. Erneut versuchen oder verwerfen.",
+ "discardCalendarSettingsConfirmation": "Ausstehende Änderungen an „{calendar}“ verwerfen?\n\nDiese Einstellungen werden auf ihre vorherigen Werte zurückgesetzt:\n{settings}\n\nDeine Kalenderauswahl in BusyMax bleibt erhalten. Es werden keine Änderungen an Google gesendet.",
+ "sharedCalendarUnavailable": "Zugriff nicht verfügbar; zwischengespeicherte Daten werden angezeigt. Synchronisierung wiederholen oder aus der Kalenderliste entfernen.",
+ "invalidCollectionColor": "Geben Sie eine sechs- oder achtstellige Hexadezimalfarbe mit # ein.",
+ "invalidCollectionOrder": "Geben Sie eine ganze Zahl ein.",
+ "invalidCollectionTimezone": "Geben Sie ein gültiges Kalenderzeitzonendokument mit VTIMEZONE und TZID ein.",
+ "invalidCollectionValue": "Korrigieren Sie diesen Wert vor dem Speichern.",
+ "accountAlreadyConnected": "Dieses Konto ist bereits verbunden. Verwenden Sie die Aktion zum erneuten Verbinden.",
+ "nextcloudRemovalAuthorizationDescription": "BusyMax versucht außerdem, das Nextcloud-App-Passwort dieses Kontos zu widerrufen. Die lokale Entfernung kann auch bei fehlgeschlagenem Widerruf erfolgreich sein."
}
diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb
index 26dc79a4..81f6686f 100644
--- a/lib/l10n/app_en.arb
+++ b/lib/l10n/app_en.arb
@@ -1,4 +1,5 @@
{
+ "contactsAccountsUnavailable": "Could not load contact accounts",
"launchAtLoginManagedExternally": "BusyMax was launched by your desktop's startup settings. Remove its entry there, then quit and reopen BusyMax to use this switch.",
"timeFormat": "Time format",
"firstDayOfWeek": "First day of the week",
@@ -1772,7 +1773,7 @@
"searchFiltersAction": "Filters",
"searchNoSources": "No search sources selected",
"searchClearText": "Clear text",
- "openSharedCalendar": "Open shared calendar",
+ "openSharedCalendar": "Open shared primary calendar",
"manageCalendarSharing": "Manage calendar sharing",
"shareRecipientEmail": "Recipient email",
"shareRole": "Access role",
@@ -1991,5 +1992,71 @@
}
},
"googleIdentityUnverified": "The identity of this older Google connection could not be verified. Its calendars, tasks and pending changes are preserved. Add a separate Google connection to synchronize safely. Pending changes will remain with the original account.",
- "addSeparateGoogleAccount": "Add a separate Google connection"
+ "addSeparateGoogleAccount": "Add a separate Google connection",
+ "seriesPatternChangeConfirmation": "This changes the recurrence pattern and may add or remove dates.\n\nCurrent: {before}\nProposed: {after}\n\nSave this pattern?",
+ "@seriesPatternChangeConfirmation": {
+ "description": "Explicit approval of an ordinary series date edit that changes recurrence semantics. Both patterns include their termination condition.",
+ "placeholders": {
+ "before": {
+ "type": "String"
+ },
+ "after": {
+ "type": "String"
+ }
+ }
+ },
+ "connectedAccounts": "Connected accounts",
+ "contactsIndependentAccounts": "Independent contact accounts",
+ "contactsNextcloudReuse": "Contact suggestions reuse this account’s existing credential through the read-only contacts integration.",
+ "contactsRetrySync": "Retry contacts sync",
+ "contactsPreferenceFailed": "Could not save contact source preference",
+ "contactsSuggestionsDisabled": "Contact suggestions disabled",
+ "contactsSyncFailed": "Contacts could not sync. Retry, or reconnect if authorization has expired.",
+ "registrationRecoveryDescription": "The saved registration or its credential binding is unavailable or rejected. Explicitly replace the registration for this account to recover; existing data is preserved.",
+ "revalidateAccount": "Revalidate connection",
+ "openSharedPrimaryDescription": "Opens the specified owner’s primary calendar. Other shared calendars are discovered through ordinary account synchronization.",
+ "showInBusyMax": "Show in BusyMax",
+ "showInGoogleCalendarList": "Show in Google’s calendar list",
+ "googleVisibilityHelp": "BusyMax selection is local. Google visibility changes your calendar list on Google; hidden calendars cannot be displayed in BusyMax.",
+ "primaryCalendarVisibilityExplanation": "Google’s primary calendar stays in its calendar list.",
+ "accountsLoading": "Loading accounts…",
+ "accountsLoadFailed": "Accounts could not be loaded. Retry opening Accounts.",
+ "collectionsLoading": "Loading collections…",
+ "collectionsLoadFailed": "Collections could not be loaded. Cached data is retained; retry synchronization.",
+ "collectionsNotDiscovered": "Discovery has not completed. Refresh or reconnect this account.",
+ "collectionsEmpty": "No collections found.",
+ "accountReadyToSync": "Ready to sync",
+ "accountSyncProblem": "Synchronization needs attention",
+ "accountServiceUnreachable": "Could not reach the service",
+ "accountSyncDetails": "Synchronization details",
+ "accountReconnectStatus": "Reconnect required",
+ "accountRegistrationStatus": "Registration needs attention",
+ "collectionsUnavailable": "Collections unavailable",
+ "accountContactSuggestions": "Suggest guests from this account’s contacts",
+ "accountLoadingSignInSettings": "Loading sign-in settings…",
+ "contactsNoAddressBooks": "No address books available",
+ "contactsSourcesUnavailable": "Could not load address books",
+ "syncNoRecord": "No successful sync time recorded",
+ "cachedSyncError": "Sync failed; cached data may be out of date.",
+ "calendarRemotePending": "Google visibility change pending",
+ "calendarRemoteFailed": "Google visibility change failed. Google’s last confirmed state is shown. BusyMax still follows the pending change. Retry or discard it.",
+ "discardCalendarSettingsConfirmation": "Discard the pending changes to “{calendar}”?\n\nThese settings will return to their previous values:\n{settings}\n\nYour BusyMax calendar selection is kept. No changes will be sent to Google.",
+ "@discardCalendarSettingsConfirmation": {
+ "description": "Confirms discarding the entire queued Google calendar settings patch. calendar is the calendar display name; settings is a localized, newline-separated bulleted list of every affected setting. No provider request is sent, and the local calendar selection is preserved.",
+ "placeholders": {
+ "calendar": {
+ "type": "String"
+ },
+ "settings": {
+ "type": "String"
+ }
+ }
+ },
+ "sharedCalendarUnavailable": "Access unavailable; showing cached data. Retry synchronization or remove from your calendar list.",
+ "invalidCollectionColor": "Enter a six- or eight-digit hexadecimal color, starting with #.",
+ "invalidCollectionOrder": "Enter a whole number.",
+ "invalidCollectionTimezone": "Enter a valid calendar timezone document containing VTIMEZONE and TZID.",
+ "invalidCollectionValue": "Correct this value before saving.",
+ "accountAlreadyConnected": "This account is already connected. Use its Reconnect action.",
+ "nextcloudRemovalAuthorizationDescription": "BusyMax will also try to revoke this account’s Nextcloud app password. Local removal can succeed even if that revocation fails."
}
diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb
index 3a8c93d6..fa2cb231 100644
--- a/lib/l10n/app_es.arb
+++ b/lib/l10n/app_es.arb
@@ -1,4 +1,5 @@
{
+ "contactsAccountsUnavailable": "No se pudieron cargar las cuentas de contactos",
"contactsTitle": "Contactos",
"contactsLinkedDescription": "Usa los contactos almacenados de esta cuenta al añadir invitados a eventos.",
"contactsEnableSuggestions": "Activar sugerencias de contactos",
@@ -1155,7 +1156,7 @@
"windowMinimize": "Minimizar",
"windowMaximize": "Maximizar",
"windowRestore": "Restaurar",
- "openSharedCalendar": "Abrir calendario compartido",
+ "openSharedCalendar": "Abrir calendario principal compartido",
"calendarOwnerEmail": "Correo del propietario del calendario",
"addCalendarShare": "Añadir acceso",
"calendarShareFreeBusy": "Solo libre/ocupado",
@@ -1359,5 +1360,49 @@
}
},
"googleIdentityUnverified": "No se pudo verificar la identidad de esta conexión antigua de Google. Se conservan los calendarios, tareas y cambios pendientes. Añade una conexión de Google separada para sincronizar con seguridad. Los cambios pendientes seguirán en la cuenta original.",
- "addSeparateGoogleAccount": "Añadir conexión de Google separada"
+ "addSeparateGoogleAccount": "Añadir conexión de Google separada",
+ "seriesPatternChangeConfirmation": "Este cambio en la repetición puede añadir o eliminar fechas.\n\nActual: {before}\nPropuesta: {after}\n\n¿Guardar esta repetición?",
+ "connectedAccounts": "Cuentas conectadas",
+ "contactsIndependentAccounts": "Cuentas de contactos independientes",
+ "contactsNextcloudReuse": "Las sugerencias reutilizan las credenciales de esta cuenta mediante la integración de contactos de solo lectura.",
+ "contactsRetrySync": "Reintentar sincronización de contactos",
+ "contactsPreferenceFailed": "No se pudo guardar la preferencia de la fuente de contactos.",
+ "contactsSuggestionsDisabled": "Sugerencias de contactos desactivadas",
+ "contactsSyncFailed": "No se pudieron sincronizar los contactos. Reinténtelo o vuelva a conectar si la autorización caducó.",
+ "registrationRecoveryDescription": "El registro guardado o su vínculo de credenciales no está disponible o fue rechazado. Sustituya explícitamente el registro de esta cuenta para recuperarla; se conservan los datos.",
+ "revalidateAccount": "Revalidar conexión",
+ "openSharedPrimaryDescription": "Abre el calendario principal del propietario indicado. Los demás calendarios compartidos se descubren mediante la sincronización habitual.",
+ "showInBusyMax": "Mostrar en BusyMax",
+ "showInGoogleCalendarList": "Mostrar en la lista de calendarios de Google",
+ "googleVisibilityHelp": "La selección de BusyMax es local. La visibilidad de Google cambia su lista en Google; los calendarios ocultos no se pueden mostrar en BusyMax.",
+ "primaryCalendarVisibilityExplanation": "El calendario principal de Google permanece en su lista.",
+ "accountsLoading": "Cargando cuentas…",
+ "accountsLoadFailed": "No se pudieron cargar las cuentas. Vuelva a abrir Cuentas.",
+ "collectionsLoading": "Cargando colecciones…",
+ "collectionsLoadFailed": "No se pudieron cargar las colecciones. Se conserva la caché; reintente la sincronización.",
+ "collectionsNotDiscovered": "El descubrimiento no ha terminado. Actualice o vuelva a conectar esta cuenta.",
+ "collectionsEmpty": "No se encontraron colecciones.",
+ "accountReadyToSync": "Listo para sincronizar",
+ "accountSyncProblem": "La sincronización requiere atención",
+ "accountServiceUnreachable": "No se pudo acceder al servicio",
+ "accountSyncDetails": "Detalles de sincronización",
+ "accountReconnectStatus": "Es necesario volver a conectar",
+ "accountRegistrationStatus": "El registro requiere atención",
+ "collectionsUnavailable": "Colecciones no disponibles",
+ "accountContactSuggestions": "Sugerir invitados de los contactos de esta cuenta",
+ "accountLoadingSignInSettings": "Cargando ajustes de inicio de sesión…",
+ "contactsNoAddressBooks": "No hay libretas de direcciones disponibles",
+ "contactsSourcesUnavailable": "No se pudieron cargar las libretas de direcciones",
+ "syncNoRecord": "No se registró ninguna sincronización correcta",
+ "cachedSyncError": "Falló la sincronización; los datos en caché pueden estar desactualizados.",
+ "calendarRemotePending": "Cambio de visibilidad en Google pendiente",
+ "calendarRemoteFailed": "Falló el cambio de visibilidad en Google. Se muestra el último estado confirmado de Google. BusyMax sigue aplicando el cambio pendiente. Reinténtalo o descártalo.",
+ "discardCalendarSettingsConfirmation": "¿Descartar los cambios pendientes de «{calendar}»?\n\nEstos ajustes volverán a sus valores anteriores:\n{settings}\n\nSe conserva tu selección de calendarios en BusyMax. No se enviarán cambios a Google.",
+ "sharedCalendarUnavailable": "Acceso no disponible; se muestra la caché. Reintente la sincronización o quite el calendario de su lista.",
+ "invalidCollectionColor": "Introduzca un color hexadecimal de seis u ocho dígitos que empiece por #.",
+ "invalidCollectionOrder": "Introduzca un número entero.",
+ "invalidCollectionTimezone": "Introduzca un documento de zona horaria válido que contenga VTIMEZONE y TZID.",
+ "invalidCollectionValue": "Corrija este valor antes de guardar.",
+ "accountAlreadyConnected": "Esta cuenta ya está conectada. Use su acción de reconexión.",
+ "nextcloudRemovalAuthorizationDescription": "BusyMax también intentará revocar la contraseña de aplicación de Nextcloud de esta cuenta. La eliminación local puede completarse aunque falle la revocación."
}
diff --git a/lib/l10n/app_et.arb b/lib/l10n/app_et.arb
index 889ffb13..e441e613 100644
--- a/lib/l10n/app_et.arb
+++ b/lib/l10n/app_et.arb
@@ -1,4 +1,5 @@
{
+ "contactsAccountsUnavailable": "Kontaktikontosid ei õnnestunud laadida",
"contactsTitle": "Kontaktid",
"contactsLinkedDescription": "Kasuta sündmuse külaliste lisamisel selle konto vahemällu salvestatud kontakte.",
"contactsEnableSuggestions": "Luba kontaktisoovitused",
@@ -1270,7 +1271,7 @@
"windowMinimize": "Minimeeri",
"windowMaximize": "Maksimeeri",
"windowRestore": "Taasta",
- "openSharedCalendar": "Ava jagatud kalender",
+ "openSharedCalendar": "Ava jagatud põhikalender",
"calendarOwnerEmail": "Kalendri omaniku e-posti aadress",
"addCalendarShare": "Lisa juurdepääs",
"calendarShareFreeBusy": "Ainult vaba/hõivatud",
@@ -1474,5 +1475,49 @@
}
},
"googleIdentityUnverified": "Selle vana Google’i ühenduse identiteeti ei saanud kinnitada. Kalendrid, ülesanded ja ootel muudatused säilitatakse. Lisa turvaliseks sünkroonimiseks eraldi Google’i ühendus. Ootel muudatused jäävad algse konto juurde.",
- "addSeparateGoogleAccount": "Lisa eraldi Google’i ühendus"
+ "addSeparateGoogleAccount": "Lisa eraldi Google’i ühendus",
+ "seriesPatternChangeConfirmation": "Kordusmustri muutmine võib kuupäevi lisada või eemaldada.\n\nPraegune: {before}\nPakutud: {after}\n\nKas salvestada see muster?",
+ "connectedAccounts": "Ühendatud kontod",
+ "contactsIndependentAccounts": "Sõltumatud kontaktikontod",
+ "contactsNextcloudReuse": "Kontaktisoovitused kasutavad selle konto olemasolevaid pääsuandmeid kirjutuskaitstud kontaktide integratsiooni kaudu.",
+ "contactsRetrySync": "Proovi kontaktide sünkroonimist uuesti",
+ "contactsPreferenceFailed": "Kontaktiallika eelistust ei saanud salvestada.",
+ "contactsSuggestionsDisabled": "Kontaktisoovitused on välja lülitatud",
+ "contactsSyncFailed": "Kontakte ei saanud sünkroonida. Proovi uuesti või ühenda konto uuesti, kui volitus on aegunud.",
+ "registrationRecoveryDescription": "Salvestatud registreering või selle pääsuandmete seos pole saadaval või on tagasi lükatud. Taastamiseks asenda selle konto registreering sõnaselgelt; andmed säilivad.",
+ "revalidateAccount": "Kontrolli ühendust uuesti",
+ "openSharedPrimaryDescription": "Avab määratud omaniku põhikalendri. Muud jagatud kalendrid leitakse konto tavapärase sünkroonimise käigus.",
+ "showInBusyMax": "Kuva BusyMaxis",
+ "showInGoogleCalendarList": "Kuva Google’i kalendriloendis",
+ "googleVisibilityHelp": "BusyMaxi valik on kohalik. Google’i nähtavus muudab sinu loendit Google’is; peidetud kalendreid ei saa BusyMaxis kuvada.",
+ "primaryCalendarVisibilityExplanation": "Google’i põhikalender jääb loendisse.",
+ "accountsLoading": "Kontode laadimine…",
+ "accountsLoadFailed": "Kontosid ei saanud laadida. Ava Kontod uuesti.",
+ "collectionsLoading": "Kogumite laadimine…",
+ "collectionsLoadFailed": "Kogumeid ei saanud laadida. Vahemälu säilib; proovi sünkroonimist uuesti.",
+ "collectionsNotDiscovered": "Tuvastamine pole lõppenud. Värskenda või ühenda konto uuesti.",
+ "collectionsEmpty": "Kogumeid ei leitud.",
+ "accountReadyToSync": "Sünkroonimiseks valmis",
+ "accountSyncProblem": "Sünkroonimine vajab tähelepanu",
+ "accountServiceUnreachable": "Teenusega ei saanud ühendust",
+ "accountSyncDetails": "Sünkroonimise üksikasjad",
+ "accountReconnectStatus": "Ühenda konto uuesti",
+ "accountRegistrationStatus": "Rakenduse registreering vajab tähelepanu",
+ "collectionsUnavailable": "Kogud pole saadaval",
+ "accountContactSuggestions": "Soovita külalisi selle konto kontaktidest",
+ "accountLoadingSignInSettings": "Sisselogimisseadete laadimine…",
+ "contactsNoAddressBooks": "Aadressiraamatud pole saadaval",
+ "contactsSourcesUnavailable": "Aadressiraamatuid ei saanud laadida",
+ "syncNoRecord": "Eduka sünkroonimise aega pole salvestatud",
+ "cachedSyncError": "Sünkroonimine nurjus; vahemälu andmed võivad olla aegunud.",
+ "calendarRemotePending": "Google’i nähtavuse muudatus on ootel",
+ "calendarRemoteFailed": "Google’i nähtavuse muutmine ebaõnnestus. Kuvatakse Google’i viimane kinnitatud olek. BusyMax järgib endiselt ootel muudatust. Proovi uuesti või loobu muudatusest.",
+ "discardCalendarSettingsConfirmation": "Kas hüljata kalendri „{calendar}“ ootel muudatused?\n\nNende seadete eelmised väärtused taastatakse:\n{settings}\n\nSinu BusyMaxi kalendrivalik säilib. Google’ile ei saadeta muudatusi.",
+ "sharedCalendarUnavailable": "Juurdepääs puudub; kuvatakse vahemälu andmed. Proovi sünkroonimist uuesti või eemalda kalender loendist.",
+ "invalidCollectionColor": "Sisesta kuue- või kaheksakohaline kuueteistkümnendvärv, alustades märgiga #.",
+ "invalidCollectionOrder": "Sisesta täisarv.",
+ "invalidCollectionTimezone": "Sisesta kehtiv kalendri ajavööndi dokument, mis sisaldab VTIMEZONE ja TZID väärtusi.",
+ "invalidCollectionValue": "Paranda see väärtus enne salvestamist.",
+ "accountAlreadyConnected": "See konto on juba ühendatud. Kasuta selle uuesti ühendamise toimingut.",
+ "nextcloudRemovalAuthorizationDescription": "BusyMax proovib tühistada ka selle konto Nextcloudi rakenduse parooli. Kohalik eemaldamine võib õnnestuda ka siis, kui tühistamine nurjub."
}
diff --git a/lib/l10n/app_fa.arb b/lib/l10n/app_fa.arb
index d2f71cb0..10635313 100644
--- a/lib/l10n/app_fa.arb
+++ b/lib/l10n/app_fa.arb
@@ -1,4 +1,5 @@
{
+ "contactsAccountsUnavailable": "حسابهای مخاطبین بارگیری نشدند",
"contactsTitle": "مخاطبان",
"contactsLinkedDescription": "هنگام افزودن مهمانان رویداد از مخاطبان ذخیرهشدهٔ این حساب استفاده شود.",
"contactsEnableSuggestions": "فعالکردن پیشنهادهای مخاطبان",
@@ -1166,7 +1167,7 @@
"windowMinimize": "کوچک کردن",
"windowMaximize": "بزرگ کردن",
"windowRestore": "بازیابی",
- "openSharedCalendar": "باز کردن تقویم اشتراکی",
+ "openSharedCalendar": "باز کردن تقویم اصلی اشتراکی",
"calendarOwnerEmail": "ایمیل مالک تقویم",
"addCalendarShare": "افزودن دسترسی",
"calendarShareFreeBusy": "فقط آزاد/مشغول",
@@ -1370,5 +1371,49 @@
}
},
"googleIdentityUnverified": "هویت این اتصال قدیمی Google تأیید نشد. تقویمها، کارها و تغییرات در انتظار حفظ شدهاند. برای همگامسازی امن، یک اتصال Google جداگانه اضافه کنید. تغییرات در انتظار در حساب اصلی باقی میمانند.",
- "addSeparateGoogleAccount": "افزودن اتصال Google جداگانه"
+ "addSeparateGoogleAccount": "افزودن اتصال Google جداگانه",
+ "seriesPatternChangeConfirmation": "این تغییر در الگوی تکرار ممکن است تاریخهایی را اضافه یا حذف کند.\n\nفعلی: \u2068{before}\u2069\nپیشنهادی: \u2068{after}\u2069\n\nاین الگو ذخیره شود؟",
+ "connectedAccounts": "حسابهای متصل",
+ "contactsIndependentAccounts": "حسابهای مستقل مخاطبان",
+ "contactsNextcloudReuse": "پیشنهادهای مخاطب از اطلاعات ورود فعلی این حساب از طریق یکپارچهسازی فقطخواندنی مخاطبان استفاده میکنند.",
+ "contactsRetrySync": "تلاش دوباره برای همگامسازی مخاطبان",
+ "contactsPreferenceFailed": "ذخیره تنظیم منبع مخاطبان ممکن نشد.",
+ "contactsSuggestionsDisabled": "پیشنهادهای مخاطب غیرفعال است",
+ "contactsSyncFailed": "همگامسازی مخاطبان انجام نشد. دوباره تلاش کنید یا اگر مجوز منقضی شده حساب را دوباره متصل کنید.",
+ "registrationRecoveryDescription": "ثبت ذخیرهشده یا پیوند اطلاعات ورود آن در دسترس نیست یا رد شده است. برای بازیابی، ثبت این حساب را بهصراحت جایگزین کنید؛ دادههای فعلی حفظ میشوند.",
+ "revalidateAccount": "بررسی دوباره اتصال",
+ "openSharedPrimaryDescription": "تقویم اصلی مالک مشخصشده را باز میکند. تقویمهای اشتراکی دیگر از طریق همگامسازی عادی حساب پیدا میشوند.",
+ "showInBusyMax": "نمایش در BusyMax",
+ "showInGoogleCalendarList": "نمایش در فهرست تقویم Google",
+ "googleVisibilityHelp": "انتخاب BusyMax محلی است. نمایانی Google فهرست شما در Google را تغییر میدهد؛ تقویمهای پنهان در BusyMax نمایش داده نمیشوند.",
+ "primaryCalendarVisibilityExplanation": "تقویم اصلی Google در فهرست باقی میماند.",
+ "accountsLoading": "در حال بارگذاری حسابها…",
+ "accountsLoadFailed": "حسابها بارگذاری نشدند. بخش حسابها را دوباره باز کنید.",
+ "collectionsLoading": "در حال بارگذاری مجموعهها…",
+ "collectionsLoadFailed": "مجموعهها بارگذاری نشدند. دادههای ذخیرهشده حفظ میشوند؛ همگامسازی را دوباره امتحان کنید.",
+ "collectionsNotDiscovered": "کشف کامل نشده است. این حساب را تازهسازی یا دوباره متصل کنید.",
+ "collectionsEmpty": "مجموعهای پیدا نشد.",
+ "accountReadyToSync": "آمادهٔ همگامسازی",
+ "accountSyncProblem": "همگامسازی نیاز به رسیدگی دارد",
+ "accountServiceUnreachable": "دسترسی به سرویس ممکن نشد",
+ "accountSyncDetails": "جزئیات همگامسازی",
+ "accountReconnectStatus": "اتصال مجدد لازم است",
+ "accountRegistrationStatus": "ثبت برنامه نیاز به رسیدگی دارد",
+ "collectionsUnavailable": "مجموعهها در دسترس نیستند",
+ "accountContactSuggestions": "پیشنهاد مهمان از مخاطبان این حساب",
+ "accountLoadingSignInSettings": "در حال بارگیری تنظیمات ورود…",
+ "contactsNoAddressBooks": "دفترچهٔ نشانی در دسترس نیست",
+ "contactsSourcesUnavailable": "بارگیری دفترچههای نشانی ممکن نشد",
+ "syncNoRecord": "زمان همگامسازی موفق ثبت نشده است",
+ "cachedSyncError": "همگامسازی ناموفق بود؛ دادههای ذخیرهشده ممکن است قدیمی باشند.",
+ "calendarRemotePending": "تغییر نمایانی Google در انتظار است",
+ "calendarRemoteFailed": "تغییر نمایش در Google ناموفق بود. آخرین وضعیت تأییدشدهٔ Google نمایش داده میشود. BusyMax همچنان از تغییر در انتظار پیروی میکند. دوباره تلاش کنید یا تغییر را کنار بگذارید.",
+ "discardCalendarSettingsConfirmation": "تغییرات در انتظار برای «{calendar}» کنار گذاشته شوند؟\n\nاین تنظیمات به مقادیر قبلی بازمیگردند:\n{settings}\n\nانتخاب تقویم شما در BusyMax حفظ میشود. هیچ تغییری به Google ارسال نخواهد شد.",
+ "sharedCalendarUnavailable": "دسترسی موجود نیست؛ دادههای ذخیرهشده نمایش داده میشوند. همگامسازی را دوباره امتحان کنید یا تقویم را از فهرست حذف کنید.",
+ "invalidCollectionColor": "یک رنگ شانزدهشانزدهی شش یا هشت رقمی با # در ابتدا وارد کنید.",
+ "invalidCollectionOrder": "یک عدد صحیح وارد کنید.",
+ "invalidCollectionTimezone": "یک سند منطقه زمانی معتبر شامل VTIMEZONE و TZID وارد کنید.",
+ "invalidCollectionValue": "این مقدار را پیش از ذخیره اصلاح کنید.",
+ "accountAlreadyConnected": "این حساب از قبل متصل است. از گزینه اتصال دوباره آن استفاده کنید.",
+ "nextcloudRemovalAuthorizationDescription": "BusyMax تلاش میکند گذرواژه برنامه Nextcloud این حساب را نیز لغو کند. حذف محلی حتی در صورت ناموفق بودن لغو میتواند انجام شود."
}
diff --git a/lib/l10n/app_fi.arb b/lib/l10n/app_fi.arb
index 16d7d619..13bb462c 100644
--- a/lib/l10n/app_fi.arb
+++ b/lib/l10n/app_fi.arb
@@ -1,4 +1,5 @@
{
+ "contactsAccountsUnavailable": "Yhteystietotilejä ei voitu ladata",
"contactsTitle": "Yhteystiedot",
"contactsLinkedDescription": "Käytä tämän tilin välimuistissa olevia yhteystietoja tapahtuman vieraita lisättäessä.",
"contactsEnableSuggestions": "Ota yhteystietoehdotukset käyttöön",
@@ -1092,7 +1093,7 @@
"windowMinimize": "Pienennä",
"windowMaximize": "Suurenna",
"windowRestore": "Palauta",
- "openSharedCalendar": "Avaa jaettu kalenteri",
+ "openSharedCalendar": "Avaa jaettu ensisijainen kalenteri",
"calendarOwnerEmail": "Kalenterin omistajan sähköpostiosoite",
"addCalendarShare": "Lisää käyttöoikeus",
"calendarShareFreeBusy": "Vain vapaa/varattu",
@@ -1296,5 +1297,49 @@
}
},
"googleIdentityUnverified": "Tämän vanhan Google-yhteyden henkilöllisyyttä ei voitu vahvistaa. Kalenterit, tehtävät ja odottavat muutokset säilytetään. Lisää erillinen Google-yhteys turvallista synkronointia varten. Odottavat muutokset jäävät alkuperäiseen tiliin.",
- "addSeparateGoogleAccount": "Lisää erillinen Google-yhteys"
+ "addSeparateGoogleAccount": "Lisää erillinen Google-yhteys",
+ "seriesPatternChangeConfirmation": "Toistumissäännön muuttaminen voi lisätä tai poistaa päivämääriä.\n\nNykyinen: {before}\nEhdotettu: {after}\n\nTallennetaanko tämä sääntö?",
+ "connectedAccounts": "Yhdistetyt tilit",
+ "contactsIndependentAccounts": "Erilliset yhteystietotilit",
+ "contactsNextcloudReuse": "Yhteystietoehdotukset käyttävät tämän tilin nykyisiä tunnistetietoja vain luku -integraation kautta.",
+ "contactsRetrySync": "Yritä yhteystietojen synkronointia uudelleen",
+ "contactsPreferenceFailed": "Yhteystietolähteen asetusta ei voitu tallentaa.",
+ "contactsSuggestionsDisabled": "Yhteystietoehdotukset poistettu käytöstä",
+ "contactsSyncFailed": "Yhteystietoja ei voitu synkronoida. Yritä uudelleen tai yhdistä tili uudelleen, jos valtuutus on vanhentunut.",
+ "registrationRecoveryDescription": "Tallennettu rekisteröinti tai sen tunnistetietosidos puuttuu tai on hylätty. Korvaa tämän tilin rekisteröinti erikseen palautusta varten; tiedot säilytetään.",
+ "revalidateAccount": "Tarkista yhteys uudelleen",
+ "openSharedPrimaryDescription": "Avaa annetun omistajan ensisijaisen kalenterin. Muut jaetut kalenterit löytyvät tavallisen tilisynkronoinnin kautta.",
+ "showInBusyMax": "Näytä BusyMaxissa",
+ "showInGoogleCalendarList": "Näytä Googlen kalenteriluettelossa",
+ "googleVisibilityHelp": "BusyMaxin valinta on paikallinen. Google-näkyvyys muuttaa Googlen luetteloa; piilotettuja kalentereita ei voi näyttää BusyMaxissa.",
+ "primaryCalendarVisibilityExplanation": "Googlen ensisijainen kalenteri pysyy luettelossa.",
+ "accountsLoading": "Ladataan tilejä…",
+ "accountsLoadFailed": "Tilejä ei voitu ladata. Avaa Tilit uudelleen.",
+ "collectionsLoading": "Ladataan kokoelmia…",
+ "collectionsLoadFailed": "Kokoelmia ei voitu ladata. Välimuisti säilytetään; yritä synkronointia uudelleen.",
+ "collectionsNotDiscovered": "Haku ei ole vielä valmis. Päivitä tai yhdistä tämä tili uudelleen.",
+ "collectionsEmpty": "Kokoelmia ei löytynyt.",
+ "accountReadyToSync": "Valmis synkronointiin",
+ "accountSyncProblem": "Synkronointi vaatii huomiota",
+ "accountServiceUnreachable": "Palveluun ei saatu yhteyttä",
+ "accountSyncDetails": "Synkronoinnin tiedot",
+ "accountReconnectStatus": "Yhdistä tili uudelleen",
+ "accountRegistrationStatus": "Sovellusrekisteröinti vaatii huomiota",
+ "collectionsUnavailable": "Kokoelmat eivät ole käytettävissä",
+ "accountContactSuggestions": "Ehdota vieraita tämän tilin yhteystiedoista",
+ "accountLoadingSignInSettings": "Ladataan kirjautumisasetuksia…",
+ "contactsNoAddressBooks": "Osoitekirjoja ei ole käytettävissä",
+ "contactsSourcesUnavailable": "Osoitekirjoja ei voitu ladata",
+ "syncNoRecord": "Onnistuneen synkronoinnin aikaa ei ole tallennettu",
+ "cachedSyncError": "Synkronointi epäonnistui; välimuistin tiedot voivat olla vanhentuneita.",
+ "calendarRemotePending": "Googlen näkyvyysmuutos odottaa",
+ "calendarRemoteFailed": "Näkyvyyden muuttaminen Googlessa epäonnistui. Googlen viimeisin vahvistettu tila näytetään. BusyMax noudattaa yhä odottavaa muutosta. Yritä uudelleen tai hylkää muutos.",
+ "discardCalendarSettingsConfirmation": "Hylätäänkö kalenterin ”{calendar}” odottavat muutokset?\n\nNämä asetukset palautetaan aiempiin arvoihinsa:\n{settings}\n\nBusyMaxin kalenterivalintasi säilyy. Googlelle ei lähetetä muutoksia.",
+ "sharedCalendarUnavailable": "Ei käyttöoikeutta; näytetään välimuistin tiedot. Yritä synkronointia uudelleen tai poista kalenteri luettelosta.",
+ "invalidCollectionColor": "Anna kuusi- tai kahdeksannumeroinen heksaväri, joka alkaa #-merkillä.",
+ "invalidCollectionOrder": "Anna kokonaisluku.",
+ "invalidCollectionTimezone": "Anna kelvollinen kalenterin aikavyöhykeasiakirja, joka sisältää VTIMEZONE- ja TZID-tiedot.",
+ "invalidCollectionValue": "Korjaa tämä arvo ennen tallentamista.",
+ "accountAlreadyConnected": "Tämä tili on jo yhdistetty. Käytä sen uudelleenyhdistämistoimintoa.",
+ "nextcloudRemovalAuthorizationDescription": "BusyMax yrittää myös perua tämän tilin Nextcloud-sovellussalasanan. Paikallinen poisto voi onnistua, vaikka peruminen epäonnistuu."
}
diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb
index 209afdd7..2f216fd3 100644
--- a/lib/l10n/app_fr.arb
+++ b/lib/l10n/app_fr.arb
@@ -1,4 +1,5 @@
{
+ "contactsAccountsUnavailable": "Impossible de charger les comptes de contacts",
"contactsTitle": "Carnet d’adresses",
"contactsLinkedDescription": "Utiliser les contacts en cache de ce compte lors de l’ajout d’invités aux événements.",
"contactsEnableSuggestions": "Activer les suggestions de contacts",
@@ -1155,7 +1156,7 @@
"windowMinimize": "Réduire",
"windowMaximize": "Agrandir",
"windowRestore": "Restaurer",
- "openSharedCalendar": "Ouvrir un calendrier partagé",
+ "openSharedCalendar": "Ouvrir un calendrier principal partagé",
"calendarOwnerEmail": "Adresse e-mail du propriétaire du calendrier",
"addCalendarShare": "Ajouter un accès",
"calendarShareFreeBusy": "Disponibilité uniquement",
@@ -1359,5 +1360,49 @@
}
},
"googleIdentityUnverified": "L’identité de cette ancienne connexion Google n’a pas pu être vérifiée. Les calendriers, tâches et modifications en attente sont conservés. Ajoutez une connexion Google distincte pour synchroniser en toute sécurité. Les modifications en attente restent dans le compte d’origine.",
- "addSeparateGoogleAccount": "Ajouter une connexion Google distincte"
+ "addSeparateGoogleAccount": "Ajouter une connexion Google distincte",
+ "seriesPatternChangeConfirmation": "Cette modification du schéma de répétition peut ajouter ou supprimer des dates.\n\nActuel : {before}\nProposé : {after}\n\nEnregistrer ce schéma ?",
+ "connectedAccounts": "Comptes connectés",
+ "contactsIndependentAccounts": "Comptes de contacts indépendants",
+ "contactsNextcloudReuse": "Les suggestions réutilisent les identifiants de ce compte via l’intégration de contacts en lecture seule.",
+ "contactsRetrySync": "Réessayer la synchronisation des contacts",
+ "contactsPreferenceFailed": "Impossible d’enregistrer la préférence de la source de contacts.",
+ "contactsSuggestionsDisabled": "Suggestions de contacts désactivées",
+ "contactsSyncFailed": "Les contacts n’ont pas pu être synchronisés. Réessayez ou reconnectez le compte si l’autorisation a expiré.",
+ "registrationRecoveryDescription": "L’inscription enregistrée ou son association aux identifiants est indisponible ou refusée. Remplacez explicitement l’inscription de ce compte pour le récupérer ; les données sont conservées.",
+ "revalidateAccount": "Revérifier la connexion",
+ "openSharedPrimaryDescription": "Ouvre le calendrier principal du propriétaire indiqué. Les autres calendriers partagés sont détectés lors de la synchronisation habituelle.",
+ "showInBusyMax": "Afficher dans BusyMax",
+ "showInGoogleCalendarList": "Afficher dans la liste de calendriers Google",
+ "googleVisibilityHelp": "La sélection BusyMax est locale. La visibilité Google modifie votre liste chez Google ; les calendriers masqués ne peuvent pas être affichés dans BusyMax.",
+ "primaryCalendarVisibilityExplanation": "Le calendrier principal Google reste dans sa liste.",
+ "accountsLoading": "Chargement des comptes…",
+ "accountsLoadFailed": "Impossible de charger les comptes. Rouvrez Comptes.",
+ "collectionsLoading": "Chargement des collections…",
+ "collectionsLoadFailed": "Impossible de charger les collections. Le cache est conservé ; réessayez la synchronisation.",
+ "collectionsNotDiscovered": "La découverte n’est pas terminée. Actualisez ou reconnectez ce compte.",
+ "collectionsEmpty": "Aucune collection trouvée.",
+ "accountReadyToSync": "Prêt à synchroniser",
+ "accountSyncProblem": "La synchronisation nécessite une intervention",
+ "accountServiceUnreachable": "Impossible de joindre le service",
+ "accountSyncDetails": "Détails de synchronisation",
+ "accountReconnectStatus": "Reconnexion nécessaire",
+ "accountRegistrationStatus": "L’enregistrement nécessite une intervention",
+ "collectionsUnavailable": "Collections indisponibles",
+ "accountContactSuggestions": "Suggérer des invités à partir des contacts de ce compte",
+ "accountLoadingSignInSettings": "Chargement des paramètres de connexion…",
+ "contactsNoAddressBooks": "Aucun carnet d’adresses disponible",
+ "contactsSourcesUnavailable": "Impossible de charger les carnets d’adresses",
+ "syncNoRecord": "Aucune date de synchronisation réussie enregistrée",
+ "cachedSyncError": "Échec de synchronisation ; les données en cache peuvent être anciennes.",
+ "calendarRemotePending": "Modification de visibilité Google en attente",
+ "calendarRemoteFailed": "La modification de visibilité dans Google a échoué. Le dernier état confirmé de Google est affiché. BusyMax suit toujours la modification en attente. Réessayez ou abandonnez cette modification.",
+ "discardCalendarSettingsConfirmation": "Abandonner les modifications en attente pour « {calendar} » ?\n\nCes paramètres retrouveront leurs valeurs précédentes :\n{settings}\n\nVotre sélection de calendriers dans BusyMax est conservée. Aucune modification ne sera envoyée à Google.",
+ "sharedCalendarUnavailable": "Accès indisponible ; affichage du cache. Réessayez la synchronisation ou retirez le calendrier de votre liste.",
+ "invalidCollectionColor": "Saisissez une couleur hexadécimale de six ou huit chiffres commençant par #.",
+ "invalidCollectionOrder": "Saisissez un nombre entier.",
+ "invalidCollectionTimezone": "Saisissez un document de fuseau horaire valide contenant VTIMEZONE et TZID.",
+ "invalidCollectionValue": "Corrigez cette valeur avant d’enregistrer.",
+ "accountAlreadyConnected": "Ce compte est déjà connecté. Utilisez son action de reconnexion.",
+ "nextcloudRemovalAuthorizationDescription": "BusyMax tentera aussi de révoquer le mot de passe d’application Nextcloud de ce compte. La suppression locale peut réussir même si cette révocation échoue."
}
diff --git a/lib/l10n/app_hi.arb b/lib/l10n/app_hi.arb
index c6d3f0b4..f9d80641 100644
--- a/lib/l10n/app_hi.arb
+++ b/lib/l10n/app_hi.arb
@@ -1,4 +1,5 @@
{
+ "contactsAccountsUnavailable": "संपर्क खाते लोड नहीं किए जा सके",
"contactsTitle": "संपर्क",
"contactsLinkedDescription": "इवेंट में अतिथि जोड़ते समय इस खाते के कैश किए गए संपर्कों का उपयोग करें।",
"contactsEnableSuggestions": "संपर्क सुझाव चालू करें",
@@ -1092,7 +1093,7 @@
"windowMinimize": "छोटा करें",
"windowMaximize": "अधिकतम करें",
"windowRestore": "पुनर्स्थापित करें",
- "openSharedCalendar": "साझा कैलेंडर खोलें",
+ "openSharedCalendar": "साझा मुख्य कैलेंडर खोलें",
"calendarOwnerEmail": "कैलेंडर स्वामी का ईमेल",
"addCalendarShare": "पहुँच जोड़ें",
"calendarShareFreeBusy": "केवल खाली/व्यस्त",
@@ -1296,5 +1297,49 @@
}
},
"googleIdentityUnverified": "इस पुराने Google कनेक्शन की पहचान सत्यापित नहीं हो सकी। कैलेंडर, कार्य और लंबित बदलाव सुरक्षित हैं। सुरक्षित सिंक के लिए अलग Google कनेक्शन जोड़ें। लंबित बदलाव मूल खाते में रहेंगे।",
- "addSeparateGoogleAccount": "अलग Google कनेक्शन जोड़ें"
+ "addSeparateGoogleAccount": "अलग Google कनेक्शन जोड़ें",
+ "seriesPatternChangeConfirmation": "दोहराव का यह बदलाव तारीखें जोड़ या हटा सकता है।\n\nवर्तमान: {before}\nप्रस्तावित: {after}\n\nयह पैटर्न सहेजें?",
+ "connectedAccounts": "जुड़े हुए खाते",
+ "contactsIndependentAccounts": "स्वतंत्र संपर्क खाते",
+ "contactsNextcloudReuse": "संपर्क सुझाव केवल पढ़ने वाले संपर्क एकीकरण के माध्यम से इस खाते के मौजूदा क्रेडेंशियल का दोबारा उपयोग करते हैं।",
+ "contactsRetrySync": "संपर्क सिंक फिर आज़माएँ",
+ "contactsPreferenceFailed": "संपर्क स्रोत की प्राथमिकता सहेजी नहीं जा सकी।",
+ "contactsSuggestionsDisabled": "संपर्क सुझाव बंद हैं",
+ "contactsSyncFailed": "संपर्क सिंक नहीं हो सके। फिर आज़माएँ, या अनुमति समाप्त होने पर खाता फिर जोड़ें।",
+ "registrationRecoveryDescription": "सहेजा गया पंजीकरण या उसका क्रेडेंशियल संबंध उपलब्ध नहीं है या अस्वीकार कर दिया गया है। बहाली के लिए इस खाते का पंजीकरण स्पष्ट रूप से बदलें; मौजूदा डेटा सुरक्षित रहेगा।",
+ "revalidateAccount": "कनेक्शन फिर सत्यापित करें",
+ "openSharedPrimaryDescription": "निर्दिष्ट मालिक का मुख्य कैलेंडर खोलता है। अन्य साझा कैलेंडर सामान्य खाता सिंक में खोजे जाते हैं।",
+ "showInBusyMax": "BusyMax में दिखाएँ",
+ "showInGoogleCalendarList": "Google की कैलेंडर सूची में दिखाएँ",
+ "googleVisibilityHelp": "BusyMax का चयन स्थानीय है। Google दृश्यता Google पर आपकी सूची बदलती है; छिपे कैलेंडर BusyMax में नहीं दिखाए जा सकते।",
+ "primaryCalendarVisibilityExplanation": "Google का मुख्य कैलेंडर उसकी सूची में रहता है।",
+ "accountsLoading": "खाते लोड हो रहे हैं…",
+ "accountsLoadFailed": "खाते लोड नहीं हो सके। खाते फिर खोलें।",
+ "collectionsLoading": "संग्रह लोड हो रहे हैं…",
+ "collectionsLoadFailed": "संग्रह लोड नहीं हो सके। कैश डेटा सुरक्षित है; सिंक फिर आज़माएँ।",
+ "collectionsNotDiscovered": "खोज पूरी नहीं हुई है। इस खाते को रीफ़्रेश करें या फिर जोड़ें।",
+ "collectionsEmpty": "कोई संग्रह नहीं मिला।",
+ "accountReadyToSync": "सिंक करने के लिए तैयार",
+ "accountSyncProblem": "सिंक पर ध्यान देना आवश्यक है",
+ "accountServiceUnreachable": "सेवा से संपर्क नहीं हो सका",
+ "accountSyncDetails": "सिंक का विवरण",
+ "accountReconnectStatus": "दोबारा कनेक्ट करना आवश्यक है",
+ "accountRegistrationStatus": "ऐप पंजीकरण पर ध्यान देना आवश्यक है",
+ "collectionsUnavailable": "संग्रह उपलब्ध नहीं हैं",
+ "accountContactSuggestions": "इस खाते के संपर्कों से मेहमान सुझाएँ",
+ "accountLoadingSignInSettings": "साइन-इन सेटिंग लोड हो रही हैं…",
+ "contactsNoAddressBooks": "कोई पता पुस्तिका उपलब्ध नहीं है",
+ "contactsSourcesUnavailable": "पता पुस्तिकाएँ लोड नहीं हो सकीं",
+ "syncNoRecord": "सफल सिंक का समय दर्ज नहीं है",
+ "cachedSyncError": "सिंक विफल हुआ; कैश डेटा पुराना हो सकता है।",
+ "calendarRemotePending": "Google दृश्यता बदलाव लंबित है",
+ "calendarRemoteFailed": "Google की दृश्यता बदलना विफल रहा। Google की पिछली पुष्ट स्थिति दिखाई गई है। BusyMax अभी भी लंबित बदलाव का पालन करता है। फिर से कोशिश करें या बदलाव छोड़ दें।",
+ "discardCalendarSettingsConfirmation": "“{calendar}” के लंबित बदलाव छोड़ें?\n\nये सेटिंग अपने पिछले मानों पर लौट जाएँगी:\n{settings}\n\nBusyMax में आपका कैलेंडर चयन बना रहेगा। Google को कोई बदलाव नहीं भेजा जाएगा।",
+ "sharedCalendarUnavailable": "पहुँच उपलब्ध नहीं; कैश डेटा दिखाया जा रहा है। सिंक फिर आज़माएँ या कैलेंडर को अपनी सूची से हटाएँ।",
+ "invalidCollectionColor": "# से शुरू होने वाला छह या आठ अंकों का हेक्स रंग दर्ज करें।",
+ "invalidCollectionOrder": "एक पूर्णांक दर्ज करें।",
+ "invalidCollectionTimezone": "VTIMEZONE और TZID वाला मान्य कैलेंडर समय क्षेत्र दस्तावेज़ दर्ज करें।",
+ "invalidCollectionValue": "सहेजने से पहले यह मान सुधारें।",
+ "accountAlreadyConnected": "यह खाता पहले से जुड़ा है। इसके फिर से जोड़ने वाले विकल्प का उपयोग करें।",
+ "nextcloudRemovalAuthorizationDescription": "BusyMax इस खाते का Nextcloud ऐप पासवर्ड निरस्त करने का भी प्रयास करेगा। निरस्तीकरण विफल होने पर भी स्थानीय हटाना सफल हो सकता है।"
}
diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb
index 21bb79c1..0372f7ae 100644
--- a/lib/l10n/app_it.arb
+++ b/lib/l10n/app_it.arb
@@ -1,4 +1,5 @@
{
+ "contactsAccountsUnavailable": "Impossibile caricare gli account dei contatti",
"contactsTitle": "Contatti",
"contactsLinkedDescription": "Usa i contatti memorizzati di questo account quando aggiungi invitati agli eventi.",
"contactsEnableSuggestions": "Attiva i suggerimenti dei contatti",
@@ -1092,7 +1093,7 @@
"windowMinimize": "Riduci",
"windowMaximize": "Ingrandisci",
"windowRestore": "Ripristina",
- "openSharedCalendar": "Apri calendario condiviso",
+ "openSharedCalendar": "Apri calendario principale condiviso",
"calendarOwnerEmail": "Email del proprietario del calendario",
"addCalendarShare": "Aggiungi accesso",
"calendarShareFreeBusy": "Solo libero/occupato",
@@ -1296,5 +1297,49 @@
}
},
"googleIdentityUnverified": "Non è stato possibile verificare l’identità di questa vecchia connessione Google. Calendari, attività e modifiche in sospeso sono conservati. Aggiungi una connessione Google separata per sincronizzare in sicurezza. Le modifiche in sospeso rimarranno nell’account originale.",
- "addSeparateGoogleAccount": "Aggiungi connessione Google separata"
+ "addSeparateGoogleAccount": "Aggiungi connessione Google separata",
+ "seriesPatternChangeConfirmation": "Questa modifica della ricorrenza può aggiungere o rimuovere date.\n\nAttuale: {before}\nProposta: {after}\n\nSalvare questa ricorrenza?",
+ "connectedAccounts": "Account collegati",
+ "contactsIndependentAccounts": "Account di contatti indipendenti",
+ "contactsNextcloudReuse": "I suggerimenti riutilizzano le credenziali di questo account tramite l’integrazione dei contatti in sola lettura.",
+ "contactsRetrySync": "Riprova sincronizzazione contatti",
+ "contactsPreferenceFailed": "Impossibile salvare la preferenza della fonte dei contatti.",
+ "contactsSuggestionsDisabled": "Suggerimenti dei contatti disattivati",
+ "contactsSyncFailed": "Impossibile sincronizzare i contatti. Riprova oppure ricollega l’account se l’autorizzazione è scaduta.",
+ "registrationRecoveryDescription": "La registrazione salvata o il collegamento alle credenziali non è disponibile o è stato rifiutato. Sostituisci esplicitamente la registrazione di questo account per recuperarlo; i dati vengono conservati.",
+ "revalidateAccount": "Verifica di nuovo la connessione",
+ "openSharedPrimaryDescription": "Apre il calendario principale del proprietario specificato. Gli altri calendari condivisi vengono rilevati tramite la normale sincronizzazione.",
+ "showInBusyMax": "Mostra in BusyMax",
+ "showInGoogleCalendarList": "Mostra nell’elenco dei calendari Google",
+ "googleVisibilityHelp": "La selezione di BusyMax è locale. La visibilità Google modifica il tuo elenco su Google; i calendari nascosti non possono essere visualizzati in BusyMax.",
+ "primaryCalendarVisibilityExplanation": "Il calendario principale Google rimane nel suo elenco.",
+ "accountsLoading": "Caricamento account…",
+ "accountsLoadFailed": "Impossibile caricare gli account. Riapri Account.",
+ "collectionsLoading": "Caricamento raccolte…",
+ "collectionsLoadFailed": "Impossibile caricare le raccolte. I dati in cache sono conservati; riprova la sincronizzazione.",
+ "collectionsNotDiscovered": "Il rilevamento non è completo. Aggiorna o ricollega questo account.",
+ "collectionsEmpty": "Nessuna raccolta trovata.",
+ "accountReadyToSync": "Pronto per la sincronizzazione",
+ "accountSyncProblem": "La sincronizzazione richiede attenzione",
+ "accountServiceUnreachable": "Impossibile raggiungere il servizio",
+ "accountSyncDetails": "Dettagli di sincronizzazione",
+ "accountReconnectStatus": "È necessario riconnettere l’account",
+ "accountRegistrationStatus": "La registrazione richiede attenzione",
+ "collectionsUnavailable": "Raccolte non disponibili",
+ "accountContactSuggestions": "Suggerisci invitati dai contatti di questo account",
+ "accountLoadingSignInSettings": "Caricamento delle impostazioni di accesso…",
+ "contactsNoAddressBooks": "Nessuna rubrica disponibile",
+ "contactsSourcesUnavailable": "Impossibile caricare le rubriche",
+ "syncNoRecord": "Nessuna data di sincronizzazione riuscita registrata",
+ "cachedSyncError": "Sincronizzazione non riuscita; i dati in cache potrebbero essere obsoleti.",
+ "calendarRemotePending": "Modifica visibilità Google in attesa",
+ "calendarRemoteFailed": "La modifica della visibilità su Google non è riuscita. Viene mostrato l’ultimo stato confermato da Google. BusyMax continua a seguire la modifica in sospeso. Riprova o scarta la modifica.",
+ "discardCalendarSettingsConfirmation": "Scartare le modifiche in attesa per «{calendar}»?\n\nQueste impostazioni torneranno ai valori precedenti:\n{settings}\n\nLa selezione dei calendari in BusyMax viene mantenuta. Non verranno inviate modifiche a Google.",
+ "sharedCalendarUnavailable": "Accesso non disponibile; vengono mostrati i dati in cache. Riprova o rimuovi il calendario dal tuo elenco.",
+ "invalidCollectionColor": "Inserisci un colore esadecimale di sei o otto cifre che inizi con #.",
+ "invalidCollectionOrder": "Inserisci un numero intero.",
+ "invalidCollectionTimezone": "Inserisci un documento di fuso orario valido contenente VTIMEZONE e TZID.",
+ "invalidCollectionValue": "Correggi questo valore prima di salvare.",
+ "accountAlreadyConnected": "Questo account è già collegato. Usa la sua azione di riconnessione.",
+ "nextcloudRemovalAuthorizationDescription": "BusyMax tenterà anche di revocare la password per app Nextcloud di questo account. La rimozione locale può riuscire anche se la revoca non riesce."
}
diff --git a/lib/l10n/app_ja.arb b/lib/l10n/app_ja.arb
index 5d53ff2b..665af566 100644
--- a/lib/l10n/app_ja.arb
+++ b/lib/l10n/app_ja.arb
@@ -1,4 +1,5 @@
{
+ "contactsAccountsUnavailable": "連絡先アカウントを読み込めませんでした",
"contactsTitle": "連絡先",
"contactsLinkedDescription": "予定のゲストを追加するときに、このアカウントのキャッシュ済み連絡先を使用します。",
"contactsEnableSuggestions": "連絡先の候補を有効にする",
@@ -1092,7 +1093,7 @@
"windowMinimize": "最小化",
"windowMaximize": "最大化",
"windowRestore": "元に戻す",
- "openSharedCalendar": "共有カレンダーを開く",
+ "openSharedCalendar": "共有メインカレンダーを開く",
"calendarOwnerEmail": "カレンダー所有者のメールアドレス",
"addCalendarShare": "アクセスを追加",
"calendarShareFreeBusy": "空き時間情報のみ",
@@ -1296,5 +1297,49 @@
}
},
"googleIdentityUnverified": "この古い Google 接続のアカウントを確認できませんでした。カレンダー、タスク、保留中の変更は保持されています。安全に同期するには、別の Google 接続を追加してください。保留中の変更は元のアカウントに残ります。",
- "addSeparateGoogleAccount": "別の Google 接続を追加"
+ "addSeparateGoogleAccount": "別の Google 接続を追加",
+ "seriesPatternChangeConfirmation": "繰り返しパターンの変更により、日付が追加または削除される場合があります。\n\n現在:{before}\n変更案:{after}\n\nこのパターンを保存しますか?",
+ "connectedAccounts": "接続済みアカウント",
+ "contactsIndependentAccounts": "独立した連絡先アカウント",
+ "contactsNextcloudReuse": "連絡先候補は、読み取り専用の連絡先連携を通じて、このアカウントの既存の認証情報を再利用します。",
+ "contactsRetrySync": "連絡先の同期を再試行",
+ "contactsPreferenceFailed": "連絡先ソースの設定を保存できませんでした。",
+ "contactsSuggestionsDisabled": "連絡先候補は無効です",
+ "contactsSyncFailed": "連絡先を同期できませんでした。再試行するか、認可が期限切れの場合はアカウントを再接続してください。",
+ "registrationRecoveryDescription": "保存済みのアプリ登録または認証情報との関連付けが利用できないか、拒否されています。復旧するには、このアカウントの登録を明示的に置き換えてください。既存のデータは保持されます。",
+ "revalidateAccount": "接続を再検証",
+ "openSharedPrimaryDescription": "指定した所有者のメインカレンダーを開きます。他の共有カレンダーは通常のアカウント同期で検出されます。",
+ "showInBusyMax": "BusyMax に表示",
+ "showInGoogleCalendarList": "Google のカレンダーリストに表示",
+ "googleVisibilityHelp": "BusyMax の選択はローカル設定です。Google の表示設定は Google 側のリストを変更します。非表示のカレンダーは BusyMax に表示できません。",
+ "primaryCalendarVisibilityExplanation": "Google のメインカレンダーはリストに残ります。",
+ "accountsLoading": "アカウントを読み込み中…",
+ "accountsLoadFailed": "アカウントを読み込めませんでした。「アカウント」を開き直してください。",
+ "collectionsLoading": "コレクションを読み込み中…",
+ "collectionsLoadFailed": "コレクションを読み込めませんでした。キャッシュは保持されています。同期を再試行してください。",
+ "collectionsNotDiscovered": "検出が完了していません。このアカウントを更新または再接続してください。",
+ "collectionsEmpty": "コレクションが見つかりません。",
+ "accountReadyToSync": "同期の準備完了",
+ "accountSyncProblem": "同期への対応が必要",
+ "accountServiceUnreachable": "サービスに接続できませんでした",
+ "accountSyncDetails": "同期の詳細",
+ "accountReconnectStatus": "再接続が必要",
+ "accountRegistrationStatus": "アプリ登録への対応が必要",
+ "collectionsUnavailable": "コレクションを利用できません",
+ "accountContactSuggestions": "このアカウントの連絡先からゲストを候補に表示",
+ "accountLoadingSignInSettings": "サインイン設定を読み込み中…",
+ "contactsNoAddressBooks": "利用できるアドレス帳がありません",
+ "contactsSourcesUnavailable": "アドレス帳を読み込めませんでした",
+ "syncNoRecord": "成功した同期時刻の記録がありません",
+ "cachedSyncError": "同期に失敗しました。キャッシュのデータが古い可能性があります。",
+ "calendarRemotePending": "Google の表示設定の変更を待機中",
+ "calendarRemoteFailed": "Google での表示設定の変更に失敗しました。Google で最後に確認された状態が表示されています。BusyMax は引き続き保留中の変更に従います。再試行するか、変更を破棄してください。",
+ "discardCalendarSettingsConfirmation": "「{calendar}」の保留中の変更を破棄しますか?\n\n次の設定を以前の値に戻します:\n{settings}\n\nBusyMaxでのカレンダーの選択は保持されます。Googleに変更は送信されません。",
+ "sharedCalendarUnavailable": "アクセスできません。キャッシュを表示しています。同期を再試行するか、リストから削除してください。",
+ "invalidCollectionColor": "# で始まる6桁または8桁の16進数の色を入力してください。",
+ "invalidCollectionOrder": "整数を入力してください。",
+ "invalidCollectionTimezone": "VTIMEZONE と TZID を含む有効なカレンダーのタイムゾーン文書を入力してください。",
+ "invalidCollectionValue": "保存する前にこの値を修正してください。",
+ "accountAlreadyConnected": "このアカウントは既に接続されています。このアカウントの再接続操作を使用してください。",
+ "nextcloudRemovalAuthorizationDescription": "BusyMax はこのアカウントの Nextcloud アプリパスワードの失効も試みます。失効に失敗しても、ローカルでの削除は完了する場合があります。"
}
diff --git a/lib/l10n/app_ko.arb b/lib/l10n/app_ko.arb
index d53385ee..7efe6e04 100644
--- a/lib/l10n/app_ko.arb
+++ b/lib/l10n/app_ko.arb
@@ -1,4 +1,5 @@
{
+ "contactsAccountsUnavailable": "연락처 계정을 불러올 수 없습니다",
"contactsTitle": "연락처",
"contactsLinkedDescription": "이벤트 참석자를 추가할 때 이 계정에 캐시된 연락처를 사용합니다.",
"contactsEnableSuggestions": "연락처 추천 사용",
@@ -1092,7 +1093,7 @@
"windowMinimize": "최소화",
"windowMaximize": "최대화",
"windowRestore": "복원",
- "openSharedCalendar": "공유 캘린더 열기",
+ "openSharedCalendar": "공유 기본 캘린더 열기",
"calendarOwnerEmail": "캘린더 소유자 이메일",
"addCalendarShare": "액세스 추가",
"calendarShareFreeBusy": "한가함/바쁨만 보기",
@@ -1296,5 +1297,49 @@
}
},
"googleIdentityUnverified": "이 오래된 Google 연결의 계정 신원을 확인할 수 없습니다. 캘린더, 할 일 및 대기 중인 변경 사항은 유지됩니다. 안전한 동기화를 위해 별도의 Google 연결을 추가하세요. 대기 중인 변경 사항은 원래 계정에 남습니다.",
- "addSeparateGoogleAccount": "별도의 Google 연결 추가"
+ "addSeparateGoogleAccount": "별도의 Google 연결 추가",
+ "seriesPatternChangeConfirmation": "반복 패턴을 변경하면 날짜가 추가되거나 삭제될 수 있습니다.\n\n현재: {before}\n제안: {after}\n\n이 패턴을 저장할까요?",
+ "connectedAccounts": "연결된 계정",
+ "contactsIndependentAccounts": "독립 연락처 계정",
+ "contactsNextcloudReuse": "연락처 제안은 읽기 전용 연락처 통합을 통해 이 계정의 기존 자격 증명을 재사용합니다.",
+ "contactsRetrySync": "연락처 동기화 다시 시도",
+ "contactsPreferenceFailed": "연락처 소스 설정을 저장하지 못했습니다.",
+ "contactsSuggestionsDisabled": "연락처 제안 꺼짐",
+ "contactsSyncFailed": "연락처를 동기화하지 못했습니다. 다시 시도하거나 권한이 만료된 경우 계정을 다시 연결하세요.",
+ "registrationRecoveryDescription": "저장된 앱 등록 또는 자격 증명 연결을 사용할 수 없거나 거부되었습니다. 복구하려면 이 계정의 등록을 명시적으로 교체하세요. 기존 데이터는 유지됩니다.",
+ "revalidateAccount": "연결 다시 검증",
+ "openSharedPrimaryDescription": "지정한 소유자의 기본 캘린더를 엽니다. 다른 공유 캘린더는 일반 계정 동기화에서 검색됩니다.",
+ "showInBusyMax": "BusyMax에 표시",
+ "showInGoogleCalendarList": "Google 캘린더 목록에 표시",
+ "googleVisibilityHelp": "BusyMax 선택은 로컬 설정입니다. Google 표시 설정은 Google의 목록을 변경하며 숨겨진 캘린더는 BusyMax에 표시할 수 없습니다.",
+ "primaryCalendarVisibilityExplanation": "Google 기본 캘린더는 목록에 유지됩니다.",
+ "accountsLoading": "계정 불러오는 중…",
+ "accountsLoadFailed": "계정을 불러오지 못했습니다. 계정을 다시 여세요.",
+ "collectionsLoading": "컬렉션 불러오는 중…",
+ "collectionsLoadFailed": "컬렉션을 불러오지 못했습니다. 캐시는 유지됩니다. 동기화를 다시 시도하세요.",
+ "collectionsNotDiscovered": "검색이 완료되지 않았습니다. 이 계정을 새로 고치거나 다시 연결하세요.",
+ "collectionsEmpty": "컬렉션을 찾지 못했습니다.",
+ "accountReadyToSync": "동기화 준비됨",
+ "accountSyncProblem": "동기화에 조치가 필요합니다",
+ "accountServiceUnreachable": "서비스에 연결할 수 없습니다",
+ "accountSyncDetails": "동기화 세부 정보",
+ "accountReconnectStatus": "다시 연결해야 합니다",
+ "accountRegistrationStatus": "앱 등록에 조치가 필요합니다",
+ "collectionsUnavailable": "컬렉션을 사용할 수 없습니다",
+ "accountContactSuggestions": "이 계정의 연락처에서 참석자 제안",
+ "accountLoadingSignInSettings": "로그인 설정을 불러오는 중…",
+ "contactsNoAddressBooks": "사용 가능한 주소록이 없습니다",
+ "contactsSourcesUnavailable": "주소록을 불러올 수 없습니다",
+ "syncNoRecord": "성공한 동기화 시간이 기록되지 않음",
+ "cachedSyncError": "동기화 실패. 캐시 데이터가 오래되었을 수 있습니다.",
+ "calendarRemotePending": "Google 표시 변경 대기 중",
+ "calendarRemoteFailed": "Google 표시 설정 변경에 실패했습니다. 마지막으로 확인된 Google 상태가 표시됩니다. BusyMax는 계속 대기 중인 변경을 따릅니다. 다시 시도하거나 변경을 취소하세요.",
+ "discardCalendarSettingsConfirmation": "“{calendar}”의 대기 중인 변경사항을 취소할까요?\n\n다음 설정이 이전 값으로 복원됩니다:\n{settings}\n\nBusyMax의 캘린더 선택은 유지됩니다. Google에 변경사항이 전송되지 않습니다.",
+ "sharedCalendarUnavailable": "접근할 수 없어 캐시를 표시합니다. 동기화를 다시 시도하거나 캘린더를 목록에서 제거하세요.",
+ "invalidCollectionColor": "#으로 시작하는 6자리 또는 8자리 16진수 색상을 입력하세요.",
+ "invalidCollectionOrder": "정수를 입력하세요.",
+ "invalidCollectionTimezone": "VTIMEZONE 및 TZID를 포함하는 올바른 캘린더 시간대 문서를 입력하세요.",
+ "invalidCollectionValue": "저장하기 전에 이 값을 수정하세요.",
+ "accountAlreadyConnected": "이 계정은 이미 연결되어 있습니다. 해당 계정의 다시 연결 작업을 사용하세요.",
+ "nextcloudRemovalAuthorizationDescription": "BusyMax는 이 계정의 Nextcloud 앱 암호 해지도 시도합니다. 해지에 실패해도 로컬 제거는 완료될 수 있습니다."
}
diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb
index f6a4543e..990d613d 100644
--- a/lib/l10n/app_pl.arb
+++ b/lib/l10n/app_pl.arb
@@ -1,4 +1,5 @@
{
+ "contactsAccountsUnavailable": "Nie udało się wczytać kont kontaktów",
"contactsTitle": "Kontakty",
"contactsLinkedDescription": "Używaj kontaktów z pamięci podręcznej tego konta podczas dodawania gości wydarzenia.",
"contactsEnableSuggestions": "Włącz sugestie kontaktów",
@@ -1773,7 +1774,7 @@
"windowMinimize": "Minimalizuj",
"windowMaximize": "Maksymalizuj",
"windowRestore": "Przywróć",
- "openSharedCalendar": "Otwórz udostępniony kalendarz",
+ "openSharedCalendar": "Otwórz udostępniony kalendarz główny",
"calendarOwnerEmail": "Adres e-mail właściciela kalendarza",
"addCalendarShare": "Dodaj dostęp",
"calendarShareFreeBusy": "Tylko wolny/zajęty",
@@ -1977,5 +1978,49 @@
}
},
"googleIdentityUnverified": "Nie można zweryfikować tożsamości tego starszego połączenia Google. Kalendarze, zadania i oczekujące zmiany są zachowane. Dodaj osobne połączenie Google, aby bezpiecznie synchronizować. Oczekujące zmiany pozostaną na pierwotnym koncie.",
- "addSeparateGoogleAccount": "Dodaj osobne połączenie Google"
+ "addSeparateGoogleAccount": "Dodaj osobne połączenie Google",
+ "seriesPatternChangeConfirmation": "Ta zmiana reguły powtarzania może dodać lub usunąć daty.\n\nObecnie: {before}\nPropozycja: {after}\n\nZapisać tę regułę?",
+ "connectedAccounts": "Połączone konta",
+ "contactsIndependentAccounts": "Niezależne konta kontaktów",
+ "contactsNextcloudReuse": "Sugestie używają istniejących danych logowania tego konta przez integrację kontaktów tylko do odczytu.",
+ "contactsRetrySync": "Ponów synchronizację kontaktów",
+ "contactsPreferenceFailed": "Nie udało się zapisać ustawienia źródła kontaktów.",
+ "contactsSuggestionsDisabled": "Sugestie kontaktów wyłączone",
+ "contactsSyncFailed": "Nie udało się zsynchronizować kontaktów. Spróbuj ponownie lub połącz konto ponownie, jeśli autoryzacja wygasła.",
+ "registrationRecoveryDescription": "Zapisana rejestracja lub powiązanie danych logowania jest niedostępne albo odrzucone. Jawnie zastąp rejestrację tego konta, aby je odzyskać; dane zostaną zachowane.",
+ "revalidateAccount": "Sprawdź połączenie ponownie",
+ "openSharedPrimaryDescription": "Otwiera główny kalendarz wskazanego właściciela. Inne udostępnione kalendarze są wykrywane podczas zwykłej synchronizacji konta.",
+ "showInBusyMax": "Pokaż w BusyMax",
+ "showInGoogleCalendarList": "Pokaż na liście kalendarzy Google",
+ "googleVisibilityHelp": "Wybór BusyMax jest lokalny. Widoczność Google zmienia listę w Google; ukrytych kalendarzy nie można wyświetlić w BusyMax.",
+ "primaryCalendarVisibilityExplanation": "Główny kalendarz Google pozostaje na liście.",
+ "accountsLoading": "Ładowanie kont…",
+ "accountsLoadFailed": "Nie udało się załadować kont. Otwórz Konta ponownie.",
+ "collectionsLoading": "Ładowanie kolekcji…",
+ "collectionsLoadFailed": "Nie udało się załadować kolekcji. Pamięć podręczna jest zachowana; ponów synchronizację.",
+ "collectionsNotDiscovered": "Wykrywanie nie zostało zakończone. Odśwież lub połącz konto ponownie.",
+ "collectionsEmpty": "Nie znaleziono kolekcji.",
+ "accountReadyToSync": "Gotowe do synchronizacji",
+ "accountSyncProblem": "Synchronizacja wymaga uwagi",
+ "accountServiceUnreachable": "Nie można połączyć się z usługą",
+ "accountSyncDetails": "Szczegóły synchronizacji",
+ "accountReconnectStatus": "Wymagane ponowne połączenie",
+ "accountRegistrationStatus": "Rejestracja aplikacji wymaga uwagi",
+ "collectionsUnavailable": "Kolekcje niedostępne",
+ "accountContactSuggestions": "Sugeruj gości z kontaktów tego konta",
+ "accountLoadingSignInSettings": "Wczytywanie ustawień logowania…",
+ "contactsNoAddressBooks": "Brak dostępnych książek adresowych",
+ "contactsSourcesUnavailable": "Nie można wczytać książek adresowych",
+ "syncNoRecord": "Brak zapisanego czasu udanej synchronizacji",
+ "cachedSyncError": "Synchronizacja nie powiodła się; dane w pamięci podręcznej mogą być nieaktualne.",
+ "calendarRemotePending": "Oczekiwanie na zmianę widoczności Google",
+ "calendarRemoteFailed": "Zmiana widoczności w Google nie powiodła się. Wyświetlany jest ostatni potwierdzony stan Google. BusyMax nadal stosuje oczekującą zmianę. Ponów próbę lub odrzuć zmianę.",
+ "discardCalendarSettingsConfirmation": "Odrzucić oczekujące zmiany kalendarza „{calendar}”?\n\nTe ustawienia wrócą do poprzednich wartości:\n{settings}\n\nWybór kalendarzy w BusyMax zostanie zachowany. Żadne zmiany nie zostaną wysłane do Google.",
+ "sharedCalendarUnavailable": "Dostęp niedostępny; wyświetlane są dane z pamięci podręcznej. Ponów synchronizację lub usuń kalendarz z listy.",
+ "invalidCollectionColor": "Wpisz sześcio- lub ośmiocyfrowy kolor szesnastkowy zaczynający się od #.",
+ "invalidCollectionOrder": "Wpisz liczbę całkowitą.",
+ "invalidCollectionTimezone": "Wpisz prawidłowy dokument strefy czasowej zawierający VTIMEZONE i TZID.",
+ "invalidCollectionValue": "Popraw tę wartość przed zapisaniem.",
+ "accountAlreadyConnected": "To konto jest już połączone. Użyj jego opcji ponownego połączenia.",
+ "nextcloudRemovalAuthorizationDescription": "BusyMax spróbuje też unieważnić hasło aplikacji Nextcloud tego konta. Usunięcie lokalne może się udać nawet wtedy, gdy unieważnienie się nie powiedzie."
}
diff --git a/lib/l10n/app_pt_PT.arb b/lib/l10n/app_pt_PT.arb
index cfb5f04c..ecd11be3 100644
--- a/lib/l10n/app_pt_PT.arb
+++ b/lib/l10n/app_pt_PT.arb
@@ -1,4 +1,5 @@
{
+ "contactsAccountsUnavailable": "Não foi possível carregar as contas de contactos",
"contactsTitle": "Contactos",
"contactsLinkedDescription": "Utilize os contactos em cache desta conta ao adicionar convidados a eventos.",
"contactsEnableSuggestions": "Ativar sugestões de contactos",
@@ -1092,7 +1093,7 @@
"windowMinimize": "Minimizar",
"windowMaximize": "Maximizar",
"windowRestore": "Restaurar",
- "openSharedCalendar": "Abrir calendário partilhado",
+ "openSharedCalendar": "Abrir calendário principal partilhado",
"calendarOwnerEmail": "E-mail do proprietário do calendário",
"addCalendarShare": "Adicionar acesso",
"calendarShareFreeBusy": "Apenas livre/ocupado",
@@ -1296,5 +1297,49 @@
}
},
"googleIdentityUnverified": "Não foi possível verificar a identidade desta ligação antiga ao Google. Os calendários, tarefas e alterações pendentes são preservados. Adicione uma ligação Google separada para sincronizar em segurança. As alterações pendentes permanecerão na conta original.",
- "addSeparateGoogleAccount": "Adicionar ligação Google separada"
+ "addSeparateGoogleAccount": "Adicionar ligação Google separada",
+ "seriesPatternChangeConfirmation": "Esta alteração da recorrência pode adicionar ou remover datas.\n\nAtual: {before}\nProposta: {after}\n\nGuardar esta recorrência?",
+ "connectedAccounts": "Contas ligadas",
+ "contactsIndependentAccounts": "Contas de contactos independentes",
+ "contactsNextcloudReuse": "As sugestões reutilizam as credenciais desta conta através da integração de contactos só de leitura.",
+ "contactsRetrySync": "Repetir sincronização de contactos",
+ "contactsPreferenceFailed": "Não foi possível guardar a preferência da origem de contactos.",
+ "contactsSuggestionsDisabled": "Sugestões de contactos desativadas",
+ "contactsSyncFailed": "Não foi possível sincronizar os contactos. Tente novamente ou volte a ligar a conta se a autorização expirou.",
+ "registrationRecoveryDescription": "O registo guardado ou a associação às credenciais está indisponível ou foi rejeitado. Substitua explicitamente o registo desta conta para a recuperar; os dados são preservados.",
+ "revalidateAccount": "Revalidar ligação",
+ "openSharedPrimaryDescription": "Abre o calendário principal do proprietário indicado. Outros calendários partilhados são detetados na sincronização normal da conta.",
+ "showInBusyMax": "Mostrar no BusyMax",
+ "showInGoogleCalendarList": "Mostrar na lista de calendários do Google",
+ "googleVisibilityHelp": "A seleção do BusyMax é local. A visibilidade do Google altera a sua lista no Google; calendários ocultos não podem ser apresentados no BusyMax.",
+ "primaryCalendarVisibilityExplanation": "O calendário principal do Google permanece na lista.",
+ "accountsLoading": "A carregar contas…",
+ "accountsLoadFailed": "Não foi possível carregar as contas. Volte a abrir Contas.",
+ "collectionsLoading": "A carregar coleções…",
+ "collectionsLoadFailed": "Não foi possível carregar as coleções. Os dados em cache são preservados; repita a sincronização.",
+ "collectionsNotDiscovered": "A descoberta não terminou. Atualize ou volte a ligar esta conta.",
+ "collectionsEmpty": "Não foram encontradas coleções.",
+ "accountReadyToSync": "Pronto para sincronizar",
+ "accountSyncProblem": "A sincronização requer atenção",
+ "accountServiceUnreachable": "Não foi possível contactar o serviço",
+ "accountSyncDetails": "Detalhes de sincronização",
+ "accountReconnectStatus": "É necessário voltar a ligar",
+ "accountRegistrationStatus": "O registo requer atenção",
+ "collectionsUnavailable": "Coleções indisponíveis",
+ "accountContactSuggestions": "Sugerir convidados a partir dos contactos desta conta",
+ "accountLoadingSignInSettings": "A carregar as definições de início de sessão…",
+ "contactsNoAddressBooks": "Nenhum livro de endereços disponível",
+ "contactsSourcesUnavailable": "Não foi possível carregar os livros de endereços",
+ "syncNoRecord": "Nenhuma data de sincronização bem-sucedida registada",
+ "cachedSyncError": "A sincronização falhou; os dados em cache podem estar desatualizados.",
+ "calendarRemotePending": "Alteração de visibilidade no Google pendente",
+ "calendarRemoteFailed": "A alteração de visibilidade no Google falhou. É apresentado o último estado confirmado do Google. O BusyMax continua a seguir a alteração pendente. Tente novamente ou descarte a alteração.",
+ "discardCalendarSettingsConfirmation": "Descartar as alterações pendentes de «{calendar}»?\n\nEstas definições regressarão aos valores anteriores:\n{settings}\n\nA sua seleção de calendários no BusyMax será mantida. Não serão enviadas alterações à Google.",
+ "sharedCalendarUnavailable": "Acesso indisponível; a mostrar dados em cache. Repita a sincronização ou remova o calendário da sua lista.",
+ "invalidCollectionColor": "Introduza uma cor hexadecimal de seis ou oito dígitos, começando por #.",
+ "invalidCollectionOrder": "Introduza um número inteiro.",
+ "invalidCollectionTimezone": "Introduza um documento de fuso horário válido com VTIMEZONE e TZID.",
+ "invalidCollectionValue": "Corrija este valor antes de guardar.",
+ "accountAlreadyConnected": "Esta conta já está ligada. Utilize a ação para voltar a ligar.",
+ "nextcloudRemovalAuthorizationDescription": "O BusyMax tentará também revogar a palavra-passe de aplicação Nextcloud desta conta. A remoção local pode ser concluída mesmo que a revogação falhe."
}
diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb
index 4edfd4da..e631b9da 100644
--- a/lib/l10n/app_ru.arb
+++ b/lib/l10n/app_ru.arb
@@ -1,4 +1,5 @@
{
+ "contactsAccountsUnavailable": "Не удалось загрузить учётные записи контактов",
"contactsTitle": "Контакты",
"contactsLinkedDescription": "Использовать кэшированные контакты этой учётной записи при добавлении гостей события.",
"contactsEnableSuggestions": "Включить предложения контактов",
@@ -1092,7 +1093,7 @@
"windowMinimize": "Свернуть",
"windowMaximize": "Развернуть",
"windowRestore": "Восстановить",
- "openSharedCalendar": "Открыть общий календарь",
+ "openSharedCalendar": "Открыть общий основной календарь",
"calendarOwnerEmail": "Адрес электронной почты владельца календаря",
"addCalendarShare": "Добавить доступ",
"calendarShareFreeBusy": "Только занятость",
@@ -1296,5 +1297,49 @@
}
},
"googleIdentityUnverified": "Не удалось подтвердить личность владельца этого старого подключения Google. Календари, задачи и ожидающие изменения сохранены. Добавьте отдельное подключение Google для безопасной синхронизации. Ожидающие изменения останутся в исходном аккаунте.",
- "addSeparateGoogleAccount": "Добавить отдельное подключение Google"
+ "addSeparateGoogleAccount": "Добавить отдельное подключение Google",
+ "seriesPatternChangeConfirmation": "Изменение правила повторения может добавить или удалить даты.\n\nСейчас: {before}\nПредложено: {after}\n\nСохранить это правило?",
+ "connectedAccounts": "Подключённые аккаунты",
+ "contactsIndependentAccounts": "Независимые аккаунты контактов",
+ "contactsNextcloudReuse": "Подсказки используют существующие учётные данные этого аккаунта через интеграцию контактов только для чтения.",
+ "contactsRetrySync": "Повторить синхронизацию контактов",
+ "contactsPreferenceFailed": "Не удалось сохранить настройку источника контактов.",
+ "contactsSuggestionsDisabled": "Подсказки контактов отключены",
+ "contactsSyncFailed": "Не удалось синхронизировать контакты. Повторите попытку или переподключите аккаунт, если авторизация истекла.",
+ "registrationRecoveryDescription": "Сохранённая регистрация или привязка учётных данных недоступна либо отклонена. Для восстановления явно замените регистрацию этого аккаунта; данные сохранятся.",
+ "revalidateAccount": "Проверить подключение",
+ "openSharedPrimaryDescription": "Открывает основной календарь указанного владельца. Другие общие календари обнаруживаются при обычной синхронизации аккаунта.",
+ "showInBusyMax": "Показывать в BusyMax",
+ "showInGoogleCalendarList": "Показывать в списке календарей Google",
+ "googleVisibilityHelp": "Выбор BusyMax действует локально. Видимость Google меняет ваш список в Google; скрытые календари нельзя показать в BusyMax.",
+ "primaryCalendarVisibilityExplanation": "Основной календарь Google остаётся в списке.",
+ "accountsLoading": "Загрузка аккаунтов…",
+ "accountsLoadFailed": "Не удалось загрузить аккаунты. Откройте раздел «Аккаунты» заново.",
+ "collectionsLoading": "Загрузка коллекций…",
+ "collectionsLoadFailed": "Не удалось загрузить коллекции. Кэш сохранён; повторите синхронизацию.",
+ "collectionsNotDiscovered": "Обнаружение ещё не завершено. Обновите или переподключите аккаунт.",
+ "collectionsEmpty": "Коллекции не найдены.",
+ "accountReadyToSync": "Готово к синхронизации",
+ "accountSyncProblem": "Синхронизация требует внимания",
+ "accountServiceUnreachable": "Не удалось связаться с сервисом",
+ "accountSyncDetails": "Подробности синхронизации",
+ "accountReconnectStatus": "Требуется повторное подключение",
+ "accountRegistrationStatus": "Регистрация приложения требует внимания",
+ "collectionsUnavailable": "Коллекции недоступны",
+ "accountContactSuggestions": "Предлагать гостей из контактов этого аккаунта",
+ "accountLoadingSignInSettings": "Загрузка настроек входа…",
+ "contactsNoAddressBooks": "Нет доступных адресных книг",
+ "contactsSourcesUnavailable": "Не удалось загрузить адресные книги",
+ "syncNoRecord": "Время успешной синхронизации не записано",
+ "cachedSyncError": "Сбой синхронизации; данные в кэше могут быть устаревшими.",
+ "calendarRemotePending": "Ожидается изменение видимости в Google",
+ "calendarRemoteFailed": "Не удалось изменить видимость в Google. Показано последнее подтверждённое состояние Google. BusyMax по-прежнему учитывает ожидающее изменение. Повторите попытку или отмените изменение.",
+ "discardCalendarSettingsConfirmation": "Отменить ожидающие изменения календаря «{calendar}»?\n\nБудут восстановлены предыдущие значения этих настроек:\n{settings}\n\nВыбор календарей в BusyMax сохранится. Изменения не будут отправлены в Google.",
+ "sharedCalendarUnavailable": "Нет доступа; показаны данные из кэша. Повторите синхронизацию или удалите календарь из своего списка.",
+ "invalidCollectionColor": "Введите шестизначный или восьмизначный шестнадцатеричный цвет, начиная с #.",
+ "invalidCollectionOrder": "Введите целое число.",
+ "invalidCollectionTimezone": "Введите корректный документ часового пояса с VTIMEZONE и TZID.",
+ "invalidCollectionValue": "Исправьте это значение перед сохранением.",
+ "accountAlreadyConnected": "Этот аккаунт уже подключён. Используйте его действие переподключения.",
+ "nextcloudRemovalAuthorizationDescription": "BusyMax также попытается отозвать пароль приложения Nextcloud этого аккаунта. Локальное удаление может завершиться, даже если отзыв не удался."
}
diff --git a/lib/l10n/app_vi.arb b/lib/l10n/app_vi.arb
index 74720c74..c8525300 100644
--- a/lib/l10n/app_vi.arb
+++ b/lib/l10n/app_vi.arb
@@ -1,4 +1,5 @@
{
+ "contactsAccountsUnavailable": "Không thể tải các tài khoản danh bạ",
"contactsTitle": "Danh bạ",
"contactsLinkedDescription": "Dùng các liên hệ đã lưu đệm của tài khoản này khi thêm khách vào sự kiện.",
"contactsEnableSuggestions": "Bật gợi ý liên hệ",
@@ -1092,7 +1093,7 @@
"windowMinimize": "Thu nhỏ",
"windowMaximize": "Phóng to",
"windowRestore": "Khôi phục",
- "openSharedCalendar": "Mở lịch được chia sẻ",
+ "openSharedCalendar": "Mở lịch chính được chia sẻ",
"calendarOwnerEmail": "Email của chủ sở hữu lịch",
"addCalendarShare": "Thêm quyền truy cập",
"calendarShareFreeBusy": "Chỉ xem bận/rảnh",
@@ -1296,5 +1297,49 @@
}
},
"googleIdentityUnverified": "Không thể xác minh danh tính của kết nối Google cũ này. Lịch, công việc và thay đổi đang chờ được giữ lại. Thêm một kết nối Google riêng để đồng bộ an toàn. Thay đổi đang chờ vẫn thuộc tài khoản gốc.",
- "addSeparateGoogleAccount": "Thêm kết nối Google riêng"
+ "addSeparateGoogleAccount": "Thêm kết nối Google riêng",
+ "seriesPatternChangeConfirmation": "Thay đổi kiểu lặp có thể thêm hoặc xóa ngày.\n\nHiện tại: {before}\nĐề xuất: {after}\n\nLưu kiểu lặp này?",
+ "connectedAccounts": "Tài khoản đã kết nối",
+ "contactsIndependentAccounts": "Tài khoản danh bạ độc lập",
+ "contactsNextcloudReuse": "Gợi ý liên hệ dùng lại thông tin xác thực hiện có của tài khoản qua tích hợp danh bạ chỉ đọc.",
+ "contactsRetrySync": "Thử đồng bộ danh bạ lại",
+ "contactsPreferenceFailed": "Không thể lưu tùy chọn nguồn danh bạ.",
+ "contactsSuggestionsDisabled": "Đã tắt gợi ý liên hệ",
+ "contactsSyncFailed": "Không thể đồng bộ danh bạ. Thử lại hoặc kết nối lại tài khoản nếu quyền đã hết hạn.",
+ "registrationRecoveryDescription": "Đăng ký đã lưu hoặc liên kết thông tin xác thực không khả dụng hoặc bị từ chối. Hãy thay thế rõ ràng đăng ký của tài khoản này để khôi phục; dữ liệu hiện có được giữ lại.",
+ "revalidateAccount": "Xác minh lại kết nối",
+ "openSharedPrimaryDescription": "Mở lịch chính của chủ sở hữu được chỉ định. Các lịch chia sẻ khác được tìm qua đồng bộ tài khoản thông thường.",
+ "showInBusyMax": "Hiện trong BusyMax",
+ "showInGoogleCalendarList": "Hiện trong danh sách lịch Google",
+ "googleVisibilityHelp": "Lựa chọn BusyMax là cục bộ. Chế độ hiển thị Google thay đổi danh sách trên Google; lịch bị ẩn không thể hiển thị trong BusyMax.",
+ "primaryCalendarVisibilityExplanation": "Lịch chính Google luôn nằm trong danh sách.",
+ "accountsLoading": "Đang tải tài khoản…",
+ "accountsLoadFailed": "Không thể tải tài khoản. Hãy mở lại Tài khoản.",
+ "collectionsLoading": "Đang tải bộ sưu tập…",
+ "collectionsLoadFailed": "Không thể tải bộ sưu tập. Dữ liệu đệm được giữ; hãy thử đồng bộ lại.",
+ "collectionsNotDiscovered": "Chưa hoàn tất phát hiện. Hãy làm mới hoặc kết nối lại tài khoản.",
+ "collectionsEmpty": "Không tìm thấy bộ sưu tập.",
+ "accountReadyToSync": "Sẵn sàng đồng bộ",
+ "accountSyncProblem": "Đồng bộ cần được xử lý",
+ "accountServiceUnreachable": "Không thể kết nối với dịch vụ",
+ "accountSyncDetails": "Chi tiết đồng bộ",
+ "accountReconnectStatus": "Cần kết nối lại",
+ "accountRegistrationStatus": "Đăng ký ứng dụng cần được xử lý",
+ "collectionsUnavailable": "Bộ sưu tập không khả dụng",
+ "accountContactSuggestions": "Đề xuất khách từ danh bạ của tài khoản này",
+ "accountLoadingSignInSettings": "Đang tải cài đặt đăng nhập…",
+ "contactsNoAddressBooks": "Không có sổ địa chỉ nào khả dụng",
+ "contactsSourcesUnavailable": "Không thể tải sổ địa chỉ",
+ "syncNoRecord": "Chưa ghi nhận thời điểm đồng bộ thành công",
+ "cachedSyncError": "Đồng bộ thất bại; dữ liệu đệm có thể đã cũ.",
+ "calendarRemotePending": "Đang chờ đổi chế độ hiển thị Google",
+ "calendarRemoteFailed": "Thay đổi khả năng hiển thị trên Google không thành công. Trạng thái Google được xác nhận gần nhất đang hiển thị. BusyMax vẫn áp dụng thay đổi đang chờ. Hãy thử lại hoặc hủy thay đổi.",
+ "discardCalendarSettingsConfirmation": "Hủy các thay đổi đang chờ của “{calendar}”?\n\nCác cài đặt sau sẽ trở về giá trị trước đó:\n{settings}\n\nLựa chọn lịch trong BusyMax của bạn được giữ nguyên. Không có thay đổi nào được gửi đến Google.",
+ "sharedCalendarUnavailable": "Không thể truy cập; đang hiện dữ liệu đệm. Thử đồng bộ lại hoặc xóa khỏi danh sách lịch.",
+ "invalidCollectionColor": "Nhập màu thập lục phân gồm sáu hoặc tám chữ số, bắt đầu bằng #.",
+ "invalidCollectionOrder": "Nhập số nguyên.",
+ "invalidCollectionTimezone": "Nhập tài liệu múi giờ lịch hợp lệ chứa VTIMEZONE và TZID.",
+ "invalidCollectionValue": "Sửa giá trị này trước khi lưu.",
+ "accountAlreadyConnected": "Tài khoản này đã kết nối. Hãy dùng thao tác kết nối lại của tài khoản.",
+ "nextcloudRemovalAuthorizationDescription": "BusyMax cũng sẽ thử thu hồi mật khẩu ứng dụng Nextcloud của tài khoản này. Việc xóa cục bộ vẫn có thể thành công dù thu hồi thất bại."
}
diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb
index f7cecdc5..5e4672d4 100644
--- a/lib/l10n/app_zh.arb
+++ b/lib/l10n/app_zh.arb
@@ -1,4 +1,5 @@
{
+ "contactsAccountsUnavailable": "无法加载联系人账户",
"contactsTitle": "联系人",
"contactsLinkedDescription": "添加日程参与者时使用此账号缓存的联系人。",
"contactsEnableSuggestions": "启用联系人建议",
@@ -1092,7 +1093,7 @@
"windowMinimize": "最小化",
"windowMaximize": "最大化",
"windowRestore": "还原",
- "openSharedCalendar": "打开共享日历",
+ "openSharedCalendar": "打开共享主日历",
"calendarOwnerEmail": "日历所有者的电子邮件地址",
"addCalendarShare": "添加访问权限",
"calendarShareFreeBusy": "仅查看忙闲",
@@ -1296,5 +1297,49 @@
}
},
"googleIdentityUnverified": "无法验证此旧 Google 连接的账号身份。日历、任务和待处理的更改已保留。请添加独立的 Google 连接以安全同步。待处理的更改将保留在原账号中。",
- "addSeparateGoogleAccount": "添加独立的 Google 连接"
+ "addSeparateGoogleAccount": "添加独立的 Google 连接",
+ "seriesPatternChangeConfirmation": "更改重复规则可能会增加或移除日期。\n\n当前:{before}\n建议:{after}\n\n保存此规则吗?",
+ "connectedAccounts": "已连接的账户",
+ "contactsIndependentAccounts": "独立联系人账户",
+ "contactsNextcloudReuse": "联系人建议通过只读联系人集成重复使用此账户现有的凭据。",
+ "contactsRetrySync": "重试联系人同步",
+ "contactsPreferenceFailed": "无法保存联系人来源偏好。",
+ "contactsSuggestionsDisabled": "联系人建议已停用",
+ "contactsSyncFailed": "无法同步联系人。请重试,或在授权过期时重新连接账户。",
+ "registrationRecoveryDescription": "保存的应用注册或凭据绑定不可用或被拒绝。请明确替换此账户的注册以恢复连接;现有数据会保留。",
+ "revalidateAccount": "重新验证连接",
+ "openSharedPrimaryDescription": "打开指定所有者的主日历。其他共享日历通过普通账户同步发现。",
+ "showInBusyMax": "在 BusyMax 中显示",
+ "showInGoogleCalendarList": "在 Google 日历列表中显示",
+ "googleVisibilityHelp": "BusyMax 的选择是本地设置。Google 可见性会更改 Google 上的日历列表;隐藏的日历无法在 BusyMax 中显示。",
+ "primaryCalendarVisibilityExplanation": "Google 主日历会保留在日历列表中。",
+ "accountsLoading": "正在加载账户…",
+ "accountsLoadFailed": "无法加载账户。请重新打开“账户”。",
+ "collectionsLoading": "正在加载集合…",
+ "collectionsLoadFailed": "无法加载集合。缓存数据已保留;请重试同步。",
+ "collectionsNotDiscovered": "发现尚未完成。请刷新或重新连接此账户。",
+ "collectionsEmpty": "未找到集合。",
+ "accountReadyToSync": "已准备好同步",
+ "accountSyncProblem": "同步需要处理",
+ "accountServiceUnreachable": "无法连接到服务",
+ "accountSyncDetails": "同步详情",
+ "accountReconnectStatus": "需要重新连接",
+ "accountRegistrationStatus": "应用注册需要处理",
+ "collectionsUnavailable": "集合不可用",
+ "accountContactSuggestions": "从此账户的联系人中推荐访客",
+ "accountLoadingSignInSettings": "正在加载登录设置…",
+ "contactsNoAddressBooks": "没有可用的通讯录",
+ "contactsSourcesUnavailable": "无法加载通讯录",
+ "syncNoRecord": "没有成功同步时间记录",
+ "cachedSyncError": "同步失败;缓存数据可能已过时。",
+ "calendarRemotePending": "Google 可见性更改待处理",
+ "calendarRemoteFailed": "Google 显示设置更改失败。现显示 Google 上次确认的状态。BusyMax 仍遵循待处理的更改。请重试或放弃该更改。",
+ "discardCalendarSettingsConfirmation": "要放弃“{calendar}”的待处理更改吗?\n\n以下设置将恢复为之前的值:\n{settings}\n\nBusyMax 中的日历选择将保留。不会向 Google 发送任何更改。",
+ "sharedCalendarUnavailable": "无法访问;正在显示缓存数据。请重试同步或从日历列表中移除。",
+ "invalidCollectionColor": "请输入以 # 开头的六位或八位十六进制颜色。",
+ "invalidCollectionOrder": "请输入整数。",
+ "invalidCollectionTimezone": "请输入包含 VTIMEZONE 和 TZID 的有效日历时区文档。",
+ "invalidCollectionValue": "保存前请修正此值。",
+ "accountAlreadyConnected": "此账户已连接。请使用该账户的“重新连接”操作。",
+ "nextcloudRemovalAuthorizationDescription": "BusyMax 还会尝试撤销此账户的 Nextcloud 应用密码。即使撤销失败,本地移除也可以成功。"
}
diff --git a/lib/l10n/app_zh_Hans.arb b/lib/l10n/app_zh_Hans.arb
index 253a2dd3..9fbf538f 100644
--- a/lib/l10n/app_zh_Hans.arb
+++ b/lib/l10n/app_zh_Hans.arb
@@ -1,4 +1,5 @@
{
+ "contactsAccountsUnavailable": "无法加载联系人账户",
"contactsTitle": "联系人",
"contactsLinkedDescription": "添加日程参与者时使用此账号缓存的联系人。",
"contactsEnableSuggestions": "启用联系人建议",
@@ -1092,7 +1093,7 @@
"windowMinimize": "最小化",
"windowMaximize": "最大化",
"windowRestore": "还原",
- "openSharedCalendar": "打开共享日历",
+ "openSharedCalendar": "打开共享主日历",
"calendarOwnerEmail": "日历所有者的电子邮件地址",
"addCalendarShare": "添加访问权限",
"calendarShareFreeBusy": "仅查看忙闲",
@@ -1296,5 +1297,49 @@
}
},
"googleIdentityUnverified": "无法验证此旧 Google 连接的账号身份。日历、任务和待处理的更改已保留。请添加独立的 Google 连接以安全同步。待处理的更改将保留在原账号中。",
- "addSeparateGoogleAccount": "添加独立的 Google 连接"
+ "addSeparateGoogleAccount": "添加独立的 Google 连接",
+ "seriesPatternChangeConfirmation": "更改重复规则可能会增加或移除日期。\n\n当前:{before}\n建议:{after}\n\n保存此规则吗?",
+ "connectedAccounts": "已连接的账户",
+ "contactsIndependentAccounts": "独立联系人账户",
+ "contactsNextcloudReuse": "联系人建议通过只读联系人集成重复使用此账户现有的凭据。",
+ "contactsRetrySync": "重试联系人同步",
+ "contactsPreferenceFailed": "无法保存联系人来源偏好。",
+ "contactsSuggestionsDisabled": "联系人建议已停用",
+ "contactsSyncFailed": "无法同步联系人。请重试,或在授权过期时重新连接账户。",
+ "registrationRecoveryDescription": "保存的应用注册或凭据绑定不可用或被拒绝。请明确替换此账户的注册以恢复连接;现有数据会保留。",
+ "revalidateAccount": "重新验证连接",
+ "openSharedPrimaryDescription": "打开指定所有者的主日历。其他共享日历通过普通账户同步发现。",
+ "showInBusyMax": "在 BusyMax 中显示",
+ "showInGoogleCalendarList": "在 Google 日历列表中显示",
+ "googleVisibilityHelp": "BusyMax 的选择是本地设置。Google 可见性会更改 Google 上的日历列表;隐藏的日历无法在 BusyMax 中显示。",
+ "primaryCalendarVisibilityExplanation": "Google 主日历会保留在日历列表中。",
+ "accountsLoading": "正在加载账户…",
+ "accountsLoadFailed": "无法加载账户。请重新打开“账户”。",
+ "collectionsLoading": "正在加载集合…",
+ "collectionsLoadFailed": "无法加载集合。缓存数据已保留;请重试同步。",
+ "collectionsNotDiscovered": "发现尚未完成。请刷新或重新连接此账户。",
+ "collectionsEmpty": "未找到集合。",
+ "accountReadyToSync": "已准备好同步",
+ "accountSyncProblem": "同步需要处理",
+ "accountServiceUnreachable": "无法连接到服务",
+ "accountSyncDetails": "同步详情",
+ "accountReconnectStatus": "需要重新连接",
+ "accountRegistrationStatus": "应用注册需要处理",
+ "collectionsUnavailable": "集合不可用",
+ "accountContactSuggestions": "从此账户的联系人中推荐访客",
+ "accountLoadingSignInSettings": "正在加载登录设置…",
+ "contactsNoAddressBooks": "没有可用的通讯录",
+ "contactsSourcesUnavailable": "无法加载通讯录",
+ "syncNoRecord": "没有成功同步时间记录",
+ "cachedSyncError": "同步失败;缓存数据可能已过时。",
+ "calendarRemotePending": "Google 可见性更改待处理",
+ "calendarRemoteFailed": "Google 显示设置更改失败。现显示 Google 上次确认的状态。BusyMax 仍遵循待处理的更改。请重试或放弃该更改。",
+ "discardCalendarSettingsConfirmation": "要放弃“{calendar}”的待处理更改吗?\n\n以下设置将恢复为之前的值:\n{settings}\n\nBusyMax 中的日历选择将保留。不会向 Google 发送任何更改。",
+ "sharedCalendarUnavailable": "无法访问;正在显示缓存数据。请重试同步或从日历列表中移除。",
+ "invalidCollectionColor": "请输入以 # 开头的六位或八位十六进制颜色。",
+ "invalidCollectionOrder": "请输入整数。",
+ "invalidCollectionTimezone": "请输入包含 VTIMEZONE 和 TZID 的有效日历时区文档。",
+ "invalidCollectionValue": "保存前请修正此值。",
+ "accountAlreadyConnected": "此账户已连接。请使用该账户的“重新连接”操作。",
+ "nextcloudRemovalAuthorizationDescription": "BusyMax 还会尝试撤销此账户的 Nextcloud 应用密码。即使撤销失败,本地移除也可以成功。"
}
diff --git a/lib/l10n/app_zh_Hant.arb b/lib/l10n/app_zh_Hant.arb
index d7bf73f3..2c9d02f6 100644
--- a/lib/l10n/app_zh_Hant.arb
+++ b/lib/l10n/app_zh_Hant.arb
@@ -1,4 +1,5 @@
{
+ "contactsAccountsUnavailable": "無法載入聯絡人帳號",
"contactsTitle": "聯絡人",
"contactsLinkedDescription": "新增活動來賓時使用此帳號快取的聯絡人。",
"contactsEnableSuggestions": "啟用聯絡人建議",
@@ -1092,7 +1093,7 @@
"windowMinimize": "最小化",
"windowMaximize": "最大化",
"windowRestore": "還原",
- "openSharedCalendar": "開啟共用行事曆",
+ "openSharedCalendar": "開啟共用主要行事曆",
"calendarOwnerEmail": "行事曆擁有者的電子郵件地址",
"addCalendarShare": "新增存取權",
"calendarShareFreeBusy": "僅顯示忙閒",
@@ -1296,5 +1297,49 @@
}
},
"googleIdentityUnverified": "無法驗證此舊 Google 連線的帳號身分。行事曆、待辦事項和待處理的變更已保留。請新增獨立的 Google 連線以安全同步。待處理的變更將保留在原帳號中。",
- "addSeparateGoogleAccount": "新增獨立的 Google 連線"
+ "addSeparateGoogleAccount": "新增獨立的 Google 連線",
+ "seriesPatternChangeConfirmation": "變更重複規則可能會增加或移除日期。\n\n目前:{before}\n建議:{after}\n\n要儲存此規則嗎?",
+ "connectedAccounts": "已連線的帳戶",
+ "contactsIndependentAccounts": "獨立聯絡人帳戶",
+ "contactsNextcloudReuse": "聯絡人建議透過唯讀聯絡人整合重複使用此帳戶現有的憑證。",
+ "contactsRetrySync": "重試聯絡人同步",
+ "contactsPreferenceFailed": "無法儲存聯絡人來源偏好。",
+ "contactsSuggestionsDisabled": "聯絡人建議已停用",
+ "contactsSyncFailed": "無法同步聯絡人。請重試,或在授權過期時重新連線帳戶。",
+ "registrationRecoveryDescription": "儲存的應用程式註冊或憑證繫結無法使用或遭拒。請明確取代此帳戶的註冊以恢復連線;現有資料會保留。",
+ "revalidateAccount": "重新驗證連線",
+ "openSharedPrimaryDescription": "開啟指定擁有者的主要行事曆。其他共用行事曆透過一般帳戶同步尋找。",
+ "showInBusyMax": "在 BusyMax 中顯示",
+ "showInGoogleCalendarList": "在 Google 行事曆清單中顯示",
+ "googleVisibilityHelp": "BusyMax 的選擇是本機設定。Google 可見性會變更 Google 上的清單;隱藏的行事曆無法在 BusyMax 中顯示。",
+ "primaryCalendarVisibilityExplanation": "Google 主要行事曆會保留在清單中。",
+ "accountsLoading": "正在載入帳戶…",
+ "accountsLoadFailed": "無法載入帳戶。請重新開啟「帳戶」。",
+ "collectionsLoading": "正在載入集合…",
+ "collectionsLoadFailed": "無法載入集合。快取資料已保留;請重試同步。",
+ "collectionsNotDiscovered": "探索尚未完成。請重新整理或重新連線此帳戶。",
+ "collectionsEmpty": "找不到集合。",
+ "accountReadyToSync": "已準備好同步",
+ "accountSyncProblem": "同步需要處理",
+ "accountServiceUnreachable": "無法連線至服務",
+ "accountSyncDetails": "同步詳細資料",
+ "accountReconnectStatus": "需要重新連線",
+ "accountRegistrationStatus": "應用程式註冊需要處理",
+ "collectionsUnavailable": "集合無法使用",
+ "accountContactSuggestions": "從此帳戶的聯絡人中建議訪客",
+ "accountLoadingSignInSettings": "正在載入登入設定…",
+ "contactsNoAddressBooks": "沒有可用的通訊錄",
+ "contactsSourcesUnavailable": "無法載入通訊錄",
+ "syncNoRecord": "沒有成功同步時間的記錄",
+ "cachedSyncError": "同步失敗;快取資料可能已過時。",
+ "calendarRemotePending": "Google 可見性變更待處理",
+ "calendarRemoteFailed": "Google 顯示設定變更失敗。目前顯示 Google 上次確認的狀態。BusyMax 仍遵循待處理的變更。請重試或捨棄該變更。",
+ "discardCalendarSettingsConfirmation": "要捨棄「{calendar}」的待處理變更嗎?\n\n以下設定將還原為先前的值:\n{settings}\n\nBusyMax 中的行事曆選擇將會保留。不會向 Google 傳送任何變更。",
+ "sharedCalendarUnavailable": "無法存取;正在顯示快取資料。請重試同步或從行事曆清單移除。",
+ "invalidCollectionColor": "請輸入以 # 開頭的六位或八位十六進位色彩。",
+ "invalidCollectionOrder": "請輸入整數。",
+ "invalidCollectionTimezone": "請輸入包含 VTIMEZONE 和 TZID 的有效行事曆時區文件。",
+ "invalidCollectionValue": "儲存前請修正此值。",
+ "accountAlreadyConnected": "此帳戶已連線。請使用該帳戶的「重新連線」操作。",
+ "nextcloudRemovalAuthorizationDescription": "BusyMax 也會嘗試撤銷此帳戶的 Nextcloud 應用程式密碼。即使撤銷失敗,本機移除仍可成功。"
}
diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart
index 62f99895..bd6001be 100644
--- a/lib/l10n/generated/app_localizations.dart
+++ b/lib/l10n/generated/app_localizations.dart
@@ -135,6 +135,12 @@ abstract class AppLocalizations {
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'),
];
+ /// No description provided for @contactsAccountsUnavailable.
+ ///
+ /// In en, this message translates to:
+ /// **'Could not load contact accounts'**
+ String get contactsAccountsUnavailable;
+
/// No description provided for @launchAtLoginManagedExternally.
///
/// In en, this message translates to:
@@ -5021,7 +5027,7 @@ abstract class AppLocalizations {
/// No description provided for @openSharedCalendar.
///
/// In en, this message translates to:
- /// **'Open shared calendar'**
+ /// **'Open shared primary calendar'**
String get openSharedCalendar;
/// No description provided for @manageCalendarSharing.
@@ -6035,6 +6041,270 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'Add a separate Google connection'**
String get addSeparateGoogleAccount;
+
+ /// Explicit approval of an ordinary series date edit that changes recurrence semantics. Both patterns include their termination condition.
+ ///
+ /// In en, this message translates to:
+ /// **'This changes the recurrence pattern and may add or remove dates.\n\nCurrent: {before}\nProposed: {after}\n\nSave this pattern?'**
+ String seriesPatternChangeConfirmation(String before, String after);
+
+ /// No description provided for @connectedAccounts.
+ ///
+ /// In en, this message translates to:
+ /// **'Connected accounts'**
+ String get connectedAccounts;
+
+ /// No description provided for @contactsIndependentAccounts.
+ ///
+ /// In en, this message translates to:
+ /// **'Independent contact accounts'**
+ String get contactsIndependentAccounts;
+
+ /// No description provided for @contactsNextcloudReuse.
+ ///
+ /// In en, this message translates to:
+ /// **'Contact suggestions reuse this account’s existing credential through the read-only contacts integration.'**
+ String get contactsNextcloudReuse;
+
+ /// No description provided for @contactsRetrySync.
+ ///
+ /// In en, this message translates to:
+ /// **'Retry contacts sync'**
+ String get contactsRetrySync;
+
+ /// No description provided for @contactsPreferenceFailed.
+ ///
+ /// In en, this message translates to:
+ /// **'Could not save contact source preference'**
+ String get contactsPreferenceFailed;
+
+ /// No description provided for @contactsSuggestionsDisabled.
+ ///
+ /// In en, this message translates to:
+ /// **'Contact suggestions disabled'**
+ String get contactsSuggestionsDisabled;
+
+ /// No description provided for @contactsSyncFailed.
+ ///
+ /// In en, this message translates to:
+ /// **'Contacts could not sync. Retry, or reconnect if authorization has expired.'**
+ String get contactsSyncFailed;
+
+ /// No description provided for @registrationRecoveryDescription.
+ ///
+ /// In en, this message translates to:
+ /// **'The saved registration or its credential binding is unavailable or rejected. Explicitly replace the registration for this account to recover; existing data is preserved.'**
+ String get registrationRecoveryDescription;
+
+ /// No description provided for @revalidateAccount.
+ ///
+ /// In en, this message translates to:
+ /// **'Revalidate connection'**
+ String get revalidateAccount;
+
+ /// No description provided for @openSharedPrimaryDescription.
+ ///
+ /// In en, this message translates to:
+ /// **'Opens the specified owner’s primary calendar. Other shared calendars are discovered through ordinary account synchronization.'**
+ String get openSharedPrimaryDescription;
+
+ /// No description provided for @showInBusyMax.
+ ///
+ /// In en, this message translates to:
+ /// **'Show in BusyMax'**
+ String get showInBusyMax;
+
+ /// No description provided for @showInGoogleCalendarList.
+ ///
+ /// In en, this message translates to:
+ /// **'Show in Google’s calendar list'**
+ String get showInGoogleCalendarList;
+
+ /// No description provided for @googleVisibilityHelp.
+ ///
+ /// In en, this message translates to:
+ /// **'BusyMax selection is local. Google visibility changes your calendar list on Google; hidden calendars cannot be displayed in BusyMax.'**
+ String get googleVisibilityHelp;
+
+ /// No description provided for @primaryCalendarVisibilityExplanation.
+ ///
+ /// In en, this message translates to:
+ /// **'Google’s primary calendar stays in its calendar list.'**
+ String get primaryCalendarVisibilityExplanation;
+
+ /// No description provided for @accountsLoading.
+ ///
+ /// In en, this message translates to:
+ /// **'Loading accounts…'**
+ String get accountsLoading;
+
+ /// No description provided for @accountsLoadFailed.
+ ///
+ /// In en, this message translates to:
+ /// **'Accounts could not be loaded. Retry opening Accounts.'**
+ String get accountsLoadFailed;
+
+ /// No description provided for @collectionsLoading.
+ ///
+ /// In en, this message translates to:
+ /// **'Loading collections…'**
+ String get collectionsLoading;
+
+ /// No description provided for @collectionsLoadFailed.
+ ///
+ /// In en, this message translates to:
+ /// **'Collections could not be loaded. Cached data is retained; retry synchronization.'**
+ String get collectionsLoadFailed;
+
+ /// No description provided for @collectionsNotDiscovered.
+ ///
+ /// In en, this message translates to:
+ /// **'Discovery has not completed. Refresh or reconnect this account.'**
+ String get collectionsNotDiscovered;
+
+ /// No description provided for @collectionsEmpty.
+ ///
+ /// In en, this message translates to:
+ /// **'No collections found.'**
+ String get collectionsEmpty;
+
+ /// No description provided for @accountReadyToSync.
+ ///
+ /// In en, this message translates to:
+ /// **'Ready to sync'**
+ String get accountReadyToSync;
+
+ /// No description provided for @accountSyncProblem.
+ ///
+ /// In en, this message translates to:
+ /// **'Synchronization needs attention'**
+ String get accountSyncProblem;
+
+ /// No description provided for @accountServiceUnreachable.
+ ///
+ /// In en, this message translates to:
+ /// **'Could not reach the service'**
+ String get accountServiceUnreachable;
+
+ /// No description provided for @accountSyncDetails.
+ ///
+ /// In en, this message translates to:
+ /// **'Synchronization details'**
+ String get accountSyncDetails;
+
+ /// No description provided for @accountReconnectStatus.
+ ///
+ /// In en, this message translates to:
+ /// **'Reconnect required'**
+ String get accountReconnectStatus;
+
+ /// No description provided for @accountRegistrationStatus.
+ ///
+ /// In en, this message translates to:
+ /// **'Registration needs attention'**
+ String get accountRegistrationStatus;
+
+ /// No description provided for @collectionsUnavailable.
+ ///
+ /// In en, this message translates to:
+ /// **'Collections unavailable'**
+ String get collectionsUnavailable;
+
+ /// No description provided for @accountContactSuggestions.
+ ///
+ /// In en, this message translates to:
+ /// **'Suggest guests from this account’s contacts'**
+ String get accountContactSuggestions;
+
+ /// No description provided for @accountLoadingSignInSettings.
+ ///
+ /// In en, this message translates to:
+ /// **'Loading sign-in settings…'**
+ String get accountLoadingSignInSettings;
+
+ /// No description provided for @contactsNoAddressBooks.
+ ///
+ /// In en, this message translates to:
+ /// **'No address books available'**
+ String get contactsNoAddressBooks;
+
+ /// No description provided for @contactsSourcesUnavailable.
+ ///
+ /// In en, this message translates to:
+ /// **'Could not load address books'**
+ String get contactsSourcesUnavailable;
+
+ /// No description provided for @syncNoRecord.
+ ///
+ /// In en, this message translates to:
+ /// **'No successful sync time recorded'**
+ String get syncNoRecord;
+
+ /// No description provided for @cachedSyncError.
+ ///
+ /// In en, this message translates to:
+ /// **'Sync failed; cached data may be out of date.'**
+ String get cachedSyncError;
+
+ /// No description provided for @calendarRemotePending.
+ ///
+ /// In en, this message translates to:
+ /// **'Google visibility change pending'**
+ String get calendarRemotePending;
+
+ /// No description provided for @calendarRemoteFailed.
+ ///
+ /// In en, this message translates to:
+ /// **'Google visibility change failed. Google’s last confirmed state is shown. BusyMax still follows the pending change. Retry or discard it.'**
+ String get calendarRemoteFailed;
+
+ /// Confirms discarding the entire queued Google calendar settings patch. calendar is the calendar display name; settings is a localized, newline-separated bulleted list of every affected setting. No provider request is sent, and the local calendar selection is preserved.
+ ///
+ /// In en, this message translates to:
+ /// **'Discard the pending changes to “{calendar}”?\n\nThese settings will return to their previous values:\n{settings}\n\nYour BusyMax calendar selection is kept. No changes will be sent to Google.'**
+ String discardCalendarSettingsConfirmation(String calendar, String settings);
+
+ /// No description provided for @sharedCalendarUnavailable.
+ ///
+ /// In en, this message translates to:
+ /// **'Access unavailable; showing cached data. Retry synchronization or remove from your calendar list.'**
+ String get sharedCalendarUnavailable;
+
+ /// No description provided for @invalidCollectionColor.
+ ///
+ /// In en, this message translates to:
+ /// **'Enter a six- or eight-digit hexadecimal color, starting with #.'**
+ String get invalidCollectionColor;
+
+ /// No description provided for @invalidCollectionOrder.
+ ///
+ /// In en, this message translates to:
+ /// **'Enter a whole number.'**
+ String get invalidCollectionOrder;
+
+ /// No description provided for @invalidCollectionTimezone.
+ ///
+ /// In en, this message translates to:
+ /// **'Enter a valid calendar timezone document containing VTIMEZONE and TZID.'**
+ String get invalidCollectionTimezone;
+
+ /// No description provided for @invalidCollectionValue.
+ ///
+ /// In en, this message translates to:
+ /// **'Correct this value before saving.'**
+ String get invalidCollectionValue;
+
+ /// No description provided for @accountAlreadyConnected.
+ ///
+ /// In en, this message translates to:
+ /// **'This account is already connected. Use its Reconnect action.'**
+ String get accountAlreadyConnected;
+
+ /// No description provided for @nextcloudRemovalAuthorizationDescription.
+ ///
+ /// In en, this message translates to:
+ /// **'BusyMax will also try to revoke this account’s Nextcloud app password. Local removal can succeed even if that revocation fails.'**
+ String get nextcloudRemovalAuthorizationDescription;
}
class _AppLocalizationsDelegate
diff --git a/lib/l10n/generated/app_localizations_ar.dart b/lib/l10n/generated/app_localizations_ar.dart
index 5b7084ff..0f473f26 100644
--- a/lib/l10n/generated/app_localizations_ar.dart
+++ b/lib/l10n/generated/app_localizations_ar.dart
@@ -13,6 +13,9 @@ import 'app_localizations.dart';
class AppLocalizationsAr extends AppLocalizations {
AppLocalizationsAr([String locale = 'ar']) : super(locale);
+ @override
+ String get contactsAccountsUnavailable => 'تعذّر تحميل حسابات جهات الاتصال';
+
@override
String get launchAtLoginManagedExternally =>
'تم تشغيل BusyMax من إعدادات بدء التشغيل في سطح المكتب. أزل إدخاله هناك، ثم أنهِ BusyMax وافتحه مجددًا لاستخدام هذا المفتاح.';
@@ -3070,7 +3073,7 @@ class AppLocalizationsAr extends AppLocalizations {
String get searchClearText => 'مسح النص';
@override
- String get openSharedCalendar => 'فتح تقويم مشترك';
+ String get openSharedCalendar => 'فتح تقويم أساسي مشترك';
@override
String get manageCalendarSharing => 'إدارة مشاركة التقويم';
@@ -3646,4 +3649,156 @@ class AppLocalizationsAr extends AppLocalizations {
@override
String get addSeparateGoogleAccount => 'إضافة اتصال Google منفصل';
+
+ @override
+ String seriesPatternChangeConfirmation(String before, String after) {
+ return 'قد تؤدي هذه التغييرات في نمط التكرار إلى إضافة تواريخ أو إزالتها.\n\nالحالي: $before\nالمقترح: $after\n\nهل تريد حفظ هذا النمط؟';
+ }
+
+ @override
+ String get connectedAccounts => 'الحسابات المتصلة';
+
+ @override
+ String get contactsIndependentAccounts => 'حسابات جهات اتصال مستقلة';
+
+ @override
+ String get contactsNextcloudReuse =>
+ 'تعيد اقتراحات جهات الاتصال استخدام بيانات اعتماد هذا الحساب عبر تكامل جهات الاتصال للقراءة فقط.';
+
+ @override
+ String get contactsRetrySync => 'إعادة محاولة مزامنة جهات الاتصال';
+
+ @override
+ String get contactsPreferenceFailed => 'تعذّر حفظ تفضيل مصدر جهات الاتصال.';
+
+ @override
+ String get contactsSuggestionsDisabled => 'اقتراحات جهات الاتصال معطّلة';
+
+ @override
+ String get contactsSyncFailed =>
+ 'تعذّرت مزامنة جهات الاتصال. أعد المحاولة أو أعد توصيل الحساب إذا انتهى التفويض.';
+
+ @override
+ String get registrationRecoveryDescription =>
+ 'التسجيل المحفوظ أو ارتباط بيانات اعتماده غير متاح أو مرفوض. استبدل تسجيل هذا الحساب صراحةً لاستعادته؛ تُحفظ البيانات الحالية.';
+
+ @override
+ String get revalidateAccount => 'إعادة التحقق من الاتصال';
+
+ @override
+ String get openSharedPrimaryDescription =>
+ 'يفتح التقويم الأساسي للمالك المحدد. تُكتشف التقاويم المشتركة الأخرى عبر مزامنة الحساب المعتادة.';
+
+ @override
+ String get showInBusyMax => 'إظهار في BusyMax';
+
+ @override
+ String get showInGoogleCalendarList => 'إظهار في قائمة تقاويم Google';
+
+ @override
+ String get googleVisibilityHelp =>
+ 'اختيار BusyMax محلي. يغيّر ظهور Google قائمتك في Google؛ لا يمكن عرض التقاويم المخفية في BusyMax.';
+
+ @override
+ String get primaryCalendarVisibilityExplanation =>
+ 'يبقى تقويم Google الأساسي في قائمته.';
+
+ @override
+ String get accountsLoading => 'جارٍ تحميل الحسابات…';
+
+ @override
+ String get accountsLoadFailed => 'تعذّر تحميل الحسابات. أعد فتح الحسابات.';
+
+ @override
+ String get collectionsLoading => 'جارٍ تحميل المجموعات…';
+
+ @override
+ String get collectionsLoadFailed =>
+ 'تعذّر تحميل المجموعات. تُحفظ البيانات المخزنة مؤقتًا؛ أعد محاولة المزامنة.';
+
+ @override
+ String get collectionsNotDiscovered =>
+ 'لم يكتمل الاكتشاف. حدّث هذا الحساب أو أعد توصيله.';
+
+ @override
+ String get collectionsEmpty => 'لم يُعثر على مجموعات.';
+
+ @override
+ String get accountReadyToSync => 'جاهز للمزامنة';
+
+ @override
+ String get accountSyncProblem => 'تحتاج المزامنة إلى معالجة';
+
+ @override
+ String get accountServiceUnreachable => 'تعذّر الوصول إلى الخدمة';
+
+ @override
+ String get accountSyncDetails => 'تفاصيل المزامنة';
+
+ @override
+ String get accountReconnectStatus => 'يلزم إعادة الاتصال';
+
+ @override
+ String get accountRegistrationStatus => 'يحتاج تسجيل التطبيق إلى معالجة';
+
+ @override
+ String get collectionsUnavailable => 'المجموعات غير متاحة';
+
+ @override
+ String get accountContactSuggestions =>
+ 'اقتراح مدعوين من جهات اتصال هذا الحساب';
+
+ @override
+ String get accountLoadingSignInSettings => 'جارٍ تحميل إعدادات تسجيل الدخول…';
+
+ @override
+ String get contactsNoAddressBooks => 'لا توجد دفاتر عناوين متاحة';
+
+ @override
+ String get contactsSourcesUnavailable => 'تعذّر تحميل دفاتر العناوين';
+
+ @override
+ String get syncNoRecord => 'لم يُسجّل وقت مزامنة ناجحة';
+
+ @override
+ String get cachedSyncError =>
+ 'فشلت المزامنة؛ قد تكون البيانات المخزنة مؤقتًا قديمة.';
+
+ @override
+ String get calendarRemotePending => 'تغيير ظهور Google قيد الانتظار';
+
+ @override
+ String get calendarRemoteFailed =>
+ 'فشل تغيير الظهور في Google. تظهر آخر حالة مؤكدة في Google. ما زال BusyMax يتبع التغيير المعلّق. أعد المحاولة أو تجاهل التغيير.';
+
+ @override
+ String discardCalendarSettingsConfirmation(String calendar, String settings) {
+ return 'هل تريد تجاهل التغييرات المعلّقة على «$calendar»؟\n\nستعود هذه الإعدادات إلى قيمها السابقة:\n$settings\n\nسيُحتفظ باختيار التقويم في BusyMax. لن تُرسل أي تغييرات إلى Google.';
+ }
+
+ @override
+ String get sharedCalendarUnavailable =>
+ 'الوصول غير متاح؛ تُعرض البيانات المخزنة مؤقتًا. أعد المزامنة أو أزل التقويم من قائمتك.';
+
+ @override
+ String get invalidCollectionColor =>
+ 'أدخل لونًا سداسيًا عشريًا من ست أو ثماني خانات يبدأ بـ #.';
+
+ @override
+ String get invalidCollectionOrder => 'أدخل عددًا صحيحًا.';
+
+ @override
+ String get invalidCollectionTimezone =>
+ 'أدخل مستند منطقة زمنية صالحًا للتقويم يحتوي على VTIMEZONE وTZID.';
+
+ @override
+ String get invalidCollectionValue => 'صحّح هذه القيمة قبل الحفظ.';
+
+ @override
+ String get accountAlreadyConnected =>
+ 'هذا الحساب متصل بالفعل. استخدم إجراء إعادة توصيله.';
+
+ @override
+ String get nextcloudRemovalAuthorizationDescription =>
+ 'سيحاول BusyMax أيضًا إلغاء كلمة مرور تطبيق Nextcloud لهذا الحساب. قد تنجح الإزالة المحلية حتى إذا فشل الإلغاء.';
}
diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart
index 339c6098..870723c6 100644
--- a/lib/l10n/generated/app_localizations_de.dart
+++ b/lib/l10n/generated/app_localizations_de.dart
@@ -13,6 +13,10 @@ import 'app_localizations.dart';
class AppLocalizationsDe extends AppLocalizations {
AppLocalizationsDe([String locale = 'de']) : super(locale);
+ @override
+ String get contactsAccountsUnavailable =>
+ 'Kontaktkonten konnten nicht geladen werden';
+
@override
String get launchAtLoginManagedExternally =>
'BusyMax wurde durch die Autostart-Einstellungen Ihres Desktops gestartet. Entfernen Sie dort den Eintrag, beenden Sie BusyMax und öffnen Sie es erneut, um diesen Schalter zu verwenden.';
@@ -3006,7 +3010,7 @@ class AppLocalizationsDe extends AppLocalizations {
String get searchClearText => 'Text löschen';
@override
- String get openSharedCalendar => 'Freigegebenen Kalender öffnen';
+ String get openSharedCalendar => 'Freigegebenen Hauptkalender öffnen';
@override
String get manageCalendarSharing => 'Kalenderfreigabe verwalten';
@@ -3597,4 +3601,163 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get addSeparateGoogleAccount =>
'Separate Google-Verbindung hinzufügen';
+
+ @override
+ String seriesPatternChangeConfirmation(String before, String after) {
+ return 'Dies ändert das Wiederholungsmuster und kann Termine hinzufügen oder entfernen.\n\nAktuell: $before\nVorschlag: $after\n\nDieses Muster speichern?';
+ }
+
+ @override
+ String get connectedAccounts => 'Verbundene Konten';
+
+ @override
+ String get contactsIndependentAccounts => 'Unabhängige Kontaktkonten';
+
+ @override
+ String get contactsNextcloudReuse =>
+ 'Kontaktvorschläge verwenden die vorhandenen Zugangsdaten dieses Kontos über die schreibgeschützte Kontaktintegration.';
+
+ @override
+ String get contactsRetrySync => 'Kontaktsynchronisierung wiederholen';
+
+ @override
+ String get contactsPreferenceFailed =>
+ 'Die Kontaktquelleneinstellung konnte nicht gespeichert werden.';
+
+ @override
+ String get contactsSuggestionsDisabled => 'Kontaktvorschläge deaktiviert';
+
+ @override
+ String get contactsSyncFailed =>
+ 'Kontakte konnten nicht synchronisiert werden. Erneut versuchen oder bei abgelaufener Autorisierung das Konto neu verbinden.';
+
+ @override
+ String get registrationRecoveryDescription =>
+ 'Die gespeicherte Registrierung oder ihre Zugangsdatenbindung fehlt oder wurde abgelehnt. Ersetzen Sie die Registrierung dieses Kontos ausdrücklich, um den Zugriff wiederherzustellen. Vorhandene Daten bleiben erhalten.';
+
+ @override
+ String get revalidateAccount => 'Verbindung erneut prüfen';
+
+ @override
+ String get openSharedPrimaryDescription =>
+ 'Öffnet den Hauptkalender des angegebenen Besitzers. Andere freigegebene Kalender werden bei der normalen Kontosynchronisierung gefunden.';
+
+ @override
+ String get showInBusyMax => 'In BusyMax anzeigen';
+
+ @override
+ String get showInGoogleCalendarList => 'In Googles Kalenderliste anzeigen';
+
+ @override
+ String get googleVisibilityHelp =>
+ 'Die BusyMax-Auswahl gilt lokal. Die Google-Sichtbarkeit ändert Ihre Kalenderliste bei Google; ausgeblendete Kalender können in BusyMax nicht angezeigt werden.';
+
+ @override
+ String get primaryCalendarVisibilityExplanation =>
+ 'Der Google-Hauptkalender bleibt in der Kalenderliste.';
+
+ @override
+ String get accountsLoading => 'Konten werden geladen…';
+
+ @override
+ String get accountsLoadFailed =>
+ 'Konten konnten nicht geladen werden. Öffnen Sie Konten erneut.';
+
+ @override
+ String get collectionsLoading => 'Sammlungen werden geladen…';
+
+ @override
+ String get collectionsLoadFailed =>
+ 'Sammlungen konnten nicht geladen werden. Zwischengespeicherte Daten bleiben erhalten. Synchronisierung erneut versuchen.';
+
+ @override
+ String get collectionsNotDiscovered =>
+ 'Die Erkennung ist noch nicht abgeschlossen. Konto aktualisieren oder neu verbinden.';
+
+ @override
+ String get collectionsEmpty => 'Keine Sammlungen gefunden.';
+
+ @override
+ String get accountReadyToSync => 'Bereit zur Synchronisierung';
+
+ @override
+ String get accountSyncProblem => 'Synchronisierung benötigt Aufmerksamkeit';
+
+ @override
+ String get accountServiceUnreachable => 'Dienst nicht erreichbar';
+
+ @override
+ String get accountSyncDetails => 'Synchronisierungsdetails';
+
+ @override
+ String get accountReconnectStatus => 'Erneute Verbindung erforderlich';
+
+ @override
+ String get accountRegistrationStatus =>
+ 'App-Registrierung benötigt Aufmerksamkeit';
+
+ @override
+ String get collectionsUnavailable => 'Sammlungen nicht verfügbar';
+
+ @override
+ String get accountContactSuggestions =>
+ 'Gäste aus den Kontakten dieses Kontos vorschlagen';
+
+ @override
+ String get accountLoadingSignInSettings =>
+ 'Anmeldeeinstellungen werden geladen…';
+
+ @override
+ String get contactsNoAddressBooks => 'Keine Adressbücher verfügbar';
+
+ @override
+ String get contactsSourcesUnavailable =>
+ 'Adressbücher konnten nicht geladen werden';
+
+ @override
+ String get syncNoRecord =>
+ 'Kein erfolgreicher Synchronisierungszeitpunkt erfasst';
+
+ @override
+ String get cachedSyncError =>
+ 'Synchronisierung fehlgeschlagen; zwischengespeicherte Daten können veraltet sein.';
+
+ @override
+ String get calendarRemotePending => 'Google-Sichtbarkeitsänderung ausstehend';
+
+ @override
+ String get calendarRemoteFailed =>
+ 'Die Sichtbarkeitsänderung bei Google ist fehlgeschlagen. Der letzte bestätigte Google-Status wird angezeigt. BusyMax folgt weiterhin der ausstehenden Änderung. Erneut versuchen oder verwerfen.';
+
+ @override
+ String discardCalendarSettingsConfirmation(String calendar, String settings) {
+ return 'Ausstehende Änderungen an „$calendar“ verwerfen?\n\nDiese Einstellungen werden auf ihre vorherigen Werte zurückgesetzt:\n$settings\n\nDeine Kalenderauswahl in BusyMax bleibt erhalten. Es werden keine Änderungen an Google gesendet.';
+ }
+
+ @override
+ String get sharedCalendarUnavailable =>
+ 'Zugriff nicht verfügbar; zwischengespeicherte Daten werden angezeigt. Synchronisierung wiederholen oder aus der Kalenderliste entfernen.';
+
+ @override
+ String get invalidCollectionColor =>
+ 'Geben Sie eine sechs- oder achtstellige Hexadezimalfarbe mit # ein.';
+
+ @override
+ String get invalidCollectionOrder => 'Geben Sie eine ganze Zahl ein.';
+
+ @override
+ String get invalidCollectionTimezone =>
+ 'Geben Sie ein gültiges Kalenderzeitzonendokument mit VTIMEZONE und TZID ein.';
+
+ @override
+ String get invalidCollectionValue =>
+ 'Korrigieren Sie diesen Wert vor dem Speichern.';
+
+ @override
+ String get accountAlreadyConnected =>
+ 'Dieses Konto ist bereits verbunden. Verwenden Sie die Aktion zum erneuten Verbinden.';
+
+ @override
+ String get nextcloudRemovalAuthorizationDescription =>
+ 'BusyMax versucht außerdem, das Nextcloud-App-Passwort dieses Kontos zu widerrufen. Die lokale Entfernung kann auch bei fehlgeschlagenem Widerruf erfolgreich sein.';
}
diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart
index 9b70309e..4d4d76ec 100644
--- a/lib/l10n/generated/app_localizations_en.dart
+++ b/lib/l10n/generated/app_localizations_en.dart
@@ -13,6 +13,9 @@ import 'app_localizations.dart';
class AppLocalizationsEn extends AppLocalizations {
AppLocalizationsEn([String locale = 'en']) : super(locale);
+ @override
+ String get contactsAccountsUnavailable => 'Could not load contact accounts';
+
@override
String get launchAtLoginManagedExternally =>
'BusyMax was launched by your desktop\'s startup settings. Remove its entry there, then quit and reopen BusyMax to use this switch.';
@@ -3038,7 +3041,7 @@ class AppLocalizationsEn extends AppLocalizations {
String get searchClearText => 'Clear text';
@override
- String get openSharedCalendar => 'Open shared calendar';
+ String get openSharedCalendar => 'Open shared primary calendar';
@override
String get manageCalendarSharing => 'Manage calendar sharing';
@@ -3618,4 +3621,157 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get addSeparateGoogleAccount => 'Add a separate Google connection';
+
+ @override
+ String seriesPatternChangeConfirmation(String before, String after) {
+ return 'This changes the recurrence pattern and may add or remove dates.\n\nCurrent: $before\nProposed: $after\n\nSave this pattern?';
+ }
+
+ @override
+ String get connectedAccounts => 'Connected accounts';
+
+ @override
+ String get contactsIndependentAccounts => 'Independent contact accounts';
+
+ @override
+ String get contactsNextcloudReuse =>
+ 'Contact suggestions reuse this account’s existing credential through the read-only contacts integration.';
+
+ @override
+ String get contactsRetrySync => 'Retry contacts sync';
+
+ @override
+ String get contactsPreferenceFailed =>
+ 'Could not save contact source preference';
+
+ @override
+ String get contactsSuggestionsDisabled => 'Contact suggestions disabled';
+
+ @override
+ String get contactsSyncFailed =>
+ 'Contacts could not sync. Retry, or reconnect if authorization has expired.';
+
+ @override
+ String get registrationRecoveryDescription =>
+ 'The saved registration or its credential binding is unavailable or rejected. Explicitly replace the registration for this account to recover; existing data is preserved.';
+
+ @override
+ String get revalidateAccount => 'Revalidate connection';
+
+ @override
+ String get openSharedPrimaryDescription =>
+ 'Opens the specified owner’s primary calendar. Other shared calendars are discovered through ordinary account synchronization.';
+
+ @override
+ String get showInBusyMax => 'Show in BusyMax';
+
+ @override
+ String get showInGoogleCalendarList => 'Show in Google’s calendar list';
+
+ @override
+ String get googleVisibilityHelp =>
+ 'BusyMax selection is local. Google visibility changes your calendar list on Google; hidden calendars cannot be displayed in BusyMax.';
+
+ @override
+ String get primaryCalendarVisibilityExplanation =>
+ 'Google’s primary calendar stays in its calendar list.';
+
+ @override
+ String get accountsLoading => 'Loading accounts…';
+
+ @override
+ String get accountsLoadFailed =>
+ 'Accounts could not be loaded. Retry opening Accounts.';
+
+ @override
+ String get collectionsLoading => 'Loading collections…';
+
+ @override
+ String get collectionsLoadFailed =>
+ 'Collections could not be loaded. Cached data is retained; retry synchronization.';
+
+ @override
+ String get collectionsNotDiscovered =>
+ 'Discovery has not completed. Refresh or reconnect this account.';
+
+ @override
+ String get collectionsEmpty => 'No collections found.';
+
+ @override
+ String get accountReadyToSync => 'Ready to sync';
+
+ @override
+ String get accountSyncProblem => 'Synchronization needs attention';
+
+ @override
+ String get accountServiceUnreachable => 'Could not reach the service';
+
+ @override
+ String get accountSyncDetails => 'Synchronization details';
+
+ @override
+ String get accountReconnectStatus => 'Reconnect required';
+
+ @override
+ String get accountRegistrationStatus => 'Registration needs attention';
+
+ @override
+ String get collectionsUnavailable => 'Collections unavailable';
+
+ @override
+ String get accountContactSuggestions =>
+ 'Suggest guests from this account’s contacts';
+
+ @override
+ String get accountLoadingSignInSettings => 'Loading sign-in settings…';
+
+ @override
+ String get contactsNoAddressBooks => 'No address books available';
+
+ @override
+ String get contactsSourcesUnavailable => 'Could not load address books';
+
+ @override
+ String get syncNoRecord => 'No successful sync time recorded';
+
+ @override
+ String get cachedSyncError => 'Sync failed; cached data may be out of date.';
+
+ @override
+ String get calendarRemotePending => 'Google visibility change pending';
+
+ @override
+ String get calendarRemoteFailed =>
+ 'Google visibility change failed. Google’s last confirmed state is shown. BusyMax still follows the pending change. Retry or discard it.';
+
+ @override
+ String discardCalendarSettingsConfirmation(String calendar, String settings) {
+ return 'Discard the pending changes to “$calendar”?\n\nThese settings will return to their previous values:\n$settings\n\nYour BusyMax calendar selection is kept. No changes will be sent to Google.';
+ }
+
+ @override
+ String get sharedCalendarUnavailable =>
+ 'Access unavailable; showing cached data. Retry synchronization or remove from your calendar list.';
+
+ @override
+ String get invalidCollectionColor =>
+ 'Enter a six- or eight-digit hexadecimal color, starting with #.';
+
+ @override
+ String get invalidCollectionOrder => 'Enter a whole number.';
+
+ @override
+ String get invalidCollectionTimezone =>
+ 'Enter a valid calendar timezone document containing VTIMEZONE and TZID.';
+
+ @override
+ String get invalidCollectionValue => 'Correct this value before saving.';
+
+ @override
+ String get accountAlreadyConnected =>
+ 'This account is already connected. Use its Reconnect action.';
+
+ @override
+ String get nextcloudRemovalAuthorizationDescription =>
+ 'BusyMax will also try to revoke this account’s Nextcloud app password. Local removal can succeed even if that revocation fails.';
}
diff --git a/lib/l10n/generated/app_localizations_es.dart b/lib/l10n/generated/app_localizations_es.dart
index 4d7115a4..ec03f3b8 100644
--- a/lib/l10n/generated/app_localizations_es.dart
+++ b/lib/l10n/generated/app_localizations_es.dart
@@ -13,6 +13,10 @@ import 'app_localizations.dart';
class AppLocalizationsEs extends AppLocalizations {
AppLocalizationsEs([String locale = 'es']) : super(locale);
+ @override
+ String get contactsAccountsUnavailable =>
+ 'No se pudieron cargar las cuentas de contactos';
+
@override
String get launchAtLoginManagedExternally =>
'BusyMax se inició desde los ajustes de inicio del escritorio. Elimina su entrada allí y luego sal de BusyMax y vuelve a abrirlo para usar este interruptor.';
@@ -3010,7 +3014,7 @@ class AppLocalizationsEs extends AppLocalizations {
String get searchClearText => 'Borrar texto';
@override
- String get openSharedCalendar => 'Abrir calendario compartido';
+ String get openSharedCalendar => 'Abrir calendario principal compartido';
@override
String get manageCalendarSharing =>
@@ -3603,4 +3607,165 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get addSeparateGoogleAccount => 'Añadir conexión de Google separada';
+
+ @override
+ String seriesPatternChangeConfirmation(String before, String after) {
+ return 'Este cambio en la repetición puede añadir o eliminar fechas.\n\nActual: $before\nPropuesta: $after\n\n¿Guardar esta repetición?';
+ }
+
+ @override
+ String get connectedAccounts => 'Cuentas conectadas';
+
+ @override
+ String get contactsIndependentAccounts =>
+ 'Cuentas de contactos independientes';
+
+ @override
+ String get contactsNextcloudReuse =>
+ 'Las sugerencias reutilizan las credenciales de esta cuenta mediante la integración de contactos de solo lectura.';
+
+ @override
+ String get contactsRetrySync => 'Reintentar sincronización de contactos';
+
+ @override
+ String get contactsPreferenceFailed =>
+ 'No se pudo guardar la preferencia de la fuente de contactos.';
+
+ @override
+ String get contactsSuggestionsDisabled =>
+ 'Sugerencias de contactos desactivadas';
+
+ @override
+ String get contactsSyncFailed =>
+ 'No se pudieron sincronizar los contactos. Reinténtelo o vuelva a conectar si la autorización caducó.';
+
+ @override
+ String get registrationRecoveryDescription =>
+ 'El registro guardado o su vínculo de credenciales no está disponible o fue rechazado. Sustituya explícitamente el registro de esta cuenta para recuperarla; se conservan los datos.';
+
+ @override
+ String get revalidateAccount => 'Revalidar conexión';
+
+ @override
+ String get openSharedPrimaryDescription =>
+ 'Abre el calendario principal del propietario indicado. Los demás calendarios compartidos se descubren mediante la sincronización habitual.';
+
+ @override
+ String get showInBusyMax => 'Mostrar en BusyMax';
+
+ @override
+ String get showInGoogleCalendarList =>
+ 'Mostrar en la lista de calendarios de Google';
+
+ @override
+ String get googleVisibilityHelp =>
+ 'La selección de BusyMax es local. La visibilidad de Google cambia su lista en Google; los calendarios ocultos no se pueden mostrar en BusyMax.';
+
+ @override
+ String get primaryCalendarVisibilityExplanation =>
+ 'El calendario principal de Google permanece en su lista.';
+
+ @override
+ String get accountsLoading => 'Cargando cuentas…';
+
+ @override
+ String get accountsLoadFailed =>
+ 'No se pudieron cargar las cuentas. Vuelva a abrir Cuentas.';
+
+ @override
+ String get collectionsLoading => 'Cargando colecciones…';
+
+ @override
+ String get collectionsLoadFailed =>
+ 'No se pudieron cargar las colecciones. Se conserva la caché; reintente la sincronización.';
+
+ @override
+ String get collectionsNotDiscovered =>
+ 'El descubrimiento no ha terminado. Actualice o vuelva a conectar esta cuenta.';
+
+ @override
+ String get collectionsEmpty => 'No se encontraron colecciones.';
+
+ @override
+ String get accountReadyToSync => 'Listo para sincronizar';
+
+ @override
+ String get accountSyncProblem => 'La sincronización requiere atención';
+
+ @override
+ String get accountServiceUnreachable => 'No se pudo acceder al servicio';
+
+ @override
+ String get accountSyncDetails => 'Detalles de sincronización';
+
+ @override
+ String get accountReconnectStatus => 'Es necesario volver a conectar';
+
+ @override
+ String get accountRegistrationStatus => 'El registro requiere atención';
+
+ @override
+ String get collectionsUnavailable => 'Colecciones no disponibles';
+
+ @override
+ String get accountContactSuggestions =>
+ 'Sugerir invitados de los contactos de esta cuenta';
+
+ @override
+ String get accountLoadingSignInSettings =>
+ 'Cargando ajustes de inicio de sesión…';
+
+ @override
+ String get contactsNoAddressBooks =>
+ 'No hay libretas de direcciones disponibles';
+
+ @override
+ String get contactsSourcesUnavailable =>
+ 'No se pudieron cargar las libretas de direcciones';
+
+ @override
+ String get syncNoRecord => 'No se registró ninguna sincronización correcta';
+
+ @override
+ String get cachedSyncError =>
+ 'Falló la sincronización; los datos en caché pueden estar desactualizados.';
+
+ @override
+ String get calendarRemotePending =>
+ 'Cambio de visibilidad en Google pendiente';
+
+ @override
+ String get calendarRemoteFailed =>
+ 'Falló el cambio de visibilidad en Google. Se muestra el último estado confirmado de Google. BusyMax sigue aplicando el cambio pendiente. Reinténtalo o descártalo.';
+
+ @override
+ String discardCalendarSettingsConfirmation(String calendar, String settings) {
+ return '¿Descartar los cambios pendientes de «$calendar»?\n\nEstos ajustes volverán a sus valores anteriores:\n$settings\n\nSe conserva tu selección de calendarios en BusyMax. No se enviarán cambios a Google.';
+ }
+
+ @override
+ String get sharedCalendarUnavailable =>
+ 'Acceso no disponible; se muestra la caché. Reintente la sincronización o quite el calendario de su lista.';
+
+ @override
+ String get invalidCollectionColor =>
+ 'Introduzca un color hexadecimal de seis u ocho dígitos que empiece por #.';
+
+ @override
+ String get invalidCollectionOrder => 'Introduzca un número entero.';
+
+ @override
+ String get invalidCollectionTimezone =>
+ 'Introduzca un documento de zona horaria válido que contenga VTIMEZONE y TZID.';
+
+ @override
+ String get invalidCollectionValue => 'Corrija este valor antes de guardar.';
+
+ @override
+ String get accountAlreadyConnected =>
+ 'Esta cuenta ya está conectada. Use su acción de reconexión.';
+
+ @override
+ String get nextcloudRemovalAuthorizationDescription =>
+ 'BusyMax también intentará revocar la contraseña de aplicación de Nextcloud de esta cuenta. La eliminación local puede completarse aunque falle la revocación.';
}
diff --git a/lib/l10n/generated/app_localizations_et.dart b/lib/l10n/generated/app_localizations_et.dart
index b451d802..376f1bd6 100644
--- a/lib/l10n/generated/app_localizations_et.dart
+++ b/lib/l10n/generated/app_localizations_et.dart
@@ -13,6 +13,10 @@ import 'app_localizations.dart';
class AppLocalizationsEt extends AppLocalizations {
AppLocalizationsEt([String locale = 'et']) : super(locale);
+ @override
+ String get contactsAccountsUnavailable =>
+ 'Kontaktikontosid ei õnnestunud laadida';
+
@override
String get launchAtLoginManagedExternally =>
'BusyMax käivitati töölaua automaatkäivituse seadete kaudu. Eemalda sealt selle kirje, seejärel sulge BusyMax täielikult ja ava see lüliti kasutamiseks uuesti.';
@@ -2985,7 +2989,7 @@ class AppLocalizationsEt extends AppLocalizations {
String get searchClearText => 'Tühjenda tekst';
@override
- String get openSharedCalendar => 'Ava jagatud kalender';
+ String get openSharedCalendar => 'Ava jagatud põhikalender';
@override
String get manageCalendarSharing => 'Halda kalendri jagamist';
@@ -3570,4 +3574,161 @@ class AppLocalizationsEt extends AppLocalizations {
@override
String get addSeparateGoogleAccount => 'Lisa eraldi Google’i ühendus';
+
+ @override
+ String seriesPatternChangeConfirmation(String before, String after) {
+ return 'Kordusmustri muutmine võib kuupäevi lisada või eemaldada.\n\nPraegune: $before\nPakutud: $after\n\nKas salvestada see muster?';
+ }
+
+ @override
+ String get connectedAccounts => 'Ühendatud kontod';
+
+ @override
+ String get contactsIndependentAccounts => 'Sõltumatud kontaktikontod';
+
+ @override
+ String get contactsNextcloudReuse =>
+ 'Kontaktisoovitused kasutavad selle konto olemasolevaid pääsuandmeid kirjutuskaitstud kontaktide integratsiooni kaudu.';
+
+ @override
+ String get contactsRetrySync => 'Proovi kontaktide sünkroonimist uuesti';
+
+ @override
+ String get contactsPreferenceFailed =>
+ 'Kontaktiallika eelistust ei saanud salvestada.';
+
+ @override
+ String get contactsSuggestionsDisabled =>
+ 'Kontaktisoovitused on välja lülitatud';
+
+ @override
+ String get contactsSyncFailed =>
+ 'Kontakte ei saanud sünkroonida. Proovi uuesti või ühenda konto uuesti, kui volitus on aegunud.';
+
+ @override
+ String get registrationRecoveryDescription =>
+ 'Salvestatud registreering või selle pääsuandmete seos pole saadaval või on tagasi lükatud. Taastamiseks asenda selle konto registreering sõnaselgelt; andmed säilivad.';
+
+ @override
+ String get revalidateAccount => 'Kontrolli ühendust uuesti';
+
+ @override
+ String get openSharedPrimaryDescription =>
+ 'Avab määratud omaniku põhikalendri. Muud jagatud kalendrid leitakse konto tavapärase sünkroonimise käigus.';
+
+ @override
+ String get showInBusyMax => 'Kuva BusyMaxis';
+
+ @override
+ String get showInGoogleCalendarList => 'Kuva Google’i kalendriloendis';
+
+ @override
+ String get googleVisibilityHelp =>
+ 'BusyMaxi valik on kohalik. Google’i nähtavus muudab sinu loendit Google’is; peidetud kalendreid ei saa BusyMaxis kuvada.';
+
+ @override
+ String get primaryCalendarVisibilityExplanation =>
+ 'Google’i põhikalender jääb loendisse.';
+
+ @override
+ String get accountsLoading => 'Kontode laadimine…';
+
+ @override
+ String get accountsLoadFailed =>
+ 'Kontosid ei saanud laadida. Ava Kontod uuesti.';
+
+ @override
+ String get collectionsLoading => 'Kogumite laadimine…';
+
+ @override
+ String get collectionsLoadFailed =>
+ 'Kogumeid ei saanud laadida. Vahemälu säilib; proovi sünkroonimist uuesti.';
+
+ @override
+ String get collectionsNotDiscovered =>
+ 'Tuvastamine pole lõppenud. Värskenda või ühenda konto uuesti.';
+
+ @override
+ String get collectionsEmpty => 'Kogumeid ei leitud.';
+
+ @override
+ String get accountReadyToSync => 'Sünkroonimiseks valmis';
+
+ @override
+ String get accountSyncProblem => 'Sünkroonimine vajab tähelepanu';
+
+ @override
+ String get accountServiceUnreachable => 'Teenusega ei saanud ühendust';
+
+ @override
+ String get accountSyncDetails => 'Sünkroonimise üksikasjad';
+
+ @override
+ String get accountReconnectStatus => 'Ühenda konto uuesti';
+
+ @override
+ String get accountRegistrationStatus =>
+ 'Rakenduse registreering vajab tähelepanu';
+
+ @override
+ String get collectionsUnavailable => 'Kogud pole saadaval';
+
+ @override
+ String get accountContactSuggestions =>
+ 'Soovita külalisi selle konto kontaktidest';
+
+ @override
+ String get accountLoadingSignInSettings => 'Sisselogimisseadete laadimine…';
+
+ @override
+ String get contactsNoAddressBooks => 'Aadressiraamatud pole saadaval';
+
+ @override
+ String get contactsSourcesUnavailable =>
+ 'Aadressiraamatuid ei saanud laadida';
+
+ @override
+ String get syncNoRecord => 'Eduka sünkroonimise aega pole salvestatud';
+
+ @override
+ String get cachedSyncError =>
+ 'Sünkroonimine nurjus; vahemälu andmed võivad olla aegunud.';
+
+ @override
+ String get calendarRemotePending => 'Google’i nähtavuse muudatus on ootel';
+
+ @override
+ String get calendarRemoteFailed =>
+ 'Google’i nähtavuse muutmine ebaõnnestus. Kuvatakse Google’i viimane kinnitatud olek. BusyMax järgib endiselt ootel muudatust. Proovi uuesti või loobu muudatusest.';
+
+ @override
+ String discardCalendarSettingsConfirmation(String calendar, String settings) {
+ return 'Kas hüljata kalendri „$calendar“ ootel muudatused?\n\nNende seadete eelmised väärtused taastatakse:\n$settings\n\nSinu BusyMaxi kalendrivalik säilib. Google’ile ei saadeta muudatusi.';
+ }
+
+ @override
+ String get sharedCalendarUnavailable =>
+ 'Juurdepääs puudub; kuvatakse vahemälu andmed. Proovi sünkroonimist uuesti või eemalda kalender loendist.';
+
+ @override
+ String get invalidCollectionColor =>
+ 'Sisesta kuue- või kaheksakohaline kuueteistkümnendvärv, alustades märgiga #.';
+
+ @override
+ String get invalidCollectionOrder => 'Sisesta täisarv.';
+
+ @override
+ String get invalidCollectionTimezone =>
+ 'Sisesta kehtiv kalendri ajavööndi dokument, mis sisaldab VTIMEZONE ja TZID väärtusi.';
+
+ @override
+ String get invalidCollectionValue => 'Paranda see väärtus enne salvestamist.';
+
+ @override
+ String get accountAlreadyConnected =>
+ 'See konto on juba ühendatud. Kasuta selle uuesti ühendamise toimingut.';
+
+ @override
+ String get nextcloudRemovalAuthorizationDescription =>
+ 'BusyMax proovib tühistada ka selle konto Nextcloudi rakenduse parooli. Kohalik eemaldamine võib õnnestuda ka siis, kui tühistamine nurjub.';
}
diff --git a/lib/l10n/generated/app_localizations_fa.dart b/lib/l10n/generated/app_localizations_fa.dart
index 02e2c5ca..406e5947 100644
--- a/lib/l10n/generated/app_localizations_fa.dart
+++ b/lib/l10n/generated/app_localizations_fa.dart
@@ -13,6 +13,9 @@ import 'app_localizations.dart';
class AppLocalizationsFa extends AppLocalizations {
AppLocalizationsFa([String locale = 'fa']) : super(locale);
+ @override
+ String get contactsAccountsUnavailable => 'حسابهای مخاطبین بارگیری نشدند';
+
@override
String get launchAtLoginManagedExternally =>
'BusyMax از تنظیمات راهاندازی خودکار میزکار شما اجرا شده است. ورودی آن را در آنجا حذف کنید، سپس از BusyMax خارج شوید و دوباره آن را باز کنید تا بتوانید از این کلید استفاده کنید.';
@@ -3041,7 +3044,7 @@ class AppLocalizationsFa extends AppLocalizations {
String get searchClearText => 'پاک کردن متن';
@override
- String get openSharedCalendar => 'باز کردن تقویم اشتراکی';
+ String get openSharedCalendar => 'باز کردن تقویم اصلی اشتراکی';
@override
String get manageCalendarSharing => 'مدیریت اشتراکگذاری تقویم';
@@ -3625,4 +3628,156 @@ class AppLocalizationsFa extends AppLocalizations {
@override
String get addSeparateGoogleAccount => 'افزودن اتصال Google جداگانه';
+
+ @override
+ String seriesPatternChangeConfirmation(String before, String after) {
+ return 'این تغییر در الگوی تکرار ممکن است تاریخهایی را اضافه یا حذف کند.\n\nفعلی: $before\nپیشنهادی: $after\n\nاین الگو ذخیره شود؟';
+ }
+
+ @override
+ String get connectedAccounts => 'حسابهای متصل';
+
+ @override
+ String get contactsIndependentAccounts => 'حسابهای مستقل مخاطبان';
+
+ @override
+ String get contactsNextcloudReuse =>
+ 'پیشنهادهای مخاطب از اطلاعات ورود فعلی این حساب از طریق یکپارچهسازی فقطخواندنی مخاطبان استفاده میکنند.';
+
+ @override
+ String get contactsRetrySync => 'تلاش دوباره برای همگامسازی مخاطبان';
+
+ @override
+ String get contactsPreferenceFailed => 'ذخیره تنظیم منبع مخاطبان ممکن نشد.';
+
+ @override
+ String get contactsSuggestionsDisabled => 'پیشنهادهای مخاطب غیرفعال است';
+
+ @override
+ String get contactsSyncFailed =>
+ 'همگامسازی مخاطبان انجام نشد. دوباره تلاش کنید یا اگر مجوز منقضی شده حساب را دوباره متصل کنید.';
+
+ @override
+ String get registrationRecoveryDescription =>
+ 'ثبت ذخیرهشده یا پیوند اطلاعات ورود آن در دسترس نیست یا رد شده است. برای بازیابی، ثبت این حساب را بهصراحت جایگزین کنید؛ دادههای فعلی حفظ میشوند.';
+
+ @override
+ String get revalidateAccount => 'بررسی دوباره اتصال';
+
+ @override
+ String get openSharedPrimaryDescription =>
+ 'تقویم اصلی مالک مشخصشده را باز میکند. تقویمهای اشتراکی دیگر از طریق همگامسازی عادی حساب پیدا میشوند.';
+
+ @override
+ String get showInBusyMax => 'نمایش در BusyMax';
+
+ @override
+ String get showInGoogleCalendarList => 'نمایش در فهرست تقویم Google';
+
+ @override
+ String get googleVisibilityHelp =>
+ 'انتخاب BusyMax محلی است. نمایانی Google فهرست شما در Google را تغییر میدهد؛ تقویمهای پنهان در BusyMax نمایش داده نمیشوند.';
+
+ @override
+ String get primaryCalendarVisibilityExplanation =>
+ 'تقویم اصلی Google در فهرست باقی میماند.';
+
+ @override
+ String get accountsLoading => 'در حال بارگذاری حسابها…';
+
+ @override
+ String get accountsLoadFailed =>
+ 'حسابها بارگذاری نشدند. بخش حسابها را دوباره باز کنید.';
+
+ @override
+ String get collectionsLoading => 'در حال بارگذاری مجموعهها…';
+
+ @override
+ String get collectionsLoadFailed =>
+ 'مجموعهها بارگذاری نشدند. دادههای ذخیرهشده حفظ میشوند؛ همگامسازی را دوباره امتحان کنید.';
+
+ @override
+ String get collectionsNotDiscovered =>
+ 'کشف کامل نشده است. این حساب را تازهسازی یا دوباره متصل کنید.';
+
+ @override
+ String get collectionsEmpty => 'مجموعهای پیدا نشد.';
+
+ @override
+ String get accountReadyToSync => 'آمادهٔ همگامسازی';
+
+ @override
+ String get accountSyncProblem => 'همگامسازی نیاز به رسیدگی دارد';
+
+ @override
+ String get accountServiceUnreachable => 'دسترسی به سرویس ممکن نشد';
+
+ @override
+ String get accountSyncDetails => 'جزئیات همگامسازی';
+
+ @override
+ String get accountReconnectStatus => 'اتصال مجدد لازم است';
+
+ @override
+ String get accountRegistrationStatus => 'ثبت برنامه نیاز به رسیدگی دارد';
+
+ @override
+ String get collectionsUnavailable => 'مجموعهها در دسترس نیستند';
+
+ @override
+ String get accountContactSuggestions => 'پیشنهاد مهمان از مخاطبان این حساب';
+
+ @override
+ String get accountLoadingSignInSettings => 'در حال بارگیری تنظیمات ورود…';
+
+ @override
+ String get contactsNoAddressBooks => 'دفترچهٔ نشانی در دسترس نیست';
+
+ @override
+ String get contactsSourcesUnavailable => 'بارگیری دفترچههای نشانی ممکن نشد';
+
+ @override
+ String get syncNoRecord => 'زمان همگامسازی موفق ثبت نشده است';
+
+ @override
+ String get cachedSyncError =>
+ 'همگامسازی ناموفق بود؛ دادههای ذخیرهشده ممکن است قدیمی باشند.';
+
+ @override
+ String get calendarRemotePending => 'تغییر نمایانی Google در انتظار است';
+
+ @override
+ String get calendarRemoteFailed =>
+ 'تغییر نمایش در Google ناموفق بود. آخرین وضعیت تأییدشدهٔ Google نمایش داده میشود. BusyMax همچنان از تغییر در انتظار پیروی میکند. دوباره تلاش کنید یا تغییر را کنار بگذارید.';
+
+ @override
+ String discardCalendarSettingsConfirmation(String calendar, String settings) {
+ return 'تغییرات در انتظار برای «$calendar» کنار گذاشته شوند؟\n\nاین تنظیمات به مقادیر قبلی بازمیگردند:\n$settings\n\nانتخاب تقویم شما در BusyMax حفظ میشود. هیچ تغییری به Google ارسال نخواهد شد.';
+ }
+
+ @override
+ String get sharedCalendarUnavailable =>
+ 'دسترسی موجود نیست؛ دادههای ذخیرهشده نمایش داده میشوند. همگامسازی را دوباره امتحان کنید یا تقویم را از فهرست حذف کنید.';
+
+ @override
+ String get invalidCollectionColor =>
+ 'یک رنگ شانزدهشانزدهی شش یا هشت رقمی با # در ابتدا وارد کنید.';
+
+ @override
+ String get invalidCollectionOrder => 'یک عدد صحیح وارد کنید.';
+
+ @override
+ String get invalidCollectionTimezone =>
+ 'یک سند منطقه زمانی معتبر شامل VTIMEZONE و TZID وارد کنید.';
+
+ @override
+ String get invalidCollectionValue => 'این مقدار را پیش از ذخیره اصلاح کنید.';
+
+ @override
+ String get accountAlreadyConnected =>
+ 'این حساب از قبل متصل است. از گزینه اتصال دوباره آن استفاده کنید.';
+
+ @override
+ String get nextcloudRemovalAuthorizationDescription =>
+ 'BusyMax تلاش میکند گذرواژه برنامه Nextcloud این حساب را نیز لغو کند. حذف محلی حتی در صورت ناموفق بودن لغو میتواند انجام شود.';
}
diff --git a/lib/l10n/generated/app_localizations_fi.dart b/lib/l10n/generated/app_localizations_fi.dart
index c1507c84..4da7fbc5 100644
--- a/lib/l10n/generated/app_localizations_fi.dart
+++ b/lib/l10n/generated/app_localizations_fi.dart
@@ -13,6 +13,9 @@ import 'app_localizations.dart';
class AppLocalizationsFi extends AppLocalizations {
AppLocalizationsFi([String locale = 'fi']) : super(locale);
+ @override
+ String get contactsAccountsUnavailable => 'Yhteystietotilejä ei voitu ladata';
+
@override
String get launchAtLoginManagedExternally =>
'BusyMax käynnistettiin työpöydän automaattisen käynnistyksen asetuksista. Poista sen merkintä sieltä, lopeta BusyMax ja avaa se uudelleen, jotta voit käyttää tätä kytkintä.';
@@ -3035,7 +3038,7 @@ class AppLocalizationsFi extends AppLocalizations {
String get searchClearText => 'Tyhjennä teksti';
@override
- String get openSharedCalendar => 'Avaa jaettu kalenteri';
+ String get openSharedCalendar => 'Avaa jaettu ensisijainen kalenteri';
@override
String get manageCalendarSharing => 'Hallitse kalenterin jakamista';
@@ -3621,4 +3624,162 @@ class AppLocalizationsFi extends AppLocalizations {
@override
String get addSeparateGoogleAccount => 'Lisää erillinen Google-yhteys';
+
+ @override
+ String seriesPatternChangeConfirmation(String before, String after) {
+ return 'Toistumissäännön muuttaminen voi lisätä tai poistaa päivämääriä.\n\nNykyinen: $before\nEhdotettu: $after\n\nTallennetaanko tämä sääntö?';
+ }
+
+ @override
+ String get connectedAccounts => 'Yhdistetyt tilit';
+
+ @override
+ String get contactsIndependentAccounts => 'Erilliset yhteystietotilit';
+
+ @override
+ String get contactsNextcloudReuse =>
+ 'Yhteystietoehdotukset käyttävät tämän tilin nykyisiä tunnistetietoja vain luku -integraation kautta.';
+
+ @override
+ String get contactsRetrySync =>
+ 'Yritä yhteystietojen synkronointia uudelleen';
+
+ @override
+ String get contactsPreferenceFailed =>
+ 'Yhteystietolähteen asetusta ei voitu tallentaa.';
+
+ @override
+ String get contactsSuggestionsDisabled =>
+ 'Yhteystietoehdotukset poistettu käytöstä';
+
+ @override
+ String get contactsSyncFailed =>
+ 'Yhteystietoja ei voitu synkronoida. Yritä uudelleen tai yhdistä tili uudelleen, jos valtuutus on vanhentunut.';
+
+ @override
+ String get registrationRecoveryDescription =>
+ 'Tallennettu rekisteröinti tai sen tunnistetietosidos puuttuu tai on hylätty. Korvaa tämän tilin rekisteröinti erikseen palautusta varten; tiedot säilytetään.';
+
+ @override
+ String get revalidateAccount => 'Tarkista yhteys uudelleen';
+
+ @override
+ String get openSharedPrimaryDescription =>
+ 'Avaa annetun omistajan ensisijaisen kalenterin. Muut jaetut kalenterit löytyvät tavallisen tilisynkronoinnin kautta.';
+
+ @override
+ String get showInBusyMax => 'Näytä BusyMaxissa';
+
+ @override
+ String get showInGoogleCalendarList => 'Näytä Googlen kalenteriluettelossa';
+
+ @override
+ String get googleVisibilityHelp =>
+ 'BusyMaxin valinta on paikallinen. Google-näkyvyys muuttaa Googlen luetteloa; piilotettuja kalentereita ei voi näyttää BusyMaxissa.';
+
+ @override
+ String get primaryCalendarVisibilityExplanation =>
+ 'Googlen ensisijainen kalenteri pysyy luettelossa.';
+
+ @override
+ String get accountsLoading => 'Ladataan tilejä…';
+
+ @override
+ String get accountsLoadFailed =>
+ 'Tilejä ei voitu ladata. Avaa Tilit uudelleen.';
+
+ @override
+ String get collectionsLoading => 'Ladataan kokoelmia…';
+
+ @override
+ String get collectionsLoadFailed =>
+ 'Kokoelmia ei voitu ladata. Välimuisti säilytetään; yritä synkronointia uudelleen.';
+
+ @override
+ String get collectionsNotDiscovered =>
+ 'Haku ei ole vielä valmis. Päivitä tai yhdistä tämä tili uudelleen.';
+
+ @override
+ String get collectionsEmpty => 'Kokoelmia ei löytynyt.';
+
+ @override
+ String get accountReadyToSync => 'Valmis synkronointiin';
+
+ @override
+ String get accountSyncProblem => 'Synkronointi vaatii huomiota';
+
+ @override
+ String get accountServiceUnreachable => 'Palveluun ei saatu yhteyttä';
+
+ @override
+ String get accountSyncDetails => 'Synkronoinnin tiedot';
+
+ @override
+ String get accountReconnectStatus => 'Yhdistä tili uudelleen';
+
+ @override
+ String get accountRegistrationStatus =>
+ 'Sovellusrekisteröinti vaatii huomiota';
+
+ @override
+ String get collectionsUnavailable => 'Kokoelmat eivät ole käytettävissä';
+
+ @override
+ String get accountContactSuggestions =>
+ 'Ehdota vieraita tämän tilin yhteystiedoista';
+
+ @override
+ String get accountLoadingSignInSettings => 'Ladataan kirjautumisasetuksia…';
+
+ @override
+ String get contactsNoAddressBooks => 'Osoitekirjoja ei ole käytettävissä';
+
+ @override
+ String get contactsSourcesUnavailable => 'Osoitekirjoja ei voitu ladata';
+
+ @override
+ String get syncNoRecord =>
+ 'Onnistuneen synkronoinnin aikaa ei ole tallennettu';
+
+ @override
+ String get cachedSyncError =>
+ 'Synkronointi epäonnistui; välimuistin tiedot voivat olla vanhentuneita.';
+
+ @override
+ String get calendarRemotePending => 'Googlen näkyvyysmuutos odottaa';
+
+ @override
+ String get calendarRemoteFailed =>
+ 'Näkyvyyden muuttaminen Googlessa epäonnistui. Googlen viimeisin vahvistettu tila näytetään. BusyMax noudattaa yhä odottavaa muutosta. Yritä uudelleen tai hylkää muutos.';
+
+ @override
+ String discardCalendarSettingsConfirmation(String calendar, String settings) {
+ return 'Hylätäänkö kalenterin ”$calendar” odottavat muutokset?\n\nNämä asetukset palautetaan aiempiin arvoihinsa:\n$settings\n\nBusyMaxin kalenterivalintasi säilyy. Googlelle ei lähetetä muutoksia.';
+ }
+
+ @override
+ String get sharedCalendarUnavailable =>
+ 'Ei käyttöoikeutta; näytetään välimuistin tiedot. Yritä synkronointia uudelleen tai poista kalenteri luettelosta.';
+
+ @override
+ String get invalidCollectionColor =>
+ 'Anna kuusi- tai kahdeksannumeroinen heksaväri, joka alkaa #-merkillä.';
+
+ @override
+ String get invalidCollectionOrder => 'Anna kokonaisluku.';
+
+ @override
+ String get invalidCollectionTimezone =>
+ 'Anna kelvollinen kalenterin aikavyöhykeasiakirja, joka sisältää VTIMEZONE- ja TZID-tiedot.';
+
+ @override
+ String get invalidCollectionValue => 'Korjaa tämä arvo ennen tallentamista.';
+
+ @override
+ String get accountAlreadyConnected =>
+ 'Tämä tili on jo yhdistetty. Käytä sen uudelleenyhdistämistoimintoa.';
+
+ @override
+ String get nextcloudRemovalAuthorizationDescription =>
+ 'BusyMax yrittää myös perua tämän tilin Nextcloud-sovellussalasanan. Paikallinen poisto voi onnistua, vaikka peruminen epäonnistuu.';
}
diff --git a/lib/l10n/generated/app_localizations_fr.dart b/lib/l10n/generated/app_localizations_fr.dart
index 8cf7c074..12c5e608 100644
--- a/lib/l10n/generated/app_localizations_fr.dart
+++ b/lib/l10n/generated/app_localizations_fr.dart
@@ -13,6 +13,10 @@ import 'app_localizations.dart';
class AppLocalizationsFr extends AppLocalizations {
AppLocalizationsFr([String locale = 'fr']) : super(locale);
+ @override
+ String get contactsAccountsUnavailable =>
+ 'Impossible de charger les comptes de contacts';
+
@override
String get launchAtLoginManagedExternally =>
'BusyMax a été lancé par les paramètres de démarrage de votre bureau. Supprimez son entrée à cet endroit, puis quittez et rouvrez BusyMax pour utiliser cet interrupteur.';
@@ -3011,7 +3015,7 @@ class AppLocalizationsFr extends AppLocalizations {
String get searchClearText => 'Effacer le texte';
@override
- String get openSharedCalendar => 'Ouvrir un calendrier partagé';
+ String get openSharedCalendar => 'Ouvrir un calendrier principal partagé';
@override
String get manageCalendarSharing => 'Gérer le partage de l’agenda';
@@ -3609,4 +3613,167 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get addSeparateGoogleAccount =>
'Ajouter une connexion Google distincte';
+
+ @override
+ String seriesPatternChangeConfirmation(String before, String after) {
+ return 'Cette modification du schéma de répétition peut ajouter ou supprimer des dates.\n\nActuel : $before\nProposé : $after\n\nEnregistrer ce schéma ?';
+ }
+
+ @override
+ String get connectedAccounts => 'Comptes connectés';
+
+ @override
+ String get contactsIndependentAccounts => 'Comptes de contacts indépendants';
+
+ @override
+ String get contactsNextcloudReuse =>
+ 'Les suggestions réutilisent les identifiants de ce compte via l’intégration de contacts en lecture seule.';
+
+ @override
+ String get contactsRetrySync => 'Réessayer la synchronisation des contacts';
+
+ @override
+ String get contactsPreferenceFailed =>
+ 'Impossible d’enregistrer la préférence de la source de contacts.';
+
+ @override
+ String get contactsSuggestionsDisabled =>
+ 'Suggestions de contacts désactivées';
+
+ @override
+ String get contactsSyncFailed =>
+ 'Les contacts n’ont pas pu être synchronisés. Réessayez ou reconnectez le compte si l’autorisation a expiré.';
+
+ @override
+ String get registrationRecoveryDescription =>
+ 'L’inscription enregistrée ou son association aux identifiants est indisponible ou refusée. Remplacez explicitement l’inscription de ce compte pour le récupérer ; les données sont conservées.';
+
+ @override
+ String get revalidateAccount => 'Revérifier la connexion';
+
+ @override
+ String get openSharedPrimaryDescription =>
+ 'Ouvre le calendrier principal du propriétaire indiqué. Les autres calendriers partagés sont détectés lors de la synchronisation habituelle.';
+
+ @override
+ String get showInBusyMax => 'Afficher dans BusyMax';
+
+ @override
+ String get showInGoogleCalendarList =>
+ 'Afficher dans la liste de calendriers Google';
+
+ @override
+ String get googleVisibilityHelp =>
+ 'La sélection BusyMax est locale. La visibilité Google modifie votre liste chez Google ; les calendriers masqués ne peuvent pas être affichés dans BusyMax.';
+
+ @override
+ String get primaryCalendarVisibilityExplanation =>
+ 'Le calendrier principal Google reste dans sa liste.';
+
+ @override
+ String get accountsLoading => 'Chargement des comptes…';
+
+ @override
+ String get accountsLoadFailed =>
+ 'Impossible de charger les comptes. Rouvrez Comptes.';
+
+ @override
+ String get collectionsLoading => 'Chargement des collections…';
+
+ @override
+ String get collectionsLoadFailed =>
+ 'Impossible de charger les collections. Le cache est conservé ; réessayez la synchronisation.';
+
+ @override
+ String get collectionsNotDiscovered =>
+ 'La découverte n’est pas terminée. Actualisez ou reconnectez ce compte.';
+
+ @override
+ String get collectionsEmpty => 'Aucune collection trouvée.';
+
+ @override
+ String get accountReadyToSync => 'Prêt à synchroniser';
+
+ @override
+ String get accountSyncProblem =>
+ 'La synchronisation nécessite une intervention';
+
+ @override
+ String get accountServiceUnreachable => 'Impossible de joindre le service';
+
+ @override
+ String get accountSyncDetails => 'Détails de synchronisation';
+
+ @override
+ String get accountReconnectStatus => 'Reconnexion nécessaire';
+
+ @override
+ String get accountRegistrationStatus =>
+ 'L’enregistrement nécessite une intervention';
+
+ @override
+ String get collectionsUnavailable => 'Collections indisponibles';
+
+ @override
+ String get accountContactSuggestions =>
+ 'Suggérer des invités à partir des contacts de ce compte';
+
+ @override
+ String get accountLoadingSignInSettings =>
+ 'Chargement des paramètres de connexion…';
+
+ @override
+ String get contactsNoAddressBooks => 'Aucun carnet d’adresses disponible';
+
+ @override
+ String get contactsSourcesUnavailable =>
+ 'Impossible de charger les carnets d’adresses';
+
+ @override
+ String get syncNoRecord =>
+ 'Aucune date de synchronisation réussie enregistrée';
+
+ @override
+ String get cachedSyncError =>
+ 'Échec de synchronisation ; les données en cache peuvent être anciennes.';
+
+ @override
+ String get calendarRemotePending =>
+ 'Modification de visibilité Google en attente';
+
+ @override
+ String get calendarRemoteFailed =>
+ 'La modification de visibilité dans Google a échoué. Le dernier état confirmé de Google est affiché. BusyMax suit toujours la modification en attente. Réessayez ou abandonnez cette modification.';
+
+ @override
+ String discardCalendarSettingsConfirmation(String calendar, String settings) {
+ return 'Abandonner les modifications en attente pour « $calendar » ?\n\nCes paramètres retrouveront leurs valeurs précédentes :\n$settings\n\nVotre sélection de calendriers dans BusyMax est conservée. Aucune modification ne sera envoyée à Google.';
+ }
+
+ @override
+ String get sharedCalendarUnavailable =>
+ 'Accès indisponible ; affichage du cache. Réessayez la synchronisation ou retirez le calendrier de votre liste.';
+
+ @override
+ String get invalidCollectionColor =>
+ 'Saisissez une couleur hexadécimale de six ou huit chiffres commençant par #.';
+
+ @override
+ String get invalidCollectionOrder => 'Saisissez un nombre entier.';
+
+ @override
+ String get invalidCollectionTimezone =>
+ 'Saisissez un document de fuseau horaire valide contenant VTIMEZONE et TZID.';
+
+ @override
+ String get invalidCollectionValue =>
+ 'Corrigez cette valeur avant d’enregistrer.';
+
+ @override
+ String get accountAlreadyConnected =>
+ 'Ce compte est déjà connecté. Utilisez son action de reconnexion.';
+
+ @override
+ String get nextcloudRemovalAuthorizationDescription =>
+ 'BusyMax tentera aussi de révoquer le mot de passe d’application Nextcloud de ce compte. La suppression locale peut réussir même si cette révocation échoue.';
}
diff --git a/lib/l10n/generated/app_localizations_hi.dart b/lib/l10n/generated/app_localizations_hi.dart
index 5f31072d..cf700463 100644
--- a/lib/l10n/generated/app_localizations_hi.dart
+++ b/lib/l10n/generated/app_localizations_hi.dart
@@ -13,6 +13,9 @@ import 'app_localizations.dart';
class AppLocalizationsHi extends AppLocalizations {
AppLocalizationsHi([String locale = 'hi']) : super(locale);
+ @override
+ String get contactsAccountsUnavailable => 'संपर्क खाते लोड नहीं किए जा सके';
+
@override
String get launchAtLoginManagedExternally =>
'BusyMax आपके डेस्कटॉप की स्टार्टअप सेटिंग से शुरू हुआ था। वहाँ से इसकी प्रविष्टि हटाएँ, फिर इस स्विच का उपयोग करने के लिए BusyMax से बाहर निकलें और इसे दोबारा खोलें।';
@@ -3006,7 +3009,7 @@ class AppLocalizationsHi extends AppLocalizations {
String get searchClearText => 'पाठ साफ़ करें';
@override
- String get openSharedCalendar => 'साझा कैलेंडर खोलें';
+ String get openSharedCalendar => 'साझा मुख्य कैलेंडर खोलें';
@override
String get manageCalendarSharing => 'कैलेंडर साझाकरण प्रबंधित करें';
@@ -3591,4 +3594,156 @@ class AppLocalizationsHi extends AppLocalizations {
@override
String get addSeparateGoogleAccount => 'अलग Google कनेक्शन जोड़ें';
+
+ @override
+ String seriesPatternChangeConfirmation(String before, String after) {
+ return 'दोहराव का यह बदलाव तारीखें जोड़ या हटा सकता है।\n\nवर्तमान: $before\nप्रस्तावित: $after\n\nयह पैटर्न सहेजें?';
+ }
+
+ @override
+ String get connectedAccounts => 'जुड़े हुए खाते';
+
+ @override
+ String get contactsIndependentAccounts => 'स्वतंत्र संपर्क खाते';
+
+ @override
+ String get contactsNextcloudReuse =>
+ 'संपर्क सुझाव केवल पढ़ने वाले संपर्क एकीकरण के माध्यम से इस खाते के मौजूदा क्रेडेंशियल का दोबारा उपयोग करते हैं।';
+
+ @override
+ String get contactsRetrySync => 'संपर्क सिंक फिर आज़माएँ';
+
+ @override
+ String get contactsPreferenceFailed =>
+ 'संपर्क स्रोत की प्राथमिकता सहेजी नहीं जा सकी।';
+
+ @override
+ String get contactsSuggestionsDisabled => 'संपर्क सुझाव बंद हैं';
+
+ @override
+ String get contactsSyncFailed =>
+ 'संपर्क सिंक नहीं हो सके। फिर आज़माएँ, या अनुमति समाप्त होने पर खाता फिर जोड़ें।';
+
+ @override
+ String get registrationRecoveryDescription =>
+ 'सहेजा गया पंजीकरण या उसका क्रेडेंशियल संबंध उपलब्ध नहीं है या अस्वीकार कर दिया गया है। बहाली के लिए इस खाते का पंजीकरण स्पष्ट रूप से बदलें; मौजूदा डेटा सुरक्षित रहेगा।';
+
+ @override
+ String get revalidateAccount => 'कनेक्शन फिर सत्यापित करें';
+
+ @override
+ String get openSharedPrimaryDescription =>
+ 'निर्दिष्ट मालिक का मुख्य कैलेंडर खोलता है। अन्य साझा कैलेंडर सामान्य खाता सिंक में खोजे जाते हैं।';
+
+ @override
+ String get showInBusyMax => 'BusyMax में दिखाएँ';
+
+ @override
+ String get showInGoogleCalendarList => 'Google की कैलेंडर सूची में दिखाएँ';
+
+ @override
+ String get googleVisibilityHelp =>
+ 'BusyMax का चयन स्थानीय है। Google दृश्यता Google पर आपकी सूची बदलती है; छिपे कैलेंडर BusyMax में नहीं दिखाए जा सकते।';
+
+ @override
+ String get primaryCalendarVisibilityExplanation =>
+ 'Google का मुख्य कैलेंडर उसकी सूची में रहता है।';
+
+ @override
+ String get accountsLoading => 'खाते लोड हो रहे हैं…';
+
+ @override
+ String get accountsLoadFailed => 'खाते लोड नहीं हो सके। खाते फिर खोलें।';
+
+ @override
+ String get collectionsLoading => 'संग्रह लोड हो रहे हैं…';
+
+ @override
+ String get collectionsLoadFailed =>
+ 'संग्रह लोड नहीं हो सके। कैश डेटा सुरक्षित है; सिंक फिर आज़माएँ।';
+
+ @override
+ String get collectionsNotDiscovered =>
+ 'खोज पूरी नहीं हुई है। इस खाते को रीफ़्रेश करें या फिर जोड़ें।';
+
+ @override
+ String get collectionsEmpty => 'कोई संग्रह नहीं मिला।';
+
+ @override
+ String get accountReadyToSync => 'सिंक करने के लिए तैयार';
+
+ @override
+ String get accountSyncProblem => 'सिंक पर ध्यान देना आवश्यक है';
+
+ @override
+ String get accountServiceUnreachable => 'सेवा से संपर्क नहीं हो सका';
+
+ @override
+ String get accountSyncDetails => 'सिंक का विवरण';
+
+ @override
+ String get accountReconnectStatus => 'दोबारा कनेक्ट करना आवश्यक है';
+
+ @override
+ String get accountRegistrationStatus => 'ऐप पंजीकरण पर ध्यान देना आवश्यक है';
+
+ @override
+ String get collectionsUnavailable => 'संग्रह उपलब्ध नहीं हैं';
+
+ @override
+ String get accountContactSuggestions =>
+ 'इस खाते के संपर्कों से मेहमान सुझाएँ';
+
+ @override
+ String get accountLoadingSignInSettings => 'साइन-इन सेटिंग लोड हो रही हैं…';
+
+ @override
+ String get contactsNoAddressBooks => 'कोई पता पुस्तिका उपलब्ध नहीं है';
+
+ @override
+ String get contactsSourcesUnavailable => 'पता पुस्तिकाएँ लोड नहीं हो सकीं';
+
+ @override
+ String get syncNoRecord => 'सफल सिंक का समय दर्ज नहीं है';
+
+ @override
+ String get cachedSyncError => 'सिंक विफल हुआ; कैश डेटा पुराना हो सकता है।';
+
+ @override
+ String get calendarRemotePending => 'Google दृश्यता बदलाव लंबित है';
+
+ @override
+ String get calendarRemoteFailed =>
+ 'Google की दृश्यता बदलना विफल रहा। Google की पिछली पुष्ट स्थिति दिखाई गई है। BusyMax अभी भी लंबित बदलाव का पालन करता है। फिर से कोशिश करें या बदलाव छोड़ दें।';
+
+ @override
+ String discardCalendarSettingsConfirmation(String calendar, String settings) {
+ return '“$calendar” के लंबित बदलाव छोड़ें?\n\nये सेटिंग अपने पिछले मानों पर लौट जाएँगी:\n$settings\n\nBusyMax में आपका कैलेंडर चयन बना रहेगा। Google को कोई बदलाव नहीं भेजा जाएगा।';
+ }
+
+ @override
+ String get sharedCalendarUnavailable =>
+ 'पहुँच उपलब्ध नहीं; कैश डेटा दिखाया जा रहा है। सिंक फिर आज़माएँ या कैलेंडर को अपनी सूची से हटाएँ।';
+
+ @override
+ String get invalidCollectionColor =>
+ '# से शुरू होने वाला छह या आठ अंकों का हेक्स रंग दर्ज करें।';
+
+ @override
+ String get invalidCollectionOrder => 'एक पूर्णांक दर्ज करें।';
+
+ @override
+ String get invalidCollectionTimezone =>
+ 'VTIMEZONE और TZID वाला मान्य कैलेंडर समय क्षेत्र दस्तावेज़ दर्ज करें।';
+
+ @override
+ String get invalidCollectionValue => 'सहेजने से पहले यह मान सुधारें।';
+
+ @override
+ String get accountAlreadyConnected =>
+ 'यह खाता पहले से जुड़ा है। इसके फिर से जोड़ने वाले विकल्प का उपयोग करें।';
+
+ @override
+ String get nextcloudRemovalAuthorizationDescription =>
+ 'BusyMax इस खाते का Nextcloud ऐप पासवर्ड निरस्त करने का भी प्रयास करेगा। निरस्तीकरण विफल होने पर भी स्थानीय हटाना सफल हो सकता है।';
}
diff --git a/lib/l10n/generated/app_localizations_it.dart b/lib/l10n/generated/app_localizations_it.dart
index f6761f95..131b70c2 100644
--- a/lib/l10n/generated/app_localizations_it.dart
+++ b/lib/l10n/generated/app_localizations_it.dart
@@ -13,6 +13,10 @@ import 'app_localizations.dart';
class AppLocalizationsIt extends AppLocalizations {
AppLocalizationsIt([String locale = 'it']) : super(locale);
+ @override
+ String get contactsAccountsUnavailable =>
+ 'Impossibile caricare gli account dei contatti';
+
@override
String get launchAtLoginManagedExternally =>
'BusyMax è stato avviato dalle impostazioni di avvio del desktop. Rimuovi la sua voce da lì, poi esci da BusyMax e riaprilo per usare questo interruttore.';
@@ -3026,7 +3030,7 @@ class AppLocalizationsIt extends AppLocalizations {
String get searchClearText => 'Cancella testo';
@override
- String get openSharedCalendar => 'Apri calendario condiviso';
+ String get openSharedCalendar => 'Apri calendario principale condiviso';
@override
String get manageCalendarSharing => 'Gestisci condivisione del calendario';
@@ -3617,4 +3621,164 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get addSeparateGoogleAccount => 'Aggiungi connessione Google separata';
+
+ @override
+ String seriesPatternChangeConfirmation(String before, String after) {
+ return 'Questa modifica della ricorrenza può aggiungere o rimuovere date.\n\nAttuale: $before\nProposta: $after\n\nSalvare questa ricorrenza?';
+ }
+
+ @override
+ String get connectedAccounts => 'Account collegati';
+
+ @override
+ String get contactsIndependentAccounts => 'Account di contatti indipendenti';
+
+ @override
+ String get contactsNextcloudReuse =>
+ 'I suggerimenti riutilizzano le credenziali di questo account tramite l’integrazione dei contatti in sola lettura.';
+
+ @override
+ String get contactsRetrySync => 'Riprova sincronizzazione contatti';
+
+ @override
+ String get contactsPreferenceFailed =>
+ 'Impossibile salvare la preferenza della fonte dei contatti.';
+
+ @override
+ String get contactsSuggestionsDisabled =>
+ 'Suggerimenti dei contatti disattivati';
+
+ @override
+ String get contactsSyncFailed =>
+ 'Impossibile sincronizzare i contatti. Riprova oppure ricollega l’account se l’autorizzazione è scaduta.';
+
+ @override
+ String get registrationRecoveryDescription =>
+ 'La registrazione salvata o il collegamento alle credenziali non è disponibile o è stato rifiutato. Sostituisci esplicitamente la registrazione di questo account per recuperarlo; i dati vengono conservati.';
+
+ @override
+ String get revalidateAccount => 'Verifica di nuovo la connessione';
+
+ @override
+ String get openSharedPrimaryDescription =>
+ 'Apre il calendario principale del proprietario specificato. Gli altri calendari condivisi vengono rilevati tramite la normale sincronizzazione.';
+
+ @override
+ String get showInBusyMax => 'Mostra in BusyMax';
+
+ @override
+ String get showInGoogleCalendarList =>
+ 'Mostra nell’elenco dei calendari Google';
+
+ @override
+ String get googleVisibilityHelp =>
+ 'La selezione di BusyMax è locale. La visibilità Google modifica il tuo elenco su Google; i calendari nascosti non possono essere visualizzati in BusyMax.';
+
+ @override
+ String get primaryCalendarVisibilityExplanation =>
+ 'Il calendario principale Google rimane nel suo elenco.';
+
+ @override
+ String get accountsLoading => 'Caricamento account…';
+
+ @override
+ String get accountsLoadFailed =>
+ 'Impossibile caricare gli account. Riapri Account.';
+
+ @override
+ String get collectionsLoading => 'Caricamento raccolte…';
+
+ @override
+ String get collectionsLoadFailed =>
+ 'Impossibile caricare le raccolte. I dati in cache sono conservati; riprova la sincronizzazione.';
+
+ @override
+ String get collectionsNotDiscovered =>
+ 'Il rilevamento non è completo. Aggiorna o ricollega questo account.';
+
+ @override
+ String get collectionsEmpty => 'Nessuna raccolta trovata.';
+
+ @override
+ String get accountReadyToSync => 'Pronto per la sincronizzazione';
+
+ @override
+ String get accountSyncProblem => 'La sincronizzazione richiede attenzione';
+
+ @override
+ String get accountServiceUnreachable => 'Impossibile raggiungere il servizio';
+
+ @override
+ String get accountSyncDetails => 'Dettagli di sincronizzazione';
+
+ @override
+ String get accountReconnectStatus => 'È necessario riconnettere l’account';
+
+ @override
+ String get accountRegistrationStatus =>
+ 'La registrazione richiede attenzione';
+
+ @override
+ String get collectionsUnavailable => 'Raccolte non disponibili';
+
+ @override
+ String get accountContactSuggestions =>
+ 'Suggerisci invitati dai contatti di questo account';
+
+ @override
+ String get accountLoadingSignInSettings =>
+ 'Caricamento delle impostazioni di accesso…';
+
+ @override
+ String get contactsNoAddressBooks => 'Nessuna rubrica disponibile';
+
+ @override
+ String get contactsSourcesUnavailable => 'Impossibile caricare le rubriche';
+
+ @override
+ String get syncNoRecord =>
+ 'Nessuna data di sincronizzazione riuscita registrata';
+
+ @override
+ String get cachedSyncError =>
+ 'Sincronizzazione non riuscita; i dati in cache potrebbero essere obsoleti.';
+
+ @override
+ String get calendarRemotePending => 'Modifica visibilità Google in attesa';
+
+ @override
+ String get calendarRemoteFailed =>
+ 'La modifica della visibilità su Google non è riuscita. Viene mostrato l’ultimo stato confermato da Google. BusyMax continua a seguire la modifica in sospeso. Riprova o scarta la modifica.';
+
+ @override
+ String discardCalendarSettingsConfirmation(String calendar, String settings) {
+ return 'Scartare le modifiche in attesa per «$calendar»?\n\nQueste impostazioni torneranno ai valori precedenti:\n$settings\n\nLa selezione dei calendari in BusyMax viene mantenuta. Non verranno inviate modifiche a Google.';
+ }
+
+ @override
+ String get sharedCalendarUnavailable =>
+ 'Accesso non disponibile; vengono mostrati i dati in cache. Riprova o rimuovi il calendario dal tuo elenco.';
+
+ @override
+ String get invalidCollectionColor =>
+ 'Inserisci un colore esadecimale di sei o otto cifre che inizi con #.';
+
+ @override
+ String get invalidCollectionOrder => 'Inserisci un numero intero.';
+
+ @override
+ String get invalidCollectionTimezone =>
+ 'Inserisci un documento di fuso orario valido contenente VTIMEZONE e TZID.';
+
+ @override
+ String get invalidCollectionValue =>
+ 'Correggi questo valore prima di salvare.';
+
+ @override
+ String get accountAlreadyConnected =>
+ 'Questo account è già collegato. Usa la sua azione di riconnessione.';
+
+ @override
+ String get nextcloudRemovalAuthorizationDescription =>
+ 'BusyMax tenterà anche di revocare la password per app Nextcloud di questo account. La rimozione locale può riuscire anche se la revoca non riesce.';
}
diff --git a/lib/l10n/generated/app_localizations_ja.dart b/lib/l10n/generated/app_localizations_ja.dart
index cee642ae..e22a18b4 100644
--- a/lib/l10n/generated/app_localizations_ja.dart
+++ b/lib/l10n/generated/app_localizations_ja.dart
@@ -13,6 +13,9 @@ import 'app_localizations.dart';
class AppLocalizationsJa extends AppLocalizations {
AppLocalizationsJa([String locale = 'ja']) : super(locale);
+ @override
+ String get contactsAccountsUnavailable => '連絡先アカウントを読み込めませんでした';
+
@override
String get launchAtLoginManagedExternally =>
'BusyMax はデスクトップの自動起動設定によって起動されました。このスイッチを使うには、そこで BusyMax の項目を削除してから、BusyMax を終了して開き直してください。';
@@ -2943,7 +2946,7 @@ class AppLocalizationsJa extends AppLocalizations {
String get searchClearText => 'テキストを消去';
@override
- String get openSharedCalendar => '共有カレンダーを開く';
+ String get openSharedCalendar => '共有メインカレンダーを開く';
@override
String get manageCalendarSharing => 'カレンダーの共有を管理';
@@ -3507,4 +3510,152 @@ class AppLocalizationsJa extends AppLocalizations {
@override
String get addSeparateGoogleAccount => '別の Google 接続を追加';
+
+ @override
+ String seriesPatternChangeConfirmation(String before, String after) {
+ return '繰り返しパターンの変更により、日付が追加または削除される場合があります。\n\n現在:$before\n変更案:$after\n\nこのパターンを保存しますか?';
+ }
+
+ @override
+ String get connectedAccounts => '接続済みアカウント';
+
+ @override
+ String get contactsIndependentAccounts => '独立した連絡先アカウント';
+
+ @override
+ String get contactsNextcloudReuse =>
+ '連絡先候補は、読み取り専用の連絡先連携を通じて、このアカウントの既存の認証情報を再利用します。';
+
+ @override
+ String get contactsRetrySync => '連絡先の同期を再試行';
+
+ @override
+ String get contactsPreferenceFailed => '連絡先ソースの設定を保存できませんでした。';
+
+ @override
+ String get contactsSuggestionsDisabled => '連絡先候補は無効です';
+
+ @override
+ String get contactsSyncFailed =>
+ '連絡先を同期できませんでした。再試行するか、認可が期限切れの場合はアカウントを再接続してください。';
+
+ @override
+ String get registrationRecoveryDescription =>
+ '保存済みのアプリ登録または認証情報との関連付けが利用できないか、拒否されています。復旧するには、このアカウントの登録を明示的に置き換えてください。既存のデータは保持されます。';
+
+ @override
+ String get revalidateAccount => '接続を再検証';
+
+ @override
+ String get openSharedPrimaryDescription =>
+ '指定した所有者のメインカレンダーを開きます。他の共有カレンダーは通常のアカウント同期で検出されます。';
+
+ @override
+ String get showInBusyMax => 'BusyMax に表示';
+
+ @override
+ String get showInGoogleCalendarList => 'Google のカレンダーリストに表示';
+
+ @override
+ String get googleVisibilityHelp =>
+ 'BusyMax の選択はローカル設定です。Google の表示設定は Google 側のリストを変更します。非表示のカレンダーは BusyMax に表示できません。';
+
+ @override
+ String get primaryCalendarVisibilityExplanation =>
+ 'Google のメインカレンダーはリストに残ります。';
+
+ @override
+ String get accountsLoading => 'アカウントを読み込み中…';
+
+ @override
+ String get accountsLoadFailed => 'アカウントを読み込めませんでした。「アカウント」を開き直してください。';
+
+ @override
+ String get collectionsLoading => 'コレクションを読み込み中…';
+
+ @override
+ String get collectionsLoadFailed =>
+ 'コレクションを読み込めませんでした。キャッシュは保持されています。同期を再試行してください。';
+
+ @override
+ String get collectionsNotDiscovered => '検出が完了していません。このアカウントを更新または再接続してください。';
+
+ @override
+ String get collectionsEmpty => 'コレクションが見つかりません。';
+
+ @override
+ String get accountReadyToSync => '同期の準備完了';
+
+ @override
+ String get accountSyncProblem => '同期への対応が必要';
+
+ @override
+ String get accountServiceUnreachable => 'サービスに接続できませんでした';
+
+ @override
+ String get accountSyncDetails => '同期の詳細';
+
+ @override
+ String get accountReconnectStatus => '再接続が必要';
+
+ @override
+ String get accountRegistrationStatus => 'アプリ登録への対応が必要';
+
+ @override
+ String get collectionsUnavailable => 'コレクションを利用できません';
+
+ @override
+ String get accountContactSuggestions => 'このアカウントの連絡先からゲストを候補に表示';
+
+ @override
+ String get accountLoadingSignInSettings => 'サインイン設定を読み込み中…';
+
+ @override
+ String get contactsNoAddressBooks => '利用できるアドレス帳がありません';
+
+ @override
+ String get contactsSourcesUnavailable => 'アドレス帳を読み込めませんでした';
+
+ @override
+ String get syncNoRecord => '成功した同期時刻の記録がありません';
+
+ @override
+ String get cachedSyncError => '同期に失敗しました。キャッシュのデータが古い可能性があります。';
+
+ @override
+ String get calendarRemotePending => 'Google の表示設定の変更を待機中';
+
+ @override
+ String get calendarRemoteFailed =>
+ 'Google での表示設定の変更に失敗しました。Google で最後に確認された状態が表示されています。BusyMax は引き続き保留中の変更に従います。再試行するか、変更を破棄してください。';
+
+ @override
+ String discardCalendarSettingsConfirmation(String calendar, String settings) {
+ return '「$calendar」の保留中の変更を破棄しますか?\n\n次の設定を以前の値に戻します:\n$settings\n\nBusyMaxでのカレンダーの選択は保持されます。Googleに変更は送信されません。';
+ }
+
+ @override
+ String get sharedCalendarUnavailable =>
+ 'アクセスできません。キャッシュを表示しています。同期を再試行するか、リストから削除してください。';
+
+ @override
+ String get invalidCollectionColor => '# で始まる6桁または8桁の16進数の色を入力してください。';
+
+ @override
+ String get invalidCollectionOrder => '整数を入力してください。';
+
+ @override
+ String get invalidCollectionTimezone =>
+ 'VTIMEZONE と TZID を含む有効なカレンダーのタイムゾーン文書を入力してください。';
+
+ @override
+ String get invalidCollectionValue => '保存する前にこの値を修正してください。';
+
+ @override
+ String get accountAlreadyConnected =>
+ 'このアカウントは既に接続されています。このアカウントの再接続操作を使用してください。';
+
+ @override
+ String get nextcloudRemovalAuthorizationDescription =>
+ 'BusyMax はこのアカウントの Nextcloud アプリパスワードの失効も試みます。失効に失敗しても、ローカルでの削除は完了する場合があります。';
}
diff --git a/lib/l10n/generated/app_localizations_ko.dart b/lib/l10n/generated/app_localizations_ko.dart
index e4657194..2655c364 100644
--- a/lib/l10n/generated/app_localizations_ko.dart
+++ b/lib/l10n/generated/app_localizations_ko.dart
@@ -13,6 +13,9 @@ import 'app_localizations.dart';
class AppLocalizationsKo extends AppLocalizations {
AppLocalizationsKo([String locale = 'ko']) : super(locale);
+ @override
+ String get contactsAccountsUnavailable => '연락처 계정을 불러올 수 없습니다';
+
@override
String get launchAtLoginManagedExternally =>
'데스크톱의 시작 프로그램 설정에 의해 BusyMax가 실행되었습니다. 이 스위치를 사용하려면 해당 설정에서 BusyMax 항목을 제거한 다음 BusyMax를 완전히 종료하고 다시 여세요.';
@@ -2948,7 +2951,7 @@ class AppLocalizationsKo extends AppLocalizations {
String get searchClearText => '텍스트 지우기';
@override
- String get openSharedCalendar => '공유 캘린더 열기';
+ String get openSharedCalendar => '공유 기본 캘린더 열기';
@override
String get manageCalendarSharing => '캘린더 공유 관리';
@@ -3510,4 +3513,153 @@ class AppLocalizationsKo extends AppLocalizations {
@override
String get addSeparateGoogleAccount => '별도의 Google 연결 추가';
+
+ @override
+ String seriesPatternChangeConfirmation(String before, String after) {
+ return '반복 패턴을 변경하면 날짜가 추가되거나 삭제될 수 있습니다.\n\n현재: $before\n제안: $after\n\n이 패턴을 저장할까요?';
+ }
+
+ @override
+ String get connectedAccounts => '연결된 계정';
+
+ @override
+ String get contactsIndependentAccounts => '독립 연락처 계정';
+
+ @override
+ String get contactsNextcloudReuse =>
+ '연락처 제안은 읽기 전용 연락처 통합을 통해 이 계정의 기존 자격 증명을 재사용합니다.';
+
+ @override
+ String get contactsRetrySync => '연락처 동기화 다시 시도';
+
+ @override
+ String get contactsPreferenceFailed => '연락처 소스 설정을 저장하지 못했습니다.';
+
+ @override
+ String get contactsSuggestionsDisabled => '연락처 제안 꺼짐';
+
+ @override
+ String get contactsSyncFailed =>
+ '연락처를 동기화하지 못했습니다. 다시 시도하거나 권한이 만료된 경우 계정을 다시 연결하세요.';
+
+ @override
+ String get registrationRecoveryDescription =>
+ '저장된 앱 등록 또는 자격 증명 연결을 사용할 수 없거나 거부되었습니다. 복구하려면 이 계정의 등록을 명시적으로 교체하세요. 기존 데이터는 유지됩니다.';
+
+ @override
+ String get revalidateAccount => '연결 다시 검증';
+
+ @override
+ String get openSharedPrimaryDescription =>
+ '지정한 소유자의 기본 캘린더를 엽니다. 다른 공유 캘린더는 일반 계정 동기화에서 검색됩니다.';
+
+ @override
+ String get showInBusyMax => 'BusyMax에 표시';
+
+ @override
+ String get showInGoogleCalendarList => 'Google 캘린더 목록에 표시';
+
+ @override
+ String get googleVisibilityHelp =>
+ 'BusyMax 선택은 로컬 설정입니다. Google 표시 설정은 Google의 목록을 변경하며 숨겨진 캘린더는 BusyMax에 표시할 수 없습니다.';
+
+ @override
+ String get primaryCalendarVisibilityExplanation =>
+ 'Google 기본 캘린더는 목록에 유지됩니다.';
+
+ @override
+ String get accountsLoading => '계정 불러오는 중…';
+
+ @override
+ String get accountsLoadFailed => '계정을 불러오지 못했습니다. 계정을 다시 여세요.';
+
+ @override
+ String get collectionsLoading => '컬렉션 불러오는 중…';
+
+ @override
+ String get collectionsLoadFailed =>
+ '컬렉션을 불러오지 못했습니다. 캐시는 유지됩니다. 동기화를 다시 시도하세요.';
+
+ @override
+ String get collectionsNotDiscovered =>
+ '검색이 완료되지 않았습니다. 이 계정을 새로 고치거나 다시 연결하세요.';
+
+ @override
+ String get collectionsEmpty => '컬렉션을 찾지 못했습니다.';
+
+ @override
+ String get accountReadyToSync => '동기화 준비됨';
+
+ @override
+ String get accountSyncProblem => '동기화에 조치가 필요합니다';
+
+ @override
+ String get accountServiceUnreachable => '서비스에 연결할 수 없습니다';
+
+ @override
+ String get accountSyncDetails => '동기화 세부 정보';
+
+ @override
+ String get accountReconnectStatus => '다시 연결해야 합니다';
+
+ @override
+ String get accountRegistrationStatus => '앱 등록에 조치가 필요합니다';
+
+ @override
+ String get collectionsUnavailable => '컬렉션을 사용할 수 없습니다';
+
+ @override
+ String get accountContactSuggestions => '이 계정의 연락처에서 참석자 제안';
+
+ @override
+ String get accountLoadingSignInSettings => '로그인 설정을 불러오는 중…';
+
+ @override
+ String get contactsNoAddressBooks => '사용 가능한 주소록이 없습니다';
+
+ @override
+ String get contactsSourcesUnavailable => '주소록을 불러올 수 없습니다';
+
+ @override
+ String get syncNoRecord => '성공한 동기화 시간이 기록되지 않음';
+
+ @override
+ String get cachedSyncError => '동기화 실패. 캐시 데이터가 오래되었을 수 있습니다.';
+
+ @override
+ String get calendarRemotePending => 'Google 표시 변경 대기 중';
+
+ @override
+ String get calendarRemoteFailed =>
+ 'Google 표시 설정 변경에 실패했습니다. 마지막으로 확인된 Google 상태가 표시됩니다. BusyMax는 계속 대기 중인 변경을 따릅니다. 다시 시도하거나 변경을 취소하세요.';
+
+ @override
+ String discardCalendarSettingsConfirmation(String calendar, String settings) {
+ return '“$calendar”의 대기 중인 변경사항을 취소할까요?\n\n다음 설정이 이전 값으로 복원됩니다:\n$settings\n\nBusyMax의 캘린더 선택은 유지됩니다. Google에 변경사항이 전송되지 않습니다.';
+ }
+
+ @override
+ String get sharedCalendarUnavailable =>
+ '접근할 수 없어 캐시를 표시합니다. 동기화를 다시 시도하거나 캘린더를 목록에서 제거하세요.';
+
+ @override
+ String get invalidCollectionColor => '#으로 시작하는 6자리 또는 8자리 16진수 색상을 입력하세요.';
+
+ @override
+ String get invalidCollectionOrder => '정수를 입력하세요.';
+
+ @override
+ String get invalidCollectionTimezone =>
+ 'VTIMEZONE 및 TZID를 포함하는 올바른 캘린더 시간대 문서를 입력하세요.';
+
+ @override
+ String get invalidCollectionValue => '저장하기 전에 이 값을 수정하세요.';
+
+ @override
+ String get accountAlreadyConnected =>
+ '이 계정은 이미 연결되어 있습니다. 해당 계정의 다시 연결 작업을 사용하세요.';
+
+ @override
+ String get nextcloudRemovalAuthorizationDescription =>
+ 'BusyMax는 이 계정의 Nextcloud 앱 암호 해지도 시도합니다. 해지에 실패해도 로컬 제거는 완료될 수 있습니다.';
}
diff --git a/lib/l10n/generated/app_localizations_pl.dart b/lib/l10n/generated/app_localizations_pl.dart
index 7968dd5b..61ddb96a 100644
--- a/lib/l10n/generated/app_localizations_pl.dart
+++ b/lib/l10n/generated/app_localizations_pl.dart
@@ -13,6 +13,10 @@ import 'app_localizations.dart';
class AppLocalizationsPl extends AppLocalizations {
AppLocalizationsPl([String locale = 'pl']) : super(locale);
+ @override
+ String get contactsAccountsUnavailable =>
+ 'Nie udało się wczytać kont kontaktów';
+
@override
String get launchAtLoginManagedExternally =>
'BusyMax został uruchomiony przez ustawienia autostartu pulpitu. Usuń stamtąd jego wpis, a następnie zakończ i ponownie uruchom BusyMax, aby użyć tego przełącznika.';
@@ -3180,7 +3184,7 @@ class AppLocalizationsPl extends AppLocalizations {
String get searchClearText => 'Wyczyść tekst';
@override
- String get openSharedCalendar => 'Otwórz udostępniony kalendarz';
+ String get openSharedCalendar => 'Otwórz udostępniony kalendarz główny';
@override
String get manageCalendarSharing => 'Zarządzaj udostępnianiem kalendarza';
@@ -3770,4 +3774,160 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String get addSeparateGoogleAccount => 'Dodaj osobne połączenie Google';
+
+ @override
+ String seriesPatternChangeConfirmation(String before, String after) {
+ return 'Ta zmiana reguły powtarzania może dodać lub usunąć daty.\n\nObecnie: $before\nPropozycja: $after\n\nZapisać tę regułę?';
+ }
+
+ @override
+ String get connectedAccounts => 'Połączone konta';
+
+ @override
+ String get contactsIndependentAccounts => 'Niezależne konta kontaktów';
+
+ @override
+ String get contactsNextcloudReuse =>
+ 'Sugestie używają istniejących danych logowania tego konta przez integrację kontaktów tylko do odczytu.';
+
+ @override
+ String get contactsRetrySync => 'Ponów synchronizację kontaktów';
+
+ @override
+ String get contactsPreferenceFailed =>
+ 'Nie udało się zapisać ustawienia źródła kontaktów.';
+
+ @override
+ String get contactsSuggestionsDisabled => 'Sugestie kontaktów wyłączone';
+
+ @override
+ String get contactsSyncFailed =>
+ 'Nie udało się zsynchronizować kontaktów. Spróbuj ponownie lub połącz konto ponownie, jeśli autoryzacja wygasła.';
+
+ @override
+ String get registrationRecoveryDescription =>
+ 'Zapisana rejestracja lub powiązanie danych logowania jest niedostępne albo odrzucone. Jawnie zastąp rejestrację tego konta, aby je odzyskać; dane zostaną zachowane.';
+
+ @override
+ String get revalidateAccount => 'Sprawdź połączenie ponownie';
+
+ @override
+ String get openSharedPrimaryDescription =>
+ 'Otwiera główny kalendarz wskazanego właściciela. Inne udostępnione kalendarze są wykrywane podczas zwykłej synchronizacji konta.';
+
+ @override
+ String get showInBusyMax => 'Pokaż w BusyMax';
+
+ @override
+ String get showInGoogleCalendarList => 'Pokaż na liście kalendarzy Google';
+
+ @override
+ String get googleVisibilityHelp =>
+ 'Wybór BusyMax jest lokalny. Widoczność Google zmienia listę w Google; ukrytych kalendarzy nie można wyświetlić w BusyMax.';
+
+ @override
+ String get primaryCalendarVisibilityExplanation =>
+ 'Główny kalendarz Google pozostaje na liście.';
+
+ @override
+ String get accountsLoading => 'Ładowanie kont…';
+
+ @override
+ String get accountsLoadFailed =>
+ 'Nie udało się załadować kont. Otwórz Konta ponownie.';
+
+ @override
+ String get collectionsLoading => 'Ładowanie kolekcji…';
+
+ @override
+ String get collectionsLoadFailed =>
+ 'Nie udało się załadować kolekcji. Pamięć podręczna jest zachowana; ponów synchronizację.';
+
+ @override
+ String get collectionsNotDiscovered =>
+ 'Wykrywanie nie zostało zakończone. Odśwież lub połącz konto ponownie.';
+
+ @override
+ String get collectionsEmpty => 'Nie znaleziono kolekcji.';
+
+ @override
+ String get accountReadyToSync => 'Gotowe do synchronizacji';
+
+ @override
+ String get accountSyncProblem => 'Synchronizacja wymaga uwagi';
+
+ @override
+ String get accountServiceUnreachable => 'Nie można połączyć się z usługą';
+
+ @override
+ String get accountSyncDetails => 'Szczegóły synchronizacji';
+
+ @override
+ String get accountReconnectStatus => 'Wymagane ponowne połączenie';
+
+ @override
+ String get accountRegistrationStatus => 'Rejestracja aplikacji wymaga uwagi';
+
+ @override
+ String get collectionsUnavailable => 'Kolekcje niedostępne';
+
+ @override
+ String get accountContactSuggestions =>
+ 'Sugeruj gości z kontaktów tego konta';
+
+ @override
+ String get accountLoadingSignInSettings => 'Wczytywanie ustawień logowania…';
+
+ @override
+ String get contactsNoAddressBooks => 'Brak dostępnych książek adresowych';
+
+ @override
+ String get contactsSourcesUnavailable =>
+ 'Nie można wczytać książek adresowych';
+
+ @override
+ String get syncNoRecord => 'Brak zapisanego czasu udanej synchronizacji';
+
+ @override
+ String get cachedSyncError =>
+ 'Synchronizacja nie powiodła się; dane w pamięci podręcznej mogą być nieaktualne.';
+
+ @override
+ String get calendarRemotePending =>
+ 'Oczekiwanie na zmianę widoczności Google';
+
+ @override
+ String get calendarRemoteFailed =>
+ 'Zmiana widoczności w Google nie powiodła się. Wyświetlany jest ostatni potwierdzony stan Google. BusyMax nadal stosuje oczekującą zmianę. Ponów próbę lub odrzuć zmianę.';
+
+ @override
+ String discardCalendarSettingsConfirmation(String calendar, String settings) {
+ return 'Odrzucić oczekujące zmiany kalendarza „$calendar”?\n\nTe ustawienia wrócą do poprzednich wartości:\n$settings\n\nWybór kalendarzy w BusyMax zostanie zachowany. Żadne zmiany nie zostaną wysłane do Google.';
+ }
+
+ @override
+ String get sharedCalendarUnavailable =>
+ 'Dostęp niedostępny; wyświetlane są dane z pamięci podręcznej. Ponów synchronizację lub usuń kalendarz z listy.';
+
+ @override
+ String get invalidCollectionColor =>
+ 'Wpisz sześcio- lub ośmiocyfrowy kolor szesnastkowy zaczynający się od #.';
+
+ @override
+ String get invalidCollectionOrder => 'Wpisz liczbę całkowitą.';
+
+ @override
+ String get invalidCollectionTimezone =>
+ 'Wpisz prawidłowy dokument strefy czasowej zawierający VTIMEZONE i TZID.';
+
+ @override
+ String get invalidCollectionValue => 'Popraw tę wartość przed zapisaniem.';
+
+ @override
+ String get accountAlreadyConnected =>
+ 'To konto jest już połączone. Użyj jego opcji ponownego połączenia.';
+
+ @override
+ String get nextcloudRemovalAuthorizationDescription =>
+ 'BusyMax spróbuje też unieważnić hasło aplikacji Nextcloud tego konta. Usunięcie lokalne może się udać nawet wtedy, gdy unieważnienie się nie powiedzie.';
}
diff --git a/lib/l10n/generated/app_localizations_pt.dart b/lib/l10n/generated/app_localizations_pt.dart
index 5bf8633c..45ac28c5 100644
--- a/lib/l10n/generated/app_localizations_pt.dart
+++ b/lib/l10n/generated/app_localizations_pt.dart
@@ -13,6 +13,9 @@ import 'app_localizations.dart';
class AppLocalizationsPt extends AppLocalizations {
AppLocalizationsPt([String locale = 'pt']) : super(locale);
+ @override
+ String get contactsAccountsUnavailable => 'Could not load contact accounts';
+
@override
String get launchAtLoginManagedExternally =>
'BusyMax was launched by your desktop\'s startup settings. Remove its entry there, then quit and reopen BusyMax to use this switch.';
@@ -3038,7 +3041,7 @@ class AppLocalizationsPt extends AppLocalizations {
String get searchClearText => 'Clear text';
@override
- String get openSharedCalendar => 'Open shared calendar';
+ String get openSharedCalendar => 'Open shared primary calendar';
@override
String get manageCalendarSharing => 'Manage calendar sharing';
@@ -3618,12 +3621,169 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get addSeparateGoogleAccount => 'Add a separate Google connection';
+
+ @override
+ String seriesPatternChangeConfirmation(String before, String after) {
+ return 'This changes the recurrence pattern and may add or remove dates.\n\nCurrent: $before\nProposed: $after\n\nSave this pattern?';
+ }
+
+ @override
+ String get connectedAccounts => 'Connected accounts';
+
+ @override
+ String get contactsIndependentAccounts => 'Independent contact accounts';
+
+ @override
+ String get contactsNextcloudReuse =>
+ 'Contact suggestions reuse this account’s existing credential through the read-only contacts integration.';
+
+ @override
+ String get contactsRetrySync => 'Retry contacts sync';
+
+ @override
+ String get contactsPreferenceFailed =>
+ 'Could not save contact source preference';
+
+ @override
+ String get contactsSuggestionsDisabled => 'Contact suggestions disabled';
+
+ @override
+ String get contactsSyncFailed =>
+ 'Contacts could not sync. Retry, or reconnect if authorization has expired.';
+
+ @override
+ String get registrationRecoveryDescription =>
+ 'The saved registration or its credential binding is unavailable or rejected. Explicitly replace the registration for this account to recover; existing data is preserved.';
+
+ @override
+ String get revalidateAccount => 'Revalidate connection';
+
+ @override
+ String get openSharedPrimaryDescription =>
+ 'Opens the specified owner’s primary calendar. Other shared calendars are discovered through ordinary account synchronization.';
+
+ @override
+ String get showInBusyMax => 'Show in BusyMax';
+
+ @override
+ String get showInGoogleCalendarList => 'Show in Google’s calendar list';
+
+ @override
+ String get googleVisibilityHelp =>
+ 'BusyMax selection is local. Google visibility changes your calendar list on Google; hidden calendars cannot be displayed in BusyMax.';
+
+ @override
+ String get primaryCalendarVisibilityExplanation =>
+ 'Google’s primary calendar stays in its calendar list.';
+
+ @override
+ String get accountsLoading => 'Loading accounts…';
+
+ @override
+ String get accountsLoadFailed =>
+ 'Accounts could not be loaded. Retry opening Accounts.';
+
+ @override
+ String get collectionsLoading => 'Loading collections…';
+
+ @override
+ String get collectionsLoadFailed =>
+ 'Collections could not be loaded. Cached data is retained; retry synchronization.';
+
+ @override
+ String get collectionsNotDiscovered =>
+ 'Discovery has not completed. Refresh or reconnect this account.';
+
+ @override
+ String get collectionsEmpty => 'No collections found.';
+
+ @override
+ String get accountReadyToSync => 'Ready to sync';
+
+ @override
+ String get accountSyncProblem => 'Synchronization needs attention';
+
+ @override
+ String get accountServiceUnreachable => 'Could not reach the service';
+
+ @override
+ String get accountSyncDetails => 'Synchronization details';
+
+ @override
+ String get accountReconnectStatus => 'Reconnect required';
+
+ @override
+ String get accountRegistrationStatus => 'Registration needs attention';
+
+ @override
+ String get collectionsUnavailable => 'Collections unavailable';
+
+ @override
+ String get accountContactSuggestions =>
+ 'Suggest guests from this account’s contacts';
+
+ @override
+ String get accountLoadingSignInSettings => 'Loading sign-in settings…';
+
+ @override
+ String get contactsNoAddressBooks => 'No address books available';
+
+ @override
+ String get contactsSourcesUnavailable => 'Could not load address books';
+
+ @override
+ String get syncNoRecord => 'No successful sync time recorded';
+
+ @override
+ String get cachedSyncError => 'Sync failed; cached data may be out of date.';
+
+ @override
+ String get calendarRemotePending => 'Google visibility change pending';
+
+ @override
+ String get calendarRemoteFailed =>
+ 'Google visibility change failed. Google’s last confirmed state is shown. BusyMax still follows the pending change. Retry or discard it.';
+
+ @override
+ String discardCalendarSettingsConfirmation(String calendar, String settings) {
+ return 'Discard the pending changes to “$calendar”?\n\nThese settings will return to their previous values:\n$settings\n\nYour BusyMax calendar selection is kept. No changes will be sent to Google.';
+ }
+
+ @override
+ String get sharedCalendarUnavailable =>
+ 'Access unavailable; showing cached data. Retry synchronization or remove from your calendar list.';
+
+ @override
+ String get invalidCollectionColor =>
+ 'Enter a six- or eight-digit hexadecimal color, starting with #.';
+
+ @override
+ String get invalidCollectionOrder => 'Enter a whole number.';
+
+ @override
+ String get invalidCollectionTimezone =>
+ 'Enter a valid calendar timezone document containing VTIMEZONE and TZID.';
+
+ @override
+ String get invalidCollectionValue => 'Correct this value before saving.';
+
+ @override
+ String get accountAlreadyConnected =>
+ 'This account is already connected. Use its Reconnect action.';
+
+ @override
+ String get nextcloudRemovalAuthorizationDescription =>
+ 'BusyMax will also try to revoke this account’s Nextcloud app password. Local removal can succeed even if that revocation fails.';
}
/// The translations for Portuguese, as used in Portugal (`pt_PT`).
class AppLocalizationsPtPt extends AppLocalizationsPt {
AppLocalizationsPtPt() : super('pt_PT');
+ @override
+ String get contactsAccountsUnavailable =>
+ 'Não foi possível carregar as contas de contactos';
+
@override
String get launchAtLoginManagedExternally =>
'O BusyMax foi iniciado pelas definições de arranque do ambiente de trabalho. Remova a respetiva entrada, depois saia do BusyMax e volte a abri-lo para utilizar este interruptor.';
@@ -6637,7 +6797,7 @@ class AppLocalizationsPtPt extends AppLocalizationsPt {
String get searchClearText => 'Limpar texto';
@override
- String get openSharedCalendar => 'Abrir calendário partilhado';
+ String get openSharedCalendar => 'Abrir calendário principal partilhado';
@override
String get manageCalendarSharing => 'Gerir partilha do calendário';
@@ -7228,4 +7388,165 @@ class AppLocalizationsPtPt extends AppLocalizationsPt {
@override
String get addSeparateGoogleAccount => 'Adicionar ligação Google separada';
+
+ @override
+ String seriesPatternChangeConfirmation(String before, String after) {
+ return 'Esta alteração da recorrência pode adicionar ou remover datas.\n\nAtual: $before\nProposta: $after\n\nGuardar esta recorrência?';
+ }
+
+ @override
+ String get connectedAccounts => 'Contas ligadas';
+
+ @override
+ String get contactsIndependentAccounts => 'Contas de contactos independentes';
+
+ @override
+ String get contactsNextcloudReuse =>
+ 'As sugestões reutilizam as credenciais desta conta através da integração de contactos só de leitura.';
+
+ @override
+ String get contactsRetrySync => 'Repetir sincronização de contactos';
+
+ @override
+ String get contactsPreferenceFailed =>
+ 'Não foi possível guardar a preferência da origem de contactos.';
+
+ @override
+ String get contactsSuggestionsDisabled =>
+ 'Sugestões de contactos desativadas';
+
+ @override
+ String get contactsSyncFailed =>
+ 'Não foi possível sincronizar os contactos. Tente novamente ou volte a ligar a conta se a autorização expirou.';
+
+ @override
+ String get registrationRecoveryDescription =>
+ 'O registo guardado ou a associação às credenciais está indisponível ou foi rejeitado. Substitua explicitamente o registo desta conta para a recuperar; os dados são preservados.';
+
+ @override
+ String get revalidateAccount => 'Revalidar ligação';
+
+ @override
+ String get openSharedPrimaryDescription =>
+ 'Abre o calendário principal do proprietário indicado. Outros calendários partilhados são detetados na sincronização normal da conta.';
+
+ @override
+ String get showInBusyMax => 'Mostrar no BusyMax';
+
+ @override
+ String get showInGoogleCalendarList =>
+ 'Mostrar na lista de calendários do Google';
+
+ @override
+ String get googleVisibilityHelp =>
+ 'A seleção do BusyMax é local. A visibilidade do Google altera a sua lista no Google; calendários ocultos não podem ser apresentados no BusyMax.';
+
+ @override
+ String get primaryCalendarVisibilityExplanation =>
+ 'O calendário principal do Google permanece na lista.';
+
+ @override
+ String get accountsLoading => 'A carregar contas…';
+
+ @override
+ String get accountsLoadFailed =>
+ 'Não foi possível carregar as contas. Volte a abrir Contas.';
+
+ @override
+ String get collectionsLoading => 'A carregar coleções…';
+
+ @override
+ String get collectionsLoadFailed =>
+ 'Não foi possível carregar as coleções. Os dados em cache são preservados; repita a sincronização.';
+
+ @override
+ String get collectionsNotDiscovered =>
+ 'A descoberta não terminou. Atualize ou volte a ligar esta conta.';
+
+ @override
+ String get collectionsEmpty => 'Não foram encontradas coleções.';
+
+ @override
+ String get accountReadyToSync => 'Pronto para sincronizar';
+
+ @override
+ String get accountSyncProblem => 'A sincronização requer atenção';
+
+ @override
+ String get accountServiceUnreachable =>
+ 'Não foi possível contactar o serviço';
+
+ @override
+ String get accountSyncDetails => 'Detalhes de sincronização';
+
+ @override
+ String get accountReconnectStatus => 'É necessário voltar a ligar';
+
+ @override
+ String get accountRegistrationStatus => 'O registo requer atenção';
+
+ @override
+ String get collectionsUnavailable => 'Coleções indisponíveis';
+
+ @override
+ String get accountContactSuggestions =>
+ 'Sugerir convidados a partir dos contactos desta conta';
+
+ @override
+ String get accountLoadingSignInSettings =>
+ 'A carregar as definições de início de sessão…';
+
+ @override
+ String get contactsNoAddressBooks => 'Nenhum livro de endereços disponível';
+
+ @override
+ String get contactsSourcesUnavailable =>
+ 'Não foi possível carregar os livros de endereços';
+
+ @override
+ String get syncNoRecord =>
+ 'Nenhuma data de sincronização bem-sucedida registada';
+
+ @override
+ String get cachedSyncError =>
+ 'A sincronização falhou; os dados em cache podem estar desatualizados.';
+
+ @override
+ String get calendarRemotePending =>
+ 'Alteração de visibilidade no Google pendente';
+
+ @override
+ String get calendarRemoteFailed =>
+ 'A alteração de visibilidade no Google falhou. É apresentado o último estado confirmado do Google. O BusyMax continua a seguir a alteração pendente. Tente novamente ou descarte a alteração.';
+
+ @override
+ String discardCalendarSettingsConfirmation(String calendar, String settings) {
+ return 'Descartar as alterações pendentes de «$calendar»?\n\nEstas definições regressarão aos valores anteriores:\n$settings\n\nA sua seleção de calendários no BusyMax será mantida. Não serão enviadas alterações à Google.';
+ }
+
+ @override
+ String get sharedCalendarUnavailable =>
+ 'Acesso indisponível; a mostrar dados em cache. Repita a sincronização ou remova o calendário da sua lista.';
+
+ @override
+ String get invalidCollectionColor =>
+ 'Introduza uma cor hexadecimal de seis ou oito dígitos, começando por #.';
+
+ @override
+ String get invalidCollectionOrder => 'Introduza um número inteiro.';
+
+ @override
+ String get invalidCollectionTimezone =>
+ 'Introduza um documento de fuso horário válido com VTIMEZONE e TZID.';
+
+ @override
+ String get invalidCollectionValue => 'Corrija este valor antes de guardar.';
+
+ @override
+ String get accountAlreadyConnected =>
+ 'Esta conta já está ligada. Utilize a ação para voltar a ligar.';
+
+ @override
+ String get nextcloudRemovalAuthorizationDescription =>
+ 'O BusyMax tentará também revogar a palavra-passe de aplicação Nextcloud desta conta. A remoção local pode ser concluída mesmo que a revogação falhe.';
}
diff --git a/lib/l10n/generated/app_localizations_ru.dart b/lib/l10n/generated/app_localizations_ru.dart
index 311e6ac5..a3867b5f 100644
--- a/lib/l10n/generated/app_localizations_ru.dart
+++ b/lib/l10n/generated/app_localizations_ru.dart
@@ -13,6 +13,10 @@ import 'app_localizations.dart';
class AppLocalizationsRu extends AppLocalizations {
AppLocalizationsRu([String locale = 'ru']) : super(locale);
+ @override
+ String get contactsAccountsUnavailable =>
+ 'Не удалось загрузить учётные записи контактов';
+
@override
String get launchAtLoginManagedExternally =>
'BusyMax запущен через настройки автозапуска рабочего стола. Удалите его запись там, затем полностью завершите и снова откройте BusyMax, чтобы использовать этот переключатель.';
@@ -3030,7 +3034,7 @@ class AppLocalizationsRu extends AppLocalizations {
String get searchClearText => 'Очистить текст';
@override
- String get openSharedCalendar => 'Открыть общий календарь';
+ String get openSharedCalendar => 'Открыть общий основной календарь';
@override
String get manageCalendarSharing => 'Управление доступом к календарю';
@@ -3630,4 +3634,162 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get addSeparateGoogleAccount =>
'Добавить отдельное подключение Google';
+
+ @override
+ String seriesPatternChangeConfirmation(String before, String after) {
+ return 'Изменение правила повторения может добавить или удалить даты.\n\nСейчас: $before\nПредложено: $after\n\nСохранить это правило?';
+ }
+
+ @override
+ String get connectedAccounts => 'Подключённые аккаунты';
+
+ @override
+ String get contactsIndependentAccounts => 'Независимые аккаунты контактов';
+
+ @override
+ String get contactsNextcloudReuse =>
+ 'Подсказки используют существующие учётные данные этого аккаунта через интеграцию контактов только для чтения.';
+
+ @override
+ String get contactsRetrySync => 'Повторить синхронизацию контактов';
+
+ @override
+ String get contactsPreferenceFailed =>
+ 'Не удалось сохранить настройку источника контактов.';
+
+ @override
+ String get contactsSuggestionsDisabled => 'Подсказки контактов отключены';
+
+ @override
+ String get contactsSyncFailed =>
+ 'Не удалось синхронизировать контакты. Повторите попытку или переподключите аккаунт, если авторизация истекла.';
+
+ @override
+ String get registrationRecoveryDescription =>
+ 'Сохранённая регистрация или привязка учётных данных недоступна либо отклонена. Для восстановления явно замените регистрацию этого аккаунта; данные сохранятся.';
+
+ @override
+ String get revalidateAccount => 'Проверить подключение';
+
+ @override
+ String get openSharedPrimaryDescription =>
+ 'Открывает основной календарь указанного владельца. Другие общие календари обнаруживаются при обычной синхронизации аккаунта.';
+
+ @override
+ String get showInBusyMax => 'Показывать в BusyMax';
+
+ @override
+ String get showInGoogleCalendarList =>
+ 'Показывать в списке календарей Google';
+
+ @override
+ String get googleVisibilityHelp =>
+ 'Выбор BusyMax действует локально. Видимость Google меняет ваш список в Google; скрытые календари нельзя показать в BusyMax.';
+
+ @override
+ String get primaryCalendarVisibilityExplanation =>
+ 'Основной календарь Google остаётся в списке.';
+
+ @override
+ String get accountsLoading => 'Загрузка аккаунтов…';
+
+ @override
+ String get accountsLoadFailed =>
+ 'Не удалось загрузить аккаунты. Откройте раздел «Аккаунты» заново.';
+
+ @override
+ String get collectionsLoading => 'Загрузка коллекций…';
+
+ @override
+ String get collectionsLoadFailed =>
+ 'Не удалось загрузить коллекции. Кэш сохранён; повторите синхронизацию.';
+
+ @override
+ String get collectionsNotDiscovered =>
+ 'Обнаружение ещё не завершено. Обновите или переподключите аккаунт.';
+
+ @override
+ String get collectionsEmpty => 'Коллекции не найдены.';
+
+ @override
+ String get accountReadyToSync => 'Готово к синхронизации';
+
+ @override
+ String get accountSyncProblem => 'Синхронизация требует внимания';
+
+ @override
+ String get accountServiceUnreachable => 'Не удалось связаться с сервисом';
+
+ @override
+ String get accountSyncDetails => 'Подробности синхронизации';
+
+ @override
+ String get accountReconnectStatus => 'Требуется повторное подключение';
+
+ @override
+ String get accountRegistrationStatus =>
+ 'Регистрация приложения требует внимания';
+
+ @override
+ String get collectionsUnavailable => 'Коллекции недоступны';
+
+ @override
+ String get accountContactSuggestions =>
+ 'Предлагать гостей из контактов этого аккаунта';
+
+ @override
+ String get accountLoadingSignInSettings => 'Загрузка настроек входа…';
+
+ @override
+ String get contactsNoAddressBooks => 'Нет доступных адресных книг';
+
+ @override
+ String get contactsSourcesUnavailable =>
+ 'Не удалось загрузить адресные книги';
+
+ @override
+ String get syncNoRecord => 'Время успешной синхронизации не записано';
+
+ @override
+ String get cachedSyncError =>
+ 'Сбой синхронизации; данные в кэше могут быть устаревшими.';
+
+ @override
+ String get calendarRemotePending => 'Ожидается изменение видимости в Google';
+
+ @override
+ String get calendarRemoteFailed =>
+ 'Не удалось изменить видимость в Google. Показано последнее подтверждённое состояние Google. BusyMax по-прежнему учитывает ожидающее изменение. Повторите попытку или отмените изменение.';
+
+ @override
+ String discardCalendarSettingsConfirmation(String calendar, String settings) {
+ return 'Отменить ожидающие изменения календаря «$calendar»?\n\nБудут восстановлены предыдущие значения этих настроек:\n$settings\n\nВыбор календарей в BusyMax сохранится. Изменения не будут отправлены в Google.';
+ }
+
+ @override
+ String get sharedCalendarUnavailable =>
+ 'Нет доступа; показаны данные из кэша. Повторите синхронизацию или удалите календарь из своего списка.';
+
+ @override
+ String get invalidCollectionColor =>
+ 'Введите шестизначный или восьмизначный шестнадцатеричный цвет, начиная с #.';
+
+ @override
+ String get invalidCollectionOrder => 'Введите целое число.';
+
+ @override
+ String get invalidCollectionTimezone =>
+ 'Введите корректный документ часового пояса с VTIMEZONE и TZID.';
+
+ @override
+ String get invalidCollectionValue =>
+ 'Исправьте это значение перед сохранением.';
+
+ @override
+ String get accountAlreadyConnected =>
+ 'Этот аккаунт уже подключён. Используйте его действие переподключения.';
+
+ @override
+ String get nextcloudRemovalAuthorizationDescription =>
+ 'BusyMax также попытается отозвать пароль приложения Nextcloud этого аккаунта. Локальное удаление может завершиться, даже если отзыв не удался.';
}
diff --git a/lib/l10n/generated/app_localizations_vi.dart b/lib/l10n/generated/app_localizations_vi.dart
index eb83a942..5d8a9cb3 100644
--- a/lib/l10n/generated/app_localizations_vi.dart
+++ b/lib/l10n/generated/app_localizations_vi.dart
@@ -13,6 +13,10 @@ import 'app_localizations.dart';
class AppLocalizationsVi extends AppLocalizations {
AppLocalizationsVi([String locale = 'vi']) : super(locale);
+ @override
+ String get contactsAccountsUnavailable =>
+ 'Không thể tải các tài khoản danh bạ';
+
@override
String get launchAtLoginManagedExternally =>
'BusyMax được mở bởi cài đặt khởi động của môi trường máy tính. Xóa mục BusyMax ở đó, rồi thoát hẳn và mở lại BusyMax để dùng công tắc này.';
@@ -3003,7 +3007,7 @@ class AppLocalizationsVi extends AppLocalizations {
String get searchClearText => 'Xóa văn bản';
@override
- String get openSharedCalendar => 'Mở lịch được chia sẻ';
+ String get openSharedCalendar => 'Mở lịch chính được chia sẻ';
@override
String get manageCalendarSharing => 'Quản lý chia sẻ lịch';
@@ -3585,4 +3589,157 @@ class AppLocalizationsVi extends AppLocalizations {
@override
String get addSeparateGoogleAccount => 'Thêm kết nối Google riêng';
+
+ @override
+ String seriesPatternChangeConfirmation(String before, String after) {
+ return 'Thay đổi kiểu lặp có thể thêm hoặc xóa ngày.\n\nHiện tại: $before\nĐề xuất: $after\n\nLưu kiểu lặp này?';
+ }
+
+ @override
+ String get connectedAccounts => 'Tài khoản đã kết nối';
+
+ @override
+ String get contactsIndependentAccounts => 'Tài khoản danh bạ độc lập';
+
+ @override
+ String get contactsNextcloudReuse =>
+ 'Gợi ý liên hệ dùng lại thông tin xác thực hiện có của tài khoản qua tích hợp danh bạ chỉ đọc.';
+
+ @override
+ String get contactsRetrySync => 'Thử đồng bộ danh bạ lại';
+
+ @override
+ String get contactsPreferenceFailed =>
+ 'Không thể lưu tùy chọn nguồn danh bạ.';
+
+ @override
+ String get contactsSuggestionsDisabled => 'Đã tắt gợi ý liên hệ';
+
+ @override
+ String get contactsSyncFailed =>
+ 'Không thể đồng bộ danh bạ. Thử lại hoặc kết nối lại tài khoản nếu quyền đã hết hạn.';
+
+ @override
+ String get registrationRecoveryDescription =>
+ 'Đăng ký đã lưu hoặc liên kết thông tin xác thực không khả dụng hoặc bị từ chối. Hãy thay thế rõ ràng đăng ký của tài khoản này để khôi phục; dữ liệu hiện có được giữ lại.';
+
+ @override
+ String get revalidateAccount => 'Xác minh lại kết nối';
+
+ @override
+ String get openSharedPrimaryDescription =>
+ 'Mở lịch chính của chủ sở hữu được chỉ định. Các lịch chia sẻ khác được tìm qua đồng bộ tài khoản thông thường.';
+
+ @override
+ String get showInBusyMax => 'Hiện trong BusyMax';
+
+ @override
+ String get showInGoogleCalendarList => 'Hiện trong danh sách lịch Google';
+
+ @override
+ String get googleVisibilityHelp =>
+ 'Lựa chọn BusyMax là cục bộ. Chế độ hiển thị Google thay đổi danh sách trên Google; lịch bị ẩn không thể hiển thị trong BusyMax.';
+
+ @override
+ String get primaryCalendarVisibilityExplanation =>
+ 'Lịch chính Google luôn nằm trong danh sách.';
+
+ @override
+ String get accountsLoading => 'Đang tải tài khoản…';
+
+ @override
+ String get accountsLoadFailed =>
+ 'Không thể tải tài khoản. Hãy mở lại Tài khoản.';
+
+ @override
+ String get collectionsLoading => 'Đang tải bộ sưu tập…';
+
+ @override
+ String get collectionsLoadFailed =>
+ 'Không thể tải bộ sưu tập. Dữ liệu đệm được giữ; hãy thử đồng bộ lại.';
+
+ @override
+ String get collectionsNotDiscovered =>
+ 'Chưa hoàn tất phát hiện. Hãy làm mới hoặc kết nối lại tài khoản.';
+
+ @override
+ String get collectionsEmpty => 'Không tìm thấy bộ sưu tập.';
+
+ @override
+ String get accountReadyToSync => 'Sẵn sàng đồng bộ';
+
+ @override
+ String get accountSyncProblem => 'Đồng bộ cần được xử lý';
+
+ @override
+ String get accountServiceUnreachable => 'Không thể kết nối với dịch vụ';
+
+ @override
+ String get accountSyncDetails => 'Chi tiết đồng bộ';
+
+ @override
+ String get accountReconnectStatus => 'Cần kết nối lại';
+
+ @override
+ String get accountRegistrationStatus => 'Đăng ký ứng dụng cần được xử lý';
+
+ @override
+ String get collectionsUnavailable => 'Bộ sưu tập không khả dụng';
+
+ @override
+ String get accountContactSuggestions =>
+ 'Đề xuất khách từ danh bạ của tài khoản này';
+
+ @override
+ String get accountLoadingSignInSettings => 'Đang tải cài đặt đăng nhập…';
+
+ @override
+ String get contactsNoAddressBooks => 'Không có sổ địa chỉ nào khả dụng';
+
+ @override
+ String get contactsSourcesUnavailable => 'Không thể tải sổ địa chỉ';
+
+ @override
+ String get syncNoRecord => 'Chưa ghi nhận thời điểm đồng bộ thành công';
+
+ @override
+ String get cachedSyncError => 'Đồng bộ thất bại; dữ liệu đệm có thể đã cũ.';
+
+ @override
+ String get calendarRemotePending => 'Đang chờ đổi chế độ hiển thị Google';
+
+ @override
+ String get calendarRemoteFailed =>
+ 'Thay đổi khả năng hiển thị trên Google không thành công. Trạng thái Google được xác nhận gần nhất đang hiển thị. BusyMax vẫn áp dụng thay đổi đang chờ. Hãy thử lại hoặc hủy thay đổi.';
+
+ @override
+ String discardCalendarSettingsConfirmation(String calendar, String settings) {
+ return 'Hủy các thay đổi đang chờ của “$calendar”?\n\nCác cài đặt sau sẽ trở về giá trị trước đó:\n$settings\n\nLựa chọn lịch trong BusyMax của bạn được giữ nguyên. Không có thay đổi nào được gửi đến Google.';
+ }
+
+ @override
+ String get sharedCalendarUnavailable =>
+ 'Không thể truy cập; đang hiện dữ liệu đệm. Thử đồng bộ lại hoặc xóa khỏi danh sách lịch.';
+
+ @override
+ String get invalidCollectionColor =>
+ 'Nhập màu thập lục phân gồm sáu hoặc tám chữ số, bắt đầu bằng #.';
+
+ @override
+ String get invalidCollectionOrder => 'Nhập số nguyên.';
+
+ @override
+ String get invalidCollectionTimezone =>
+ 'Nhập tài liệu múi giờ lịch hợp lệ chứa VTIMEZONE và TZID.';
+
+ @override
+ String get invalidCollectionValue => 'Sửa giá trị này trước khi lưu.';
+
+ @override
+ String get accountAlreadyConnected =>
+ 'Tài khoản này đã kết nối. Hãy dùng thao tác kết nối lại của tài khoản.';
+
+ @override
+ String get nextcloudRemovalAuthorizationDescription =>
+ 'BusyMax cũng sẽ thử thu hồi mật khẩu ứng dụng Nextcloud của tài khoản này. Việc xóa cục bộ vẫn có thể thành công dù thu hồi thất bại.';
}
diff --git a/lib/l10n/generated/app_localizations_zh.dart b/lib/l10n/generated/app_localizations_zh.dart
index b93b8852..ad9f5ed7 100644
--- a/lib/l10n/generated/app_localizations_zh.dart
+++ b/lib/l10n/generated/app_localizations_zh.dart
@@ -13,6 +13,9 @@ import 'app_localizations.dart';
class AppLocalizationsZh extends AppLocalizations {
AppLocalizationsZh([String locale = 'zh']) : super(locale);
+ @override
+ String get contactsAccountsUnavailable => '无法加载联系人账户';
+
@override
String get launchAtLoginManagedExternally =>
'BusyMax 由桌面的启动设置启动。请在那里移除 BusyMax 的条目,然后完全退出并重新打开 BusyMax,以使用此开关。';
@@ -2905,7 +2908,7 @@ class AppLocalizationsZh extends AppLocalizations {
String get searchClearText => '清除文字';
@override
- String get openSharedCalendar => '打开共享日历';
+ String get openSharedCalendar => '打开共享主日历';
@override
String get manageCalendarSharing => '管理日历共享';
@@ -3454,12 +3457,155 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get addSeparateGoogleAccount => '添加独立的 Google 连接';
+
+ @override
+ String seriesPatternChangeConfirmation(String before, String after) {
+ return '更改重复规则可能会增加或移除日期。\n\n当前:$before\n建议:$after\n\n保存此规则吗?';
+ }
+
+ @override
+ String get connectedAccounts => '已连接的账户';
+
+ @override
+ String get contactsIndependentAccounts => '独立联系人账户';
+
+ @override
+ String get contactsNextcloudReuse => '联系人建议通过只读联系人集成重复使用此账户现有的凭据。';
+
+ @override
+ String get contactsRetrySync => '重试联系人同步';
+
+ @override
+ String get contactsPreferenceFailed => '无法保存联系人来源偏好。';
+
+ @override
+ String get contactsSuggestionsDisabled => '联系人建议已停用';
+
+ @override
+ String get contactsSyncFailed => '无法同步联系人。请重试,或在授权过期时重新连接账户。';
+
+ @override
+ String get registrationRecoveryDescription =>
+ '保存的应用注册或凭据绑定不可用或被拒绝。请明确替换此账户的注册以恢复连接;现有数据会保留。';
+
+ @override
+ String get revalidateAccount => '重新验证连接';
+
+ @override
+ String get openSharedPrimaryDescription => '打开指定所有者的主日历。其他共享日历通过普通账户同步发现。';
+
+ @override
+ String get showInBusyMax => '在 BusyMax 中显示';
+
+ @override
+ String get showInGoogleCalendarList => '在 Google 日历列表中显示';
+
+ @override
+ String get googleVisibilityHelp =>
+ 'BusyMax 的选择是本地设置。Google 可见性会更改 Google 上的日历列表;隐藏的日历无法在 BusyMax 中显示。';
+
+ @override
+ String get primaryCalendarVisibilityExplanation => 'Google 主日历会保留在日历列表中。';
+
+ @override
+ String get accountsLoading => '正在加载账户…';
+
+ @override
+ String get accountsLoadFailed => '无法加载账户。请重新打开“账户”。';
+
+ @override
+ String get collectionsLoading => '正在加载集合…';
+
+ @override
+ String get collectionsLoadFailed => '无法加载集合。缓存数据已保留;请重试同步。';
+
+ @override
+ String get collectionsNotDiscovered => '发现尚未完成。请刷新或重新连接此账户。';
+
+ @override
+ String get collectionsEmpty => '未找到集合。';
+
+ @override
+ String get accountReadyToSync => '已准备好同步';
+
+ @override
+ String get accountSyncProblem => '同步需要处理';
+
+ @override
+ String get accountServiceUnreachable => '无法连接到服务';
+
+ @override
+ String get accountSyncDetails => '同步详情';
+
+ @override
+ String get accountReconnectStatus => '需要重新连接';
+
+ @override
+ String get accountRegistrationStatus => '应用注册需要处理';
+
+ @override
+ String get collectionsUnavailable => '集合不可用';
+
+ @override
+ String get accountContactSuggestions => '从此账户的联系人中推荐访客';
+
+ @override
+ String get accountLoadingSignInSettings => '正在加载登录设置…';
+
+ @override
+ String get contactsNoAddressBooks => '没有可用的通讯录';
+
+ @override
+ String get contactsSourcesUnavailable => '无法加载通讯录';
+
+ @override
+ String get syncNoRecord => '没有成功同步时间记录';
+
+ @override
+ String get cachedSyncError => '同步失败;缓存数据可能已过时。';
+
+ @override
+ String get calendarRemotePending => 'Google 可见性更改待处理';
+
+ @override
+ String get calendarRemoteFailed =>
+ 'Google 显示设置更改失败。现显示 Google 上次确认的状态。BusyMax 仍遵循待处理的更改。请重试或放弃该更改。';
+
+ @override
+ String discardCalendarSettingsConfirmation(String calendar, String settings) {
+ return '要放弃“$calendar”的待处理更改吗?\n\n以下设置将恢复为之前的值:\n$settings\n\nBusyMax 中的日历选择将保留。不会向 Google 发送任何更改。';
+ }
+
+ @override
+ String get sharedCalendarUnavailable => '无法访问;正在显示缓存数据。请重试同步或从日历列表中移除。';
+
+ @override
+ String get invalidCollectionColor => '请输入以 # 开头的六位或八位十六进制颜色。';
+
+ @override
+ String get invalidCollectionOrder => '请输入整数。';
+
+ @override
+ String get invalidCollectionTimezone => '请输入包含 VTIMEZONE 和 TZID 的有效日历时区文档。';
+
+ @override
+ String get invalidCollectionValue => '保存前请修正此值。';
+
+ @override
+ String get accountAlreadyConnected => '此账户已连接。请使用该账户的“重新连接”操作。';
+
+ @override
+ String get nextcloudRemovalAuthorizationDescription =>
+ 'BusyMax 还会尝试撤销此账户的 Nextcloud 应用密码。即使撤销失败,本地移除也可以成功。';
}
/// The translations for Chinese, using the Han script (`zh_Hans`).
class AppLocalizationsZhHans extends AppLocalizationsZh {
AppLocalizationsZhHans() : super('zh_Hans');
+ @override
+ String get contactsAccountsUnavailable => '无法加载联系人账户';
+
@override
String get launchAtLoginManagedExternally =>
'BusyMax 由桌面的启动设置启动。请在那里移除 BusyMax 的条目,然后完全退出并重新打开 BusyMax,以使用此开关。';
@@ -6352,7 +6498,7 @@ class AppLocalizationsZhHans extends AppLocalizationsZh {
String get searchClearText => '清除文字';
@override
- String get openSharedCalendar => '打开共享日历';
+ String get openSharedCalendar => '打开共享主日历';
@override
String get manageCalendarSharing => '管理日历共享';
@@ -6901,12 +7047,155 @@ class AppLocalizationsZhHans extends AppLocalizationsZh {
@override
String get addSeparateGoogleAccount => '添加独立的 Google 连接';
+
+ @override
+ String seriesPatternChangeConfirmation(String before, String after) {
+ return '更改重复规则可能会增加或移除日期。\n\n当前:$before\n建议:$after\n\n保存此规则吗?';
+ }
+
+ @override
+ String get connectedAccounts => '已连接的账户';
+
+ @override
+ String get contactsIndependentAccounts => '独立联系人账户';
+
+ @override
+ String get contactsNextcloudReuse => '联系人建议通过只读联系人集成重复使用此账户现有的凭据。';
+
+ @override
+ String get contactsRetrySync => '重试联系人同步';
+
+ @override
+ String get contactsPreferenceFailed => '无法保存联系人来源偏好。';
+
+ @override
+ String get contactsSuggestionsDisabled => '联系人建议已停用';
+
+ @override
+ String get contactsSyncFailed => '无法同步联系人。请重试,或在授权过期时重新连接账户。';
+
+ @override
+ String get registrationRecoveryDescription =>
+ '保存的应用注册或凭据绑定不可用或被拒绝。请明确替换此账户的注册以恢复连接;现有数据会保留。';
+
+ @override
+ String get revalidateAccount => '重新验证连接';
+
+ @override
+ String get openSharedPrimaryDescription => '打开指定所有者的主日历。其他共享日历通过普通账户同步发现。';
+
+ @override
+ String get showInBusyMax => '在 BusyMax 中显示';
+
+ @override
+ String get showInGoogleCalendarList => '在 Google 日历列表中显示';
+
+ @override
+ String get googleVisibilityHelp =>
+ 'BusyMax 的选择是本地设置。Google 可见性会更改 Google 上的日历列表;隐藏的日历无法在 BusyMax 中显示。';
+
+ @override
+ String get primaryCalendarVisibilityExplanation => 'Google 主日历会保留在日历列表中。';
+
+ @override
+ String get accountsLoading => '正在加载账户…';
+
+ @override
+ String get accountsLoadFailed => '无法加载账户。请重新打开“账户”。';
+
+ @override
+ String get collectionsLoading => '正在加载集合…';
+
+ @override
+ String get collectionsLoadFailed => '无法加载集合。缓存数据已保留;请重试同步。';
+
+ @override
+ String get collectionsNotDiscovered => '发现尚未完成。请刷新或重新连接此账户。';
+
+ @override
+ String get collectionsEmpty => '未找到集合。';
+
+ @override
+ String get accountReadyToSync => '已准备好同步';
+
+ @override
+ String get accountSyncProblem => '同步需要处理';
+
+ @override
+ String get accountServiceUnreachable => '无法连接到服务';
+
+ @override
+ String get accountSyncDetails => '同步详情';
+
+ @override
+ String get accountReconnectStatus => '需要重新连接';
+
+ @override
+ String get accountRegistrationStatus => '应用注册需要处理';
+
+ @override
+ String get collectionsUnavailable => '集合不可用';
+
+ @override
+ String get accountContactSuggestions => '从此账户的联系人中推荐访客';
+
+ @override
+ String get accountLoadingSignInSettings => '正在加载登录设置…';
+
+ @override
+ String get contactsNoAddressBooks => '没有可用的通讯录';
+
+ @override
+ String get contactsSourcesUnavailable => '无法加载通讯录';
+
+ @override
+ String get syncNoRecord => '没有成功同步时间记录';
+
+ @override
+ String get cachedSyncError => '同步失败;缓存数据可能已过时。';
+
+ @override
+ String get calendarRemotePending => 'Google 可见性更改待处理';
+
+ @override
+ String get calendarRemoteFailed =>
+ 'Google 显示设置更改失败。现显示 Google 上次确认的状态。BusyMax 仍遵循待处理的更改。请重试或放弃该更改。';
+
+ @override
+ String discardCalendarSettingsConfirmation(String calendar, String settings) {
+ return '要放弃“$calendar”的待处理更改吗?\n\n以下设置将恢复为之前的值:\n$settings\n\nBusyMax 中的日历选择将保留。不会向 Google 发送任何更改。';
+ }
+
+ @override
+ String get sharedCalendarUnavailable => '无法访问;正在显示缓存数据。请重试同步或从日历列表中移除。';
+
+ @override
+ String get invalidCollectionColor => '请输入以 # 开头的六位或八位十六进制颜色。';
+
+ @override
+ String get invalidCollectionOrder => '请输入整数。';
+
+ @override
+ String get invalidCollectionTimezone => '请输入包含 VTIMEZONE 和 TZID 的有效日历时区文档。';
+
+ @override
+ String get invalidCollectionValue => '保存前请修正此值。';
+
+ @override
+ String get accountAlreadyConnected => '此账户已连接。请使用该账户的“重新连接”操作。';
+
+ @override
+ String get nextcloudRemovalAuthorizationDescription =>
+ 'BusyMax 还会尝试撤销此账户的 Nextcloud 应用密码。即使撤销失败,本地移除也可以成功。';
}
/// The translations for Chinese, using the Han script (`zh_Hant`).
class AppLocalizationsZhHant extends AppLocalizationsZh {
AppLocalizationsZhHant() : super('zh_Hant');
+ @override
+ String get contactsAccountsUnavailable => '無法載入聯絡人帳號';
+
@override
String get launchAtLoginManagedExternally =>
'BusyMax 由桌面的啟動設定啟動。請在那裡移除 BusyMax 的項目,然後完全結束並重新開啟 BusyMax,以使用此開關。';
@@ -9800,7 +10089,7 @@ class AppLocalizationsZhHant extends AppLocalizationsZh {
String get searchClearText => '清除文字';
@override
- String get openSharedCalendar => '開啟共用行事曆';
+ String get openSharedCalendar => '開啟共用主要行事曆';
@override
String get manageCalendarSharing => '管理行事曆共用';
@@ -10349,4 +10638,144 @@ class AppLocalizationsZhHant extends AppLocalizationsZh {
@override
String get addSeparateGoogleAccount => '新增獨立的 Google 連線';
+
+ @override
+ String seriesPatternChangeConfirmation(String before, String after) {
+ return '變更重複規則可能會增加或移除日期。\n\n目前:$before\n建議:$after\n\n要儲存此規則嗎?';
+ }
+
+ @override
+ String get connectedAccounts => '已連線的帳戶';
+
+ @override
+ String get contactsIndependentAccounts => '獨立聯絡人帳戶';
+
+ @override
+ String get contactsNextcloudReuse => '聯絡人建議透過唯讀聯絡人整合重複使用此帳戶現有的憑證。';
+
+ @override
+ String get contactsRetrySync => '重試聯絡人同步';
+
+ @override
+ String get contactsPreferenceFailed => '無法儲存聯絡人來源偏好。';
+
+ @override
+ String get contactsSuggestionsDisabled => '聯絡人建議已停用';
+
+ @override
+ String get contactsSyncFailed => '無法同步聯絡人。請重試,或在授權過期時重新連線帳戶。';
+
+ @override
+ String get registrationRecoveryDescription =>
+ '儲存的應用程式註冊或憑證繫結無法使用或遭拒。請明確取代此帳戶的註冊以恢復連線;現有資料會保留。';
+
+ @override
+ String get revalidateAccount => '重新驗證連線';
+
+ @override
+ String get openSharedPrimaryDescription => '開啟指定擁有者的主要行事曆。其他共用行事曆透過一般帳戶同步尋找。';
+
+ @override
+ String get showInBusyMax => '在 BusyMax 中顯示';
+
+ @override
+ String get showInGoogleCalendarList => '在 Google 行事曆清單中顯示';
+
+ @override
+ String get googleVisibilityHelp =>
+ 'BusyMax 的選擇是本機設定。Google 可見性會變更 Google 上的清單;隱藏的行事曆無法在 BusyMax 中顯示。';
+
+ @override
+ String get primaryCalendarVisibilityExplanation => 'Google 主要行事曆會保留在清單中。';
+
+ @override
+ String get accountsLoading => '正在載入帳戶…';
+
+ @override
+ String get accountsLoadFailed => '無法載入帳戶。請重新開啟「帳戶」。';
+
+ @override
+ String get collectionsLoading => '正在載入集合…';
+
+ @override
+ String get collectionsLoadFailed => '無法載入集合。快取資料已保留;請重試同步。';
+
+ @override
+ String get collectionsNotDiscovered => '探索尚未完成。請重新整理或重新連線此帳戶。';
+
+ @override
+ String get collectionsEmpty => '找不到集合。';
+
+ @override
+ String get accountReadyToSync => '已準備好同步';
+
+ @override
+ String get accountSyncProblem => '同步需要處理';
+
+ @override
+ String get accountServiceUnreachable => '無法連線至服務';
+
+ @override
+ String get accountSyncDetails => '同步詳細資料';
+
+ @override
+ String get accountReconnectStatus => '需要重新連線';
+
+ @override
+ String get accountRegistrationStatus => '應用程式註冊需要處理';
+
+ @override
+ String get collectionsUnavailable => '集合無法使用';
+
+ @override
+ String get accountContactSuggestions => '從此帳戶的聯絡人中建議訪客';
+
+ @override
+ String get accountLoadingSignInSettings => '正在載入登入設定…';
+
+ @override
+ String get contactsNoAddressBooks => '沒有可用的通訊錄';
+
+ @override
+ String get contactsSourcesUnavailable => '無法載入通訊錄';
+
+ @override
+ String get syncNoRecord => '沒有成功同步時間的記錄';
+
+ @override
+ String get cachedSyncError => '同步失敗;快取資料可能已過時。';
+
+ @override
+ String get calendarRemotePending => 'Google 可見性變更待處理';
+
+ @override
+ String get calendarRemoteFailed =>
+ 'Google 顯示設定變更失敗。目前顯示 Google 上次確認的狀態。BusyMax 仍遵循待處理的變更。請重試或捨棄該變更。';
+
+ @override
+ String discardCalendarSettingsConfirmation(String calendar, String settings) {
+ return '要捨棄「$calendar」的待處理變更嗎?\n\n以下設定將還原為先前的值:\n$settings\n\nBusyMax 中的行事曆選擇將會保留。不會向 Google 傳送任何變更。';
+ }
+
+ @override
+ String get sharedCalendarUnavailable => '無法存取;正在顯示快取資料。請重試同步或從行事曆清單移除。';
+
+ @override
+ String get invalidCollectionColor => '請輸入以 # 開頭的六位或八位十六進位色彩。';
+
+ @override
+ String get invalidCollectionOrder => '請輸入整數。';
+
+ @override
+ String get invalidCollectionTimezone => '請輸入包含 VTIMEZONE 和 TZID 的有效行事曆時區文件。';
+
+ @override
+ String get invalidCollectionValue => '儲存前請修正此值。';
+
+ @override
+ String get accountAlreadyConnected => '此帳戶已連線。請使用該帳戶的「重新連線」操作。';
+
+ @override
+ String get nextcloudRemovalAuthorizationDescription =>
+ 'BusyMax 也會嘗試撤銷此帳戶的 Nextcloud 應用程式密碼。即使撤銷失敗,本機移除仍可成功。';
}
diff --git a/lib/src/android/android_authorization.dart b/lib/src/android/android_authorization.dart
index e7eb4557..57c6b613 100644
--- a/lib/src/android/android_authorization.dart
+++ b/lib/src/android/android_authorization.dart
@@ -7,6 +7,7 @@ import '../db/app_database.dart' hide AuthorizationCommit;
import 'android_account_gate.dart';
import 'dart:async';
+import 'dart:convert';
import '../core/http/bounded_http.dart';
@@ -115,6 +116,9 @@ final class AndroidAuthorizationBroker
final http.Client _httpClient;
final SecretStore _secretStore;
final Map _lastAccessTokens = {};
+ final _microsoftRefreshRequests = {};
+ final _microsoftRefreshedRequests = >{};
+ var _microsoftRefreshSequence = 0;
@override
Future get activeAccountId => _secretStore.readActiveAccountId();
@@ -752,18 +756,21 @@ final class AndroidAuthorizationBroker
String id, {
AuthorizationCancellation? cancellation,
Future Function()? persistContacts,
+ bool forceAuthorization = false,
}) async {
final attempt = _attempts.begin(_nowUtc, cancellation);
NativeOAuthCredential? current;
const scope = googleContactsReadScope;
try {
current = await attempt.wait(nativeCredential(id, BusyProvider.google));
- if (await _persistExistingContacts(
- id,
- current!,
- attempt,
- persistContacts,
- )) {
+ if (!forceAuthorization &&
+ await _parentConnected(id) &&
+ await _persistExistingContacts(
+ id,
+ current!,
+ attempt,
+ persistContacts,
+ )) {
attempt.committed();
return;
}
@@ -790,11 +797,13 @@ final class AndroidAuthorizationBroker
String id, {
AuthorizationCancellation? cancellation,
Future Function()? persistContacts,
+ bool forceAuthorization = false,
}) => _optionalNativeConsent(
id,
_microsoftContactsReadNativeScope,
cancellation,
persistContacts: persistContacts,
+ forceAuthorization: forceAuthorization,
);
@override
@@ -803,16 +812,19 @@ final class AndroidAuthorizationBroker
String accountId, {
AuthorizationCancellation? cancellation,
Future Function()? persistContacts,
+ bool forceAuthorization = false,
}) => switch (provider) {
BusyProvider.google => authorizeGoogleContacts(
accountId,
cancellation: cancellation,
persistContacts: persistContacts,
+ forceAuthorization: forceAuthorization,
),
BusyProvider.microsoft => authorizeMicrosoftContacts(
accountId,
cancellation: cancellation,
persistContacts: persistContacts,
+ forceAuthorization: forceAuthorization,
),
_ => throw StateError('$provider does not use OAuth contacts access.'),
};
@@ -822,6 +834,7 @@ final class AndroidAuthorizationBroker
String scope,
AuthorizationCancellation? cancellation, {
Future Function()? persistContacts,
+ bool forceAuthorization = false,
}) async {
final attempt = _attempts.begin(_nowUtc, cancellation);
NativeOAuthCredential? current;
@@ -829,7 +842,9 @@ final class AndroidAuthorizationBroker
current = await attempt.wait(
nativeCredential(id, BusyProvider.microsoft),
);
- if (scope == _microsoftContactsReadNativeScope &&
+ if (!forceAuthorization &&
+ await _parentConnected(id) &&
+ scope == _microsoftContactsReadNativeScope &&
await _persistExistingContacts(
id,
current!,
@@ -857,6 +872,12 @@ final class AndroidAuthorizationBroker
}
}
+ Future _parentConnected(String id) async =>
+ (await (persistence!.database.select(
+ persistence!.database.accounts,
+ )..where((r) => r.id.equals(id))).getSingleOrNull())?.authState ==
+ 'signed_in';
+
Future _writeNativeGrants(String id, OAuthTokenSet tokens) async =>
await (persistence!.database.update(
persistence!.database.accounts,
@@ -992,7 +1013,7 @@ final class AndroidAuthorizationBroker
),
),
BusyProvider.microsoft => await _boundedSilent(
- _platform.authorizeMicrosoftSilently(
+ _microsoftSilentToken(
accountId: accountId,
scopes: _microsoftNativeScopes,
clientId: microsoft?.registration.clientId,
@@ -1054,7 +1075,7 @@ final class AndroidAuthorizationBroker
: await nativeCredential(accountId, BusyProvider.microsoft)
as MicrosoftAndroidCredential;
final native = await _boundedSilent(
- _platform.authorizeMicrosoftSilently(
+ _microsoftSilentToken(
accountId: accountId,
scopes: [
..._microsoftNativeScopes,
@@ -1154,7 +1175,7 @@ final class AndroidAuthorizationBroker
as MicrosoftAndroidCredential;
const requiredNativeScope = _microsoftContactsReadNativeScope;
final native = await _boundedSilent(
- _platform.authorizeMicrosoftSilently(
+ _microsoftSilentToken(
accountId: accountId,
scopes: [..._microsoftNativeScopes, requiredNativeScope],
clientId: current?.registration.clientId,
@@ -1191,7 +1212,7 @@ final class AndroidAuthorizationBroker
: await nativeCredential(accountId, BusyProvider.microsoft)
as MicrosoftAndroidCredential;
final native = await _boundedSilent(
- _platform.authorizeMicrosoftSilently(
+ _microsoftSilentToken(
accountId: accountId,
scopes: [..._microsoftNativeScopes, _microsoftSharedNativeScope],
clientId: current?.registration.clientId,
@@ -1223,7 +1244,7 @@ final class AndroidAuthorizationBroker
: await nativeCredential(accountId, BusyProvider.microsoft)
as MicrosoftAndroidCredential;
final native = await _boundedSilent(
- _platform.authorizeMicrosoftSilently(
+ _microsoftSilentToken(
accountId: accountId,
scopes: [..._microsoftNativeScopes, _microsoftCategoryNativeScope],
clientId: current?.registration.clientId,
@@ -1247,12 +1268,61 @@ final class AndroidAuthorizationBroker
}
}
+ Future _microsoftSilentToken({
+ required String accountId,
+ required List scopes,
+ String? clientId,
+ String? authorityTenant,
+ String? nativeAccountId,
+ String? authority,
+ String? claims,
+ }) async {
+ final refreshRequest = _microsoftRefreshRequests[accountId];
+ final requestKey = jsonEncode([
+ clientId,
+ authorityTenant,
+ nativeAccountId,
+ authority,
+ claims,
+ [...scopes]..sort(),
+ ]);
+ final forceRefresh =
+ refreshRequest != null &&
+ !(_microsoftRefreshedRequests[accountId]?.contains(requestKey) ??
+ false);
+ final token = await _platform.authorizeMicrosoftSilently(
+ accountId: accountId,
+ scopes: scopes,
+ clientId: clientId,
+ authorityTenant: authorityTenant,
+ nativeAccountId: nativeAccountId,
+ authority: authority,
+ claims: claims,
+ forceRefresh: forceRefresh,
+ );
+ if (forceRefresh &&
+ _microsoftRefreshRequests[accountId] == refreshRequest) {
+ (_microsoftRefreshedRequests[accountId] ??= {}).add(requestKey);
+ }
+ return token;
+ }
+
@override
Future recoverUnauthorized(
BusyProvider provider,
String accountId,
) async {
final rejected = _lastAccessTokens.remove(accountId);
+ if (provider == BusyProvider.microsoft) {
+ // Refresh the retry's exact native request, including its optional scopes
+ // and claims. Acquiring an ordinary token here could leave that request
+ // using a different rejected cache entry.
+ _microsoftRefreshRequests[accountId] = ++_microsoftRefreshSequence;
+ // Scope and claims variants can occupy different MSAL cache entries.
+ // An intervening acquisition must not acknowledge another variant.
+ _microsoftRefreshedRequests.remove(accountId);
+ return;
+ }
if (provider == BusyProvider.google && rejected != null) {
await _platform.clearRejectedToken(rejected);
}
@@ -1356,6 +1426,8 @@ final class AndroidAuthorizationBroker
final target = accountId ?? await activeAccountId;
if (target != null) {
_lastAccessTokens.remove(target);
+ _microsoftRefreshRequests.remove(target);
+ _microsoftRefreshedRequests.remove(target);
await _secretStore.deleteCredential(target);
if (target.startsWith('google:')) {
await _platform.removeAuthorization(
diff --git a/lib/src/android/android_notifications.dart b/lib/src/android/android_notifications.dart
index 1c9de7b1..6b19b98d 100644
--- a/lib/src/android/android_notifications.dart
+++ b/lib/src/android/android_notifications.dart
@@ -712,7 +712,7 @@ final class AndroidNotificationService
if (task.status == 'completed' || task.status == 'cancelled') {
return false;
}
- final due = taskDueAsLocal(task);
+ final due = taskDeadlineAsLocal(task);
return due != null &&
due.year == now.year &&
due.month == now.month &&
diff --git a/lib/src/android/presentation/android_contacts_settings.dart b/lib/src/android/presentation/android_contacts_settings.dart
index ccf7c214..5ef17449 100644
--- a/lib/src/android/presentation/android_contacts_settings.dart
+++ b/lib/src/android/presentation/android_contacts_settings.dart
@@ -1,4 +1,5 @@
import '../../l10n/l10n.dart';
+import '../../l10n/contacts_error_description.dart';
import 'dart:async';
@@ -32,11 +33,7 @@ class _AndroidContactsSettingsState
await action();
} on Object catch (error) {
if (mounted) {
- setState(
- () => _error = error is ContactsException
- ? error.code
- : context.l10n.operationFailed,
- );
+ setState(() => _error = localizedContactsError(context.l10n, error));
}
} finally {
if (mounted) setState(() => _pending = false);
@@ -119,7 +116,11 @@ class _AndroidContactsSettingsState
].contains(p.provider),
)) ...[
Text('${context.l10n.contactsTitle}: ${parent.displayLabel}'),
- Text(context.l10n.contactsReadPermissionDescription),
+ Text(
+ parent.provider == BusyProvider.nextcloud
+ ? context.l10n.contactsNextcloudReuse
+ : context.l10n.contactsReadPermissionDescription,
+ ),
_action(
context.l10n.contactsEnableSuggestions,
() => parent.provider == BusyProvider.nextcloud
@@ -138,12 +139,18 @@ class _AndroidContactsSettingsState
Text(
'${account.displayName} · ${context.l10n.contactsReadOnlyUse} · ${account.enabled ? context.l10n.contactsSuggestionsEnabled : context.l10n.off}',
),
- if (account.errorCode != null) Text(account.errorCode!),
+ if (account.errorCode != null)
+ Text(localizedContactsStatus(context.l10n, account.errorCode!)),
Wrap(
spacing: 8,
children: [
+ if (account.enabled)
+ _action(
+ context.l10n.contactsRetrySync,
+ () => controller.synchronizeAccount(account.id),
+ ),
_action(
- context.l10n.connectAccountAction,
+ context.l10n.reconnectAccount,
() => _reconnect(account),
),
_action(
diff --git a/lib/src/android/presentation/android_schedule_screen.dart b/lib/src/android/presentation/android_schedule_screen.dart
index 946d2ea7..a7af2308 100644
--- a/lib/src/android/presentation/android_schedule_screen.dart
+++ b/lib/src/android/presentation/android_schedule_screen.dart
@@ -1,3 +1,5 @@
+import '../../features/recurrence/presentation/recurrence_summary.dart';
+
import 'package:busymax/src/core/auth/authorization_attempt.dart';
import 'dart:async';
@@ -3527,6 +3529,56 @@ class _AndroidEventEditorState extends ConsumerState {
}
setState(() => _saving = true);
try {
+ final seriesTiming = await ref
+ .read(calendarRepositoryProvider)
+ .prepareSeriesTimingChange(draft);
+ if (!mounted) return;
+ if (seriesTiming?.requiresConfirmation == true) {
+ final confirmed = await showDialog(
+ context: context,
+ builder: (dialogContext) => AlertDialog(
+ title: Text(context.l10n.repeat),
+ content: Text(
+ context.l10n.seriesPatternChangeConfirmation(
+ recurrenceRuleSummary(
+ dialogContext,
+ seriesTiming!.previewRule(
+ following:
+ draft.recurringMutationScope ==
+ RecurringEventMutationScope.thisAndFuture,
+ original: true,
+ ),
+ timeZone: seriesTiming.timeZone,
+ ),
+ recurrenceRuleSummary(
+ dialogContext,
+ seriesTiming.previewRule(
+ following:
+ draft.recurringMutationScope ==
+ RecurringEventMutationScope.thisAndFuture,
+ ),
+ timeZone: seriesTiming.timeZone,
+ ),
+ ),
+ ),
+ actions: [
+ TextButton(
+ onPressed: () => Navigator.pop(dialogContext, false),
+ child: Text(context.l10n.cancel),
+ ),
+ FilledButton(
+ onPressed: () => Navigator.pop(dialogContext, true),
+ child: Text(context.l10n.save),
+ ),
+ ],
+ ),
+ );
+ if (confirmed != true) {
+ if (mounted) setState(() => _saving = false);
+ return;
+ }
+ draft = draft.copyWith(seriesTimingApproval: seriesTiming!.signature);
+ }
if ((_reminderMinutes ?? -2) >= 0 ||
androidGooglePopupReminderRows(_editedGoogleReminders).isNotEmpty ||
(_editedNextcloudReminders?.isNotEmpty ?? false)) {
diff --git a/lib/src/android/presentation/android_settings_screen.dart b/lib/src/android/presentation/android_settings_screen.dart
index 20d48f42..bc3e0de3 100644
--- a/lib/src/android/presentation/android_settings_screen.dart
+++ b/lib/src/android/presentation/android_settings_screen.dart
@@ -22,6 +22,7 @@ import '../../app/app_bootstrap.dart';
import '../../calendar_providers/calendar_colors.dart';
import '../../core/auth/oauth_models.dart';
import '../../dav/dav_errors.dart';
+import '../../dav/http/dav_http_transport.dart';
import '../../dav/mutation/dav_conflict_repository.dart';
import '../../dav/nextcloud/nextcloud_sharing_service.dart';
import '../../dav/nextcloud/nextcloud_trash_service.dart';
@@ -142,7 +143,7 @@ class _AndroidSettingsScreenState extends ConsumerState {
account.needsReconnect)
PopupMenuItem(
value: 'reconnect',
- child: Text(context.l10n.connectAccountAction),
+ child: Text(context.l10n.reconnectAccount),
),
PopupMenuItem(
value: 'remove',
@@ -706,7 +707,11 @@ class _AndroidSettingsScreenState extends ConsumerState {
final state = account.isSignedIn
? context.l10n.signedInAccount
: account.authState;
- final synced = account.lastSuccessfulSyncAtUtc;
+ final synced =
+ account.provider == BusyProvider.nextcloud ||
+ account.provider == BusyProvider.appleICloud
+ ? account.lastSuccessfulSyncAtUtc
+ : null;
return synced == null
? '${account.provider.displayName} · $state'
: '${account.provider.displayName} · $state\n${context.l10n.davLastSuccessfulSync(DateFormat.yMd().add_jm().format(synced.toLocal()))}';
@@ -723,6 +728,8 @@ class _AndroidSettingsScreenState extends ConsumerState {
}
final ownedCancellation = AuthorizationCancellation();
_authorizationCancellation = ownedCancellation;
+ final davCancellation = DavCancellationToken();
+ unawaited(ownedCancellation.signal.then((_) => davCancellation.cancel()));
try {
final localNetworkDenied = context.l10n.nextcloudOperationDenied;
String? email;
@@ -801,24 +808,35 @@ class _AndroidSettingsScreenState extends ConsumerState {
? (await service.connectAppleICloud(
email: email!,
appSpecificPassword: password!,
+ cancellationToken: davCancellation,
)).accountId
: (await service.replaceAppleAppSpecificPassword(
accountId: reconnecting.id,
appSpecificPassword: password!,
+ cancellationToken: davCancellation,
)).accountId;
case BusyProvider.nextcloud:
final service = ref.read(davAccountOnboardingServiceProvider);
accountId = reconnecting == null
- ? (await service.connectNextcloud(enteredServer: server!))
- .accountId
+ ? (await service.connectNextcloud(
+ enteredServer: server!,
+ cancellationToken: davCancellation,
+ )).accountId
: (await service.reconnectNextcloud(
accountId: reconnecting.id,
enteredServer: server!,
+ cancellationToken: davCancellation,
)).accountId;
case BusyProvider.webCal:
return;
}
- if (accountId != null) unawaited(_syncAccount(accountId));
+ if (accountId != null) {
+ ownedCancellation.committed();
+ if (mounted &&
+ identical(_authorizationCancellation, ownedCancellation)) {
+ unawaited(_syncAccount(accountId));
+ }
+ }
} on Object catch (error) {
if (error is OAuthException && error.code == 'OAuthSignInCancelled') {
return;
@@ -826,7 +844,10 @@ class _AndroidSettingsScreenState extends ConsumerState {
if (error is DavException && error.kind == DavErrorKind.cancelled) {
return;
}
- if (mounted) _message(localizedAuthorizationError(context.l10n, error));
+ if (mounted &&
+ identical(_authorizationCancellation, ownedCancellation)) {
+ _message(localizedAuthorizationError(context.l10n, error));
+ }
} finally {
if (mounted &&
identical(_authorizationCancellation, ownedCancellation)) {
@@ -861,6 +882,8 @@ class _AndroidSettingsScreenState extends ConsumerState {
mainAxisSize: MainAxisSize.min,
children: [
Text(context.l10n.removeAccountConfirmation),
+ if (account.provider == BusyProvider.nextcloud)
+ Text(context.l10n.nextcloudRemovalAuthorizationDescription),
if (account.provider == BusyProvider.google)
CheckboxListTile(
contentPadding: EdgeInsets.zero,
@@ -888,21 +911,30 @@ class _AndroidSettingsScreenState extends ConsumerState {
if (confirmed != true) return;
setState(() => _removing.add(account.id));
try {
- await ref.read(crossEngineAccountGateProvider).run(account.id, () async {
- if (account.provider == BusyProvider.appleICloud ||
- account.provider == BusyProvider.nextcloud) {
- await ref
- .read(davAccountOnboardingServiceProvider)
- .removeAccount(account.id);
- } else {
- await ref
- .read(authRepositoryProvider)
- .removeAccount(
- accountId: account.id,
- revokeAuthorization: revoke,
- );
+ if (account.provider == BusyProvider.appleICloud ||
+ account.provider == BusyProvider.nextcloud) {
+ // DAV onboarding owns this account gate, including contact preparation.
+ final result = await ref
+ .read(davAccountOnboardingServiceProvider)
+ .removeAccount(account.id);
+ if (mounted &&
+ result.remoteRevocationAttempted &&
+ !result.remoteRevocationSucceeded) {
+ _message(l10n.nextcloudAccountRemovedRevokeFailed);
}
- });
+ } else {
+ await ref
+ .read(crossEngineAccountGateProvider)
+ .run(
+ account.id,
+ () => ref
+ .read(authRepositoryProvider)
+ .removeAccount(
+ accountId: account.id,
+ revokeAuthorization: revoke,
+ ),
+ );
+ }
await ref.read(notificationReconcilerProvider).reconcile();
} on Object catch (error) {
_message(localizedAccountRemovalError(l10n, error));
diff --git a/lib/src/app/app_bootstrap.dart b/lib/src/app/app_bootstrap.dart
index 784e0db7..fbf6d418 100644
--- a/lib/src/app/app_bootstrap.dart
+++ b/lib/src/app/app_bootstrap.dart
@@ -1,5 +1,6 @@
import '../core/http/request_dispatch_exception.dart';
import '../features/sync/domain_sync_schedule.dart';
+import '../features/sync/account_sync_activity.dart';
import '../core/auth/authorization_persistence.dart';
import '../core/auth/registration_staging.dart';
import '../core/auth/oauth_registration.dart';
@@ -151,67 +152,91 @@ final baseHttpClientProvider = Provider((ref) {
});
final nextcloudLoginFlowV2Provider = Provider((ref) {
- return NextcloudLoginFlowV2(client: ref.watch(baseHttpClientProvider));
+ final flow = NextcloudLoginFlowV2(client: ref.watch(baseHttpClientProvider));
+ ref.onDispose(flow.cancel);
+ return flow;
});
-final davAccountOnboardingServiceProvider =
- Provider((ref) {
- final client = ref.watch(baseHttpClientProvider);
- return DavAccountOnboardingService(
- database: ref.watch(databaseProvider),
- secretStore: ref.watch(secretStoreProvider),
- accountsRepository: ref.watch(accountsRepositoryProvider),
- nextcloudLoginFlow: ref.watch(nextcloudLoginFlowV2Provider),
- discover:
- ({
- required accountId,
- required provider,
- required accountAuthority,
- required credential,
- cancellationToken,
- }) {
- final profile = davProviderProfile(
- provider,
- nextcloudServer: provider == BusyProvider.nextcloud
- ? accountAuthority
- : null,
- );
- final transport = DavHttpTransport(
- client: client,
- profile: profile,
- accountAuthority: accountAuthority,
- );
- return DavDiscoveryService(
- transport: transport,
- profile: profile,
- accountAuthority: accountAuthority,
- accountId: accountId,
- credential: credential,
- ).discover(
- correlationId: const Uuid().v4(),
- cancellationToken: cancellationToken,
- );
- },
- nextcloudCredentialRevoker:
- ({required accountId, required credential}) {
- final profile = davProviderProfile(
- BusyProvider.nextcloud,
- nextcloudServer: credential.canonicalServer,
- );
- return NextcloudAppPasswordRevoker(
- transport: DavHttpTransport(
- client: client,
- profile: profile,
- accountAuthority: credential.canonicalServer,
- ),
- ).revoke(
- accountId: accountId,
- credential: credential,
- correlationId: const Uuid().v4(),
- );
- },
+final Provider
+davAccountOnboardingServiceProvider = Provider((
+ ref,
+) {
+ final client = ref.watch(baseHttpClientProvider);
+ return DavAccountOnboardingService(
+ database: ref.watch(databaseProvider),
+ secretStore: ref.watch(secretStoreProvider),
+ authorizationPersistence: ref.watch(authorizationPersistenceProvider),
+ accountGate: ref.watch(crossEngineAccountGateProvider),
+ syncCoordinator: ref.watch(accountSyncCoordinatorProvider),
+ onAccountRemovalStarting: (id) => ref
+ .read(busyMaxContactsControllerProvider)
+ .beginLinkedAccountRemoval(id),
+ onAccountRemoving: (id) => ref
+ .read(busyMaxContactsControllerProvider)
+ .prepareLinkedAccountRemoval(id),
+ persistLinkedAccountRemoval: (id) => ref
+ .read(busyMaxContactsControllerProvider)
+ .persistLinkedAccountRemoval(id),
+ onAccountRemovalFailed: (id) => ref
+ .read(busyMaxContactsControllerProvider)
+ .restoreLinkedAccountAfterFailedRemoval(id),
+ onAccountRemoved: () => ref
+ .read(busyMaxContactsControllerProvider)
+ .linkedAccountRemovalCommitted(),
+ onAccountRemovalFinished: (id) => ref
+ .read(busyMaxContactsControllerProvider)
+ .finishLinkedAccountRemoval(id),
+ accountsRepository: ref.watch(accountsRepositoryProvider),
+ nextcloudLoginFlow: ref.watch(nextcloudLoginFlowV2Provider),
+ discover:
+ ({
+ required accountId,
+ required provider,
+ required accountAuthority,
+ required credential,
+ cancellationToken,
+ }) {
+ final profile = davProviderProfile(
+ provider,
+ nextcloudServer: provider == BusyProvider.nextcloud
+ ? accountAuthority
+ : null,
+ );
+ final transport = DavHttpTransport(
+ client: client,
+ profile: profile,
+ accountAuthority: accountAuthority,
+ );
+ return DavDiscoveryService(
+ transport: transport,
+ profile: profile,
+ accountAuthority: accountAuthority,
+ accountId: accountId,
+ credential: credential,
+ ).discover(
+ correlationId: const Uuid().v4(),
+ cancellationToken: cancellationToken,
+ );
+ },
+ nextcloudCredentialRevoker: ({required accountId, required credential}) {
+ final profile = davProviderProfile(
+ BusyProvider.nextcloud,
+ nextcloudServer: credential.canonicalServer,
);
- });
+ return NextcloudAppPasswordRevoker(
+ transport: DavHttpTransport(
+ client: client,
+ profile: profile,
+ accountAuthority: credential.canonicalServer,
+ ),
+ ).revoke(
+ accountId: accountId,
+ credential: credential,
+ correlationId: const Uuid().v4(),
+ );
+ },
+ );
+});
final retryingHttpClientProvider = Provider((ref) {
return RetryingHttpClient(inner: ref.watch(baseHttpClientProvider));
@@ -370,8 +395,8 @@ final busyMaxContactsControllerProvider = Provider((
deleteDavSecret: (accountId) => storage.delete(key: key(accountId)),
readLinkedDavCredential: (accountId) async {
final credential = await ref
- .read(secretStoreProvider)
- .readCredential(accountId);
+ .read(authorizationPersistenceProvider)
+ .readCurrentCredential(accountId);
return credential is NextcloudSecretRecord
? BusyMaxLinkedDavCredential(
server: credential.canonicalServer,
@@ -380,6 +405,28 @@ final busyMaxContactsControllerProvider = Provider((
)
: null;
},
+ reconnectLinkedDavAccount: (id, cancellation) async {
+ final account = await ref
+ .read(accountsRepositoryProvider)
+ .accountById(id);
+ if (account == null) {
+ throw const ContactsException(
+ ContactsFailure.authentication,
+ 'linked-parent-removed',
+ );
+ }
+ final token = DavCancellationToken();
+ if (cancellation.isCancelled) token.cancel();
+ unawaited(cancellation.signal.then((_) => token.cancel()));
+ await ref
+ .read(davAccountOnboardingServiceProvider)
+ .reconnectNextcloud(
+ accountId: id,
+ enteredServer: account.authority,
+ cancellationToken: token,
+ );
+ cancellation.committed();
+ },
launchBrowser: (uri) =>
launchUrl(uri, mode: LaunchMode.externalApplication),
);
@@ -1134,10 +1181,68 @@ final calendarMonthRetrieverProvider = Provider((ref) {
);
});
+final accountSyncActivityProvider = ChangeNotifierProvider(
+ (ref) => AccountSyncActivity(),
+);
+
+final accountDomainSyncTimesProvider =
+ StreamProvider.family