diff --git a/CHANGELOG.md b/CHANGELOG.md index c184cea94..c9af8be6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [16.7.3](https://github.com/johannesjo/super-productivity/compare/v16.7.2...v16.7.3) (2025-12-20) + +### Bug Fixes + +- es.json ([75e0e7e](https://github.com/johannesjo/super-productivity/commit/75e0e7e86c4bca3adb886e1d4bccc02c9f48568a)) + ## [16.7.2](https://github.com/johannesjo/super-productivity/compare/v16.6.1...v16.7.2) (2025-12-19) ### Bug Fixes diff --git a/android/app/build.gradle b/android/app/build.gradle index 2d921f01e..55f9f3078 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -20,8 +20,8 @@ android { minSdkVersion 24 targetSdkVersion 35 compileSdk 35 - versionCode 16_07_02_0000 - versionName "16.7.2" + versionCode 16_07_03_0000 + versionName "16.7.3" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" manifestPlaceholders = [ hostName : "app.super-productivity.com", diff --git a/android/app/src/main/java/com/superproductivity/superproductivity/service/FocusModeForegroundService.kt b/android/app/src/main/java/com/superproductivity/superproductivity/service/FocusModeForegroundService.kt index a5d490ad7..c49a393e7 100644 --- a/android/app/src/main/java/com/superproductivity/superproductivity/service/FocusModeForegroundService.kt +++ b/android/app/src/main/java/com/superproductivity/superproductivity/service/FocusModeForegroundService.kt @@ -86,10 +86,22 @@ class FocusModeForegroundService : Service() { } ACTION_UPDATE -> { + val wasPaused = isPaused + title = intent.getStringExtra(EXTRA_TITLE) ?: title remainingMs = intent.getLongExtra(EXTRA_REMAINING_MS, remainingMs) isPaused = intent.getBooleanExtra(EXTRA_IS_PAUSED, isPaused) + isBreak = intent.getBooleanExtra(EXTRA_IS_BREAK, isBreak) taskTitle = intent.getStringExtra(EXTRA_TASK_TITLE) ?: taskTitle lastUpdateTimestamp = System.currentTimeMillis() + + // Restart update runnable if resuming from paused state + if (wasPaused && !isPaused) { + handler.removeCallbacks(updateRunnable) + handler.post(updateRunnable) + } else if (!wasPaused && isPaused) { + handler.removeCallbacks(updateRunnable) + } + updateNotification() } diff --git a/android/app/src/main/java/com/superproductivity/superproductivity/service/TrackingForegroundService.kt b/android/app/src/main/java/com/superproductivity/superproductivity/service/TrackingForegroundService.kt index 22e52eb0b..b7775501e 100644 --- a/android/app/src/main/java/com/superproductivity/superproductivity/service/TrackingForegroundService.kt +++ b/android/app/src/main/java/com/superproductivity/superproductivity/service/TrackingForegroundService.kt @@ -15,6 +15,7 @@ class TrackingForegroundService : Service() { const val ACTION_START = "com.superproductivity.ACTION_START_TRACKING" const val ACTION_STOP = "com.superproductivity.ACTION_STOP_TRACKING" + const val ACTION_UPDATE = "com.superproductivity.ACTION_UPDATE_TRACKING" const val ACTION_PAUSE = "com.superproductivity.ACTION_PAUSE_TRACKING" const val ACTION_DONE = "com.superproductivity.ACTION_MARK_DONE" const val ACTION_GET_ELAPSED = "com.superproductivity.ACTION_GET_ELAPSED" @@ -79,6 +80,11 @@ class TrackingForegroundService : Service() { startTracking(taskId, title, timeSpentMs) } + ACTION_UPDATE -> { + val timeSpentMs = intent.getLongExtra(EXTRA_TIME_SPENT, accumulatedMs) + updateTimeSpent(timeSpentMs) + } + ACTION_STOP -> { stopTracking() } @@ -115,6 +121,21 @@ class TrackingForegroundService : Service() { handler.post(updateRunnable) } + private fun updateTimeSpent(timeSpentMs: Long) { + if (!isTracking) { + Log.d(TAG, "Ignoring updateTimeSpent: not tracking") + return + } + Log.d(TAG, "Updating time spent: timeSpentMs=$timeSpentMs (was accumulated=$accumulatedMs)") + + // Reset the timer with the new accumulated value + accumulatedMs = timeSpentMs + startTimestamp = System.currentTimeMillis() + + // Update notification immediately + updateNotification() + } + private fun stopTracking() { Log.d(TAG, "Stopping tracking, elapsed=${getElapsedMs()}ms") diff --git a/android/app/src/main/java/com/superproductivity/superproductivity/webview/JavaScriptInterface.kt b/android/app/src/main/java/com/superproductivity/superproductivity/webview/JavaScriptInterface.kt index 537e47a24..c8b1e4800 100644 --- a/android/app/src/main/java/com/superproductivity/superproductivity/webview/JavaScriptInterface.kt +++ b/android/app/src/main/java/com/superproductivity/superproductivity/webview/JavaScriptInterface.kt @@ -97,6 +97,16 @@ class JavaScriptInterface( activity.startService(intent) } + @Suppress("unused") + @JavascriptInterface + fun updateTrackingService(timeSpentMs: Long) { + val intent = Intent(activity, TrackingForegroundService::class.java).apply { + action = TrackingForegroundService.ACTION_UPDATE + putExtra(TrackingForegroundService.EXTRA_TIME_SPENT, timeSpentMs) + } + activity.startService(intent) + } + @Suppress("unused") @JavascriptInterface fun getTrackingElapsed(): String { @@ -143,11 +153,13 @@ class JavaScriptInterface( @Suppress("unused") @JavascriptInterface - fun updateFocusModeService(remainingMs: Long, isPaused: Boolean, taskTitle: String?) { + fun updateFocusModeService(title: String, remainingMs: Long, isPaused: Boolean, isBreak: Boolean, taskTitle: String?) { val intent = Intent(activity, FocusModeForegroundService::class.java).apply { action = FocusModeForegroundService.ACTION_UPDATE + putExtra(FocusModeForegroundService.EXTRA_TITLE, title) putExtra(FocusModeForegroundService.EXTRA_REMAINING_MS, remainingMs) putExtra(FocusModeForegroundService.EXTRA_IS_PAUSED, isPaused) + putExtra(FocusModeForegroundService.EXTRA_IS_BREAK, isBreak) putExtra(FocusModeForegroundService.EXTRA_TASK_TITLE, taskTitle) } activity.startService(intent) diff --git a/android/fastlane/metadata/android/en-US/changelogs/1607030000.txt b/android/fastlane/metadata/android/en-US/changelogs/1607030000.txt new file mode 100644 index 000000000..57fdb9c75 --- /dev/null +++ b/android/fastlane/metadata/android/en-US/changelogs/1607030000.txt @@ -0,0 +1,4 @@ + +### Bug Fixes + +* es.json \ No newline at end of file diff --git a/electron/electronAPI.d.ts b/electron/electronAPI.d.ts index 45767b94d..84877d1c3 100644 --- a/electron/electronAPI.d.ts +++ b/electron/electronAPI.d.ts @@ -85,6 +85,8 @@ export interface ElectronAPI { isSnap(): boolean; + isFlatpak(): boolean; + // SEND // ---- reloadMainWin(): void; diff --git a/electron/preload.ts b/electron/preload.ts index 45404b629..0f8faeae2 100644 --- a/electron/preload.ts +++ b/electron/preload.ts @@ -61,6 +61,7 @@ const ea: ElectronAPI = { isLinux: () => process.platform === 'linux', isMacOS: () => process.platform === 'darwin', isSnap: () => process && process.env && !!process.env.SNAP, + isFlatpak: () => process && process.env && !!process.env.FLATPAK_ID, // SEND // ---- diff --git a/package-lock.json b/package-lock.json index bb913317c..0aa332dac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "superProductivity", - "version": "16.7.2", + "version": "16.7.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "superProductivity", - "version": "16.7.2", + "version": "16.7.3", "license": "MIT", "workspaces": [ "packages/*" diff --git a/package.json b/package.json index 5d59535f1..7228f5b5b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "superProductivity", - "version": "16.7.2", + "version": "16.7.3", "description": "ToDo list and Time Tracking", "keywords": [ "ToDo", diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 3864123ff..116c760cc 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -160,10 +160,17 @@ export class AppComponent implements OnDestroy, AfterViewInit { ), ); - isShowFocusOverlay = toSignal(this._store.select(selectIsOverlayShown), { + private _isOverlayShownFromStore = toSignal(this._store.select(selectIsOverlayShown), { initialValue: false, }); + // Only show focus overlay if both the store says to show it AND the feature is enabled + isShowFocusOverlay = computed( + () => + this._isOverlayShownFromStore() && + this._globalConfigService.cfg()?.appFeatures.isFocusModeEnabled, + ); + private readonly _activeWorkContextId = toSignal( this.workContextService.activeWorkContextId$, { initialValue: null }, diff --git a/src/app/features/android/android-interface.ts b/src/app/features/android/android-interface.ts index bd71bd41b..2d8fbe15e 100644 --- a/src/app/features/android/android-interface.ts +++ b/src/app/features/android/android-interface.ts @@ -41,6 +41,7 @@ export interface AndroidInterface { // Foreground service methods for background time tracking startTrackingService?(taskId: string, taskTitle: string, timeSpentMs: number): void; stopTrackingService?(): void; + updateTrackingService?(timeSpentMs: number): void; getTrackingElapsed?(): string; // Foreground service methods for focus mode timer @@ -54,8 +55,10 @@ export interface AndroidInterface { ): void; stopFocusModeService?(): void; updateFocusModeService?( + title: string, remainingMs: number, isPaused: boolean, + isBreak: boolean, taskTitle: string | null, ): void; diff --git a/src/app/features/android/store/android-focus-mode.effects.ts b/src/app/features/android/store/android-focus-mode.effects.ts index 23b151ae1..43c96c6d2 100644 --- a/src/app/features/android/store/android-focus-mode.effects.ts +++ b/src/app/features/android/store/android-focus-mode.effects.ts @@ -8,6 +8,7 @@ import { selectIsBreakActive, selectIsLongBreak, selectMode, + selectPausedTaskId, selectTimeRemaining, selectTimer, } from '../../focus-mode/store/focus-mode.selectors'; @@ -87,12 +88,16 @@ export class AndroidFocusModeEffects { } else if (this._hasStateChanged(prev?.timer, timer, taskTitle, curr)) { // Only update if something significant changed DroidLog.log('AndroidFocusModeEffects: Updating focus mode service', { + title, remaining: remainingMs, isPaused: !timer.isRunning, + isBreak: isBreakActive, }); androidInterface.updateFocusModeService?.( + title, remainingMs, !timer.isRunning, + isBreakActive, taskTitle, ); } @@ -133,7 +138,8 @@ export class AndroidFocusModeEffects { createEffect(() => androidInterface.onFocusSkip$.pipe( tap(() => DroidLog.log('AndroidFocusModeEffects: Skip action received')), - map(() => focusModeActions.skipBreak()), + withLatestFrom(this._store.select(selectPausedTaskId)), + map(([_, pausedTaskId]) => focusModeActions.skipBreak({ pausedTaskId })), ), ); diff --git a/src/app/features/android/store/android-foreground-tracking.effects.spec.ts b/src/app/features/android/store/android-foreground-tracking.effects.spec.ts new file mode 100644 index 000000000..314cad3b4 --- /dev/null +++ b/src/app/features/android/store/android-foreground-tracking.effects.spec.ts @@ -0,0 +1,159 @@ +import { TestBed, fakeAsync } from '@angular/core/testing'; +import { provideMockStore, MockStore } from '@ngrx/store/testing'; +import { BehaviorSubject } from 'rxjs'; +import { TaskService } from '../../tasks/task.service'; +import { DateService } from '../../../core/date/date.service'; +import { Task } from '../../tasks/task.model'; + +// We need to test the effect logic by reimplementing it in tests since +// the actual effects are conditionally created based on IS_ANDROID_WEB_VIEW + +describe('AndroidForegroundTrackingEffects - syncTimeSpentChanges logic', () => { + let store: MockStore; + let currentTask$: BehaviorSubject; + let updateTrackingServiceSpy: jasmine.Spy; + + beforeEach(() => { + currentTask$ = new BehaviorSubject(null); + updateTrackingServiceSpy = jasmine.createSpy('updateTrackingService'); + + TestBed.configureTestingModule({ + providers: [ + provideMockStore(), + { provide: TaskService, useValue: { getByIdOnce$: () => currentTask$ } }, + { provide: DateService, useValue: { todayStr: () => '2024-01-01' } }, + ], + }); + + store = TestBed.inject(MockStore); + }); + + afterEach(() => { + store.resetSelectors(); + }); + + /** + * Test the core logic: when timeSpent changes for the same task while tracking, + * the updateTrackingService should be called with the new value. + */ + describe('timeSpent change detection logic', () => { + it('should call updateTrackingService when timeSpent changes for the same task', fakeAsync(() => { + // Simulate the effect logic + const prevState = { taskId: 'task-1', timeSpent: 60000, isFocusModeActive: false }; + const currState = { taskId: 'task-1', timeSpent: 0, isFocusModeActive: false }; + + const shouldUpdate = + prevState.taskId === currState.taskId && + currState.taskId !== null && + !currState.isFocusModeActive && + prevState.timeSpent !== currState.timeSpent; + + expect(shouldUpdate).toBeTrue(); + + // In real code, this triggers: androidInterface.updateTrackingService?.(curr.timeSpent); + if (shouldUpdate) { + updateTrackingServiceSpy(currState.timeSpent); + } + + expect(updateTrackingServiceSpy).toHaveBeenCalledWith(0); + })); + + it('should NOT call updateTrackingService when switching to a different task', fakeAsync(() => { + const prevState = { taskId: 'task-1', timeSpent: 60000, isFocusModeActive: false }; + const currState = { taskId: 'task-2', timeSpent: 30000, isFocusModeActive: false }; + + const shouldUpdate = + prevState.taskId === currState.taskId && + currState.taskId !== null && + !currState.isFocusModeActive && + prevState.timeSpent !== currState.timeSpent; + + expect(shouldUpdate).toBeFalse(); + })); + + it('should NOT call updateTrackingService when focus mode is active', fakeAsync(() => { + const prevState = { taskId: 'task-1', timeSpent: 60000, isFocusModeActive: false }; + const currState = { taskId: 'task-1', timeSpent: 0, isFocusModeActive: true }; + + const shouldUpdate = + prevState.taskId === currState.taskId && + currState.taskId !== null && + !currState.isFocusModeActive && + prevState.timeSpent !== currState.timeSpent; + + expect(shouldUpdate).toBeFalse(); + })); + + it('should NOT call updateTrackingService when no task is being tracked', fakeAsync(() => { + const prevState = { taskId: null, timeSpent: 0, isFocusModeActive: false }; + const currState = { taskId: null, timeSpent: 0, isFocusModeActive: false }; + + const shouldUpdate = + prevState.taskId === currState.taskId && + currState.taskId !== null && + !currState.isFocusModeActive && + prevState.timeSpent !== currState.timeSpent; + + expect(shouldUpdate).toBeFalse(); + })); + + it('should NOT call updateTrackingService when timeSpent did not change', fakeAsync(() => { + const prevState = { taskId: 'task-1', timeSpent: 60000, isFocusModeActive: false }; + const currState = { taskId: 'task-1', timeSpent: 60000, isFocusModeActive: false }; + + const shouldUpdate = + prevState.taskId === currState.taskId && + currState.taskId !== null && + !currState.isFocusModeActive && + prevState.timeSpent !== currState.timeSpent; + + expect(shouldUpdate).toBeFalse(); + })); + + it('should call updateTrackingService when timeSpent is increased', fakeAsync(() => { + const prevState = { taskId: 'task-1', timeSpent: 60000, isFocusModeActive: false }; + const currState = { taskId: 'task-1', timeSpent: 120000, isFocusModeActive: false }; + + const shouldUpdate = + prevState.taskId === currState.taskId && + currState.taskId !== null && + !currState.isFocusModeActive && + prevState.timeSpent !== currState.timeSpent; + + expect(shouldUpdate).toBeTrue(); + + if (shouldUpdate) { + updateTrackingServiceSpy(currState.timeSpent); + } + + expect(updateTrackingServiceSpy).toHaveBeenCalledWith(120000); + })); + }); + + describe('distinctUntilChanged behavior', () => { + it('should detect changes when only timeSpent differs', () => { + const stateA = { taskId: 'task-1', timeSpent: 60000, isFocusModeActive: false }; + const stateB = { taskId: 'task-1', timeSpent: 0, isFocusModeActive: false }; + + // The distinctUntilChanged comparator + const isEqual = + stateA.taskId === stateB.taskId && + stateA.timeSpent === stateB.timeSpent && + stateA.isFocusModeActive === stateB.isFocusModeActive; + + expect(isEqual).toBeFalse(); // Should NOT be equal, so effect should fire + }); + + it('should NOT detect changes when state is identical', () => { + const stateA = { taskId: 'task-1', timeSpent: 60000, isFocusModeActive: false }; + const stateB = { taskId: 'task-1', timeSpent: 60000, isFocusModeActive: false }; + + const isEqual = + stateA.taskId === stateB.taskId && + stateA.timeSpent === stateB.timeSpent && + stateA.isFocusModeActive === stateB.isFocusModeActive; + + expect(isEqual).toBeTrue(); // Should be equal, so effect should NOT fire + }); + }); +}); diff --git a/src/app/features/android/store/android-foreground-tracking.effects.ts b/src/app/features/android/store/android-foreground-tracking.effects.ts index 5c647020f..cc45c6c82 100644 --- a/src/app/features/android/store/android-foreground-tracking.effects.ts +++ b/src/app/features/android/store/android-foreground-tracking.effects.ts @@ -110,6 +110,55 @@ export class AndroidForegroundTrackingEffects { { dispatch: false }, ); + /** + * Update the native service when timeSpent changes for the current task. + * This handles the case where the user manually edits the time spent. + */ + syncTimeSpentChanges$ = + IS_ANDROID_WEB_VIEW && + createEffect( + () => + combineLatest([ + this._store.select(selectCurrentTask), + this._store.select(selectTimer), + ]).pipe( + map(([currentTask, timer]) => ({ + taskId: currentTask?.id || null, + timeSpent: currentTask?.timeSpent || 0, + isFocusModeActive: timer.purpose !== null, + })), + // Only react when timeSpent changes for the same task + distinctUntilChanged( + (a, b) => + a.taskId === b.taskId && + a.timeSpent === b.timeSpent && + a.isFocusModeActive === b.isFocusModeActive, + ), + pairwise(), + filter(([prev, curr]) => { + // Only update if: + // 1. Same task (not switching tasks - that's handled by syncTrackingToService$) + // 2. Task exists + // 3. Focus mode is not active (notification is hidden during focus mode) + // 4. timeSpent actually changed + return ( + prev.taskId === curr.taskId && + curr.taskId !== null && + !curr.isFocusModeActive && + prev.timeSpent !== curr.timeSpent + ); + }), + tap(([, curr]) => { + DroidLog.log('Time spent changed for current task, updating service', { + taskId: curr.taskId, + timeSpent: curr.timeSpent, + }); + androidInterface.updateTrackingService?.(curr.timeSpent); + }), + ), + { dispatch: false }, + ); + /** * Handle pause action from the notification. */ diff --git a/src/app/features/android/store/android.effects.ts b/src/app/features/android/store/android.effects.ts index 09bc043e1..86c35a26b 100644 --- a/src/app/features/android/store/android.effects.ts +++ b/src/app/features/android/store/android.effects.ts @@ -27,6 +27,8 @@ export class AndroidEffects { // Single-shot guard so we don't spam the user with duplicate warnings. private _hasShownNotificationWarning = false; private _hasCheckedExactAlarm = false; + // Track scheduled reminder IDs to cancel removed ones + private _scheduledReminderIds = new Set(); askPermissionsIfNotGiven$ = IS_ANDROID_WEB_VIEW && @@ -65,8 +67,24 @@ export class AndroidEffects { switchMap(() => this._store.select(selectAllTasksWithReminder)), tap(async (tasksWithReminders) => { try { + const currentReminderIds = new Set( + (tasksWithReminders || []).map((t) => t.id), + ); + + // Cancel reminders that are no longer in the list + for (const previousId of this._scheduledReminderIds) { + if (!currentReminderIds.has(previousId)) { + const notificationId = generateNotificationId(previousId); + DroidLog.log('AndroidEffects: cancelling removed reminder', { + relatedId: previousId, + notificationId, + }); + androidInterface.cancelNativeReminder?.(notificationId); + } + } + if (!tasksWithReminders || tasksWithReminders.length === 0) { - // Nothing to schedule yet, so avoid triggering the runtime permission dialog prematurely. + this._scheduledReminderIds.clear(); return; } DroidLog.log('AndroidEffects: scheduling reminders natively', { @@ -102,6 +120,9 @@ export class AndroidEffects { ); } + // Update tracked IDs + this._scheduledReminderIds = currentReminderIds; + DroidLog.log('AndroidEffects: scheduled native reminders', { reminderCount: tasksWithReminders.length, }); diff --git a/src/app/features/config/store/global-config.reducer.ts b/src/app/features/config/store/global-config.reducer.ts index f206da9be..41d88f18d 100644 --- a/src/app/features/config/store/global-config.reducer.ts +++ b/src/app/features/config/store/global-config.reducer.ts @@ -77,6 +77,10 @@ export const selectReminderConfig = createSelector( selectConfigFeatureState, (cfg): ReminderConfig => cfg.reminder, ); +export const selectIsFocusModeEnabled = createSelector( + selectConfigFeatureState, + (cfg): boolean => cfg.appFeatures.isFocusModeEnabled, +); export const initialGlobalConfigState: GlobalConfigState = { ...DEFAULT_GLOBAL_CONFIG, diff --git a/src/app/features/focus-mode/focus-mode-break/focus-mode-break.component.spec.ts b/src/app/features/focus-mode/focus-mode-break/focus-mode-break.component.spec.ts index c36ce0022..f6ff779b7 100644 --- a/src/app/features/focus-mode/focus-mode-break/focus-mode-break.component.spec.ts +++ b/src/app/features/focus-mode/focus-mode-break/focus-mode-break.component.spec.ts @@ -10,6 +10,7 @@ import { Signal, } from '@angular/core'; import { T } from '../../../t.const'; +import { of } from 'rxjs'; describe('FocusModeBreakComponent', () => { let component: FocusModeBreakComponent; @@ -20,9 +21,11 @@ describe('FocusModeBreakComponent', () => { isBreakLong: Signal; }; let environmentInjector: EnvironmentInjector; + const mockPausedTaskId = 'test-task-id'; beforeEach(() => { - mockStore = jasmine.createSpyObj('Store', ['dispatch']); + mockStore = jasmine.createSpyObj('Store', ['dispatch', 'select']); + mockStore.select.and.returnValue(of(mockPausedTaskId)); mockFocusModeService = { timeRemaining: signal(300000), @@ -78,18 +81,22 @@ describe('FocusModeBreakComponent', () => { }); describe('skipBreak', () => { - it('should dispatch skipBreak action', () => { + it('should dispatch skipBreak action with pausedTaskId', () => { component.skipBreak(); - expect(mockStore.dispatch).toHaveBeenCalledWith(skipBreak()); + expect(mockStore.dispatch).toHaveBeenCalledWith( + skipBreak({ pausedTaskId: mockPausedTaskId }), + ); }); }); describe('completeBreak', () => { - it('should dispatch completeBreak action', () => { + it('should dispatch completeBreak action with pausedTaskId', () => { component.completeBreak(); - expect(mockStore.dispatch).toHaveBeenCalledWith(completeBreak()); + expect(mockStore.dispatch).toHaveBeenCalledWith( + completeBreak({ pausedTaskId: mockPausedTaskId }), + ); }); }); }); diff --git a/src/app/features/focus-mode/focus-mode-break/focus-mode-break.component.ts b/src/app/features/focus-mode/focus-mode-break/focus-mode-break.component.ts index e3655dbd7..c3a110310 100644 --- a/src/app/features/focus-mode/focus-mode-break/focus-mode-break.component.ts +++ b/src/app/features/focus-mode/focus-mode-break/focus-mode-break.component.ts @@ -5,10 +5,12 @@ import { FocusModeService } from '../focus-mode.service'; import { MsToClockStringPipe } from '../../../ui/duration/ms-to-clock-string.pipe'; import { Store } from '@ngrx/store'; import { completeBreak, skipBreak } from '../store/focus-mode.actions'; +import { selectPausedTaskId } from '../store/focus-mode.selectors'; import { MatIcon } from '@angular/material/icon'; import { T } from '../../../t.const'; import { TranslatePipe } from '@ngx-translate/core'; import { TaskTrackingInfoComponent } from '../task-tracking-info/task-tracking-info.component'; +import { toSignal } from '@angular/core/rxjs-interop'; @Component({ selector: 'focus-mode-break', @@ -30,6 +32,9 @@ export class FocusModeBreakComponent { private readonly _store = inject(Store); T: typeof T = T; + // Get pausedTaskId before break ends (passed in action to avoid race condition) + private readonly _pausedTaskId = toSignal(this._store.select(selectPausedTaskId)); + readonly remainingTime = computed(() => { return this.focusModeService.timeRemaining() || 0; }); @@ -45,10 +50,10 @@ export class FocusModeBreakComponent { ); skipBreak(): void { - this._store.dispatch(skipBreak()); + this._store.dispatch(skipBreak({ pausedTaskId: this._pausedTaskId() })); } completeBreak(): void { - this._store.dispatch(completeBreak()); + this._store.dispatch(completeBreak({ pausedTaskId: this._pausedTaskId() })); } } diff --git a/src/app/features/focus-mode/store/focus-mode.actions.ts b/src/app/features/focus-mode/store/focus-mode.actions.ts index aba65e768..d4b69ca5f 100644 --- a/src/app/features/focus-mode/store/focus-mode.actions.ts +++ b/src/app/features/focus-mode/store/focus-mode.actions.ts @@ -42,8 +42,14 @@ export const startBreak = createAction( '[FocusMode] Start Break', props<{ duration?: number; isLongBreak?: boolean; pausedTaskId?: string | null }>(), ); -export const skipBreak = createAction('[FocusMode] Skip Break'); -export const completeBreak = createAction('[FocusMode] Complete Break'); +export const skipBreak = createAction( + '[FocusMode] Skip Break', + props<{ pausedTaskId?: string | null }>(), +); +export const completeBreak = createAction( + '[FocusMode] Complete Break', + props<{ pausedTaskId?: string | null }>(), +); export const incrementCycle = createAction('[FocusMode] Next Cycle'); export const resetCycles = createAction('[FocusMode] Reset Cycles'); diff --git a/src/app/features/focus-mode/store/focus-mode.effects.spec.ts b/src/app/features/focus-mode/store/focus-mode.effects.spec.ts index 370e657a6..b0cbd98e9 100644 --- a/src/app/features/focus-mode/store/focus-mode.effects.spec.ts +++ b/src/app/features/focus-mode/store/focus-mode.effects.spec.ts @@ -12,11 +12,12 @@ import { FocusModeStorageService } from '../focus-mode-storage.service'; import * as actions from './focus-mode.actions'; import * as selectors from './focus-mode.selectors'; import { FocusModeMode, FocusScreen, TimerState } from '../focus-mode.model'; -import { unsetCurrentTask } from '../../tasks/store/task.actions'; +import { unsetCurrentTask, setCurrentTask } from '../../tasks/store/task.actions'; import { openIdleDialog } from '../../idle/store/idle.actions'; import { selectTaskById } from '../../tasks/store/task.selectors'; import { selectFocusModeConfig, + selectIsFocusModeEnabled, selectPomodoroConfig, } from '../../config/store/global-config.reducer'; import { updateGlobalConfigSection } from '../../config/store/global-config.actions'; @@ -90,6 +91,7 @@ describe('FocusModeEffects', () => { value: { isSyncSessionWithTracking: false }, }, { selector: selectPomodoroConfig, value: { duration: 25 * 60 * 1000 } }, + { selector: selectIsFocusModeEnabled, value: true }, ], }), { provide: FocusModeStrategyFactory, useValue: strategyFactoryMock }, @@ -422,7 +424,7 @@ describe('FocusModeEffects', () => { describe('breakComplete$', () => { it('should dispatch startFocusSession when strategy.shouldAutoStartNextSession is true', (done) => { - actions$ = of(actions.completeBreak()); + actions$ = of(actions.completeBreak({ pausedTaskId: null })); store.overrideSelector(selectors.selectMode, FocusModeMode.Pomodoro); store.refreshState(); @@ -433,7 +435,7 @@ describe('FocusModeEffects', () => { }); it('should NOT dispatch startFocusSession when shouldAutoStartNextSession is false', (done) => { - actions$ = of(actions.completeBreak()); + actions$ = of(actions.completeBreak({ pausedTaskId: null })); store.overrideSelector(selectors.selectMode, FocusModeMode.Countdown); store.refreshState(); @@ -453,11 +455,30 @@ describe('FocusModeEffects', () => { }, }); }); + + it('should dispatch setCurrentTask when pausedTaskId is provided', (done) => { + const pausedTaskId = 'test-paused-task-id'; + actions$ = of(actions.completeBreak({ pausedTaskId })); + store.overrideSelector(selectors.selectMode, FocusModeMode.Countdown); + store.refreshState(); + + strategyFactoryMock.getStrategy.and.returnValue({ + initialSessionDuration: 25 * 60 * 1000, + shouldStartBreakAfterSession: false, + shouldAutoStartNextSession: false, + getBreakDuration: () => null, + }); + + effects.breakComplete$.pipe(take(1)).subscribe((action) => { + expect(action).toEqual(setCurrentTask({ id: pausedTaskId })); + done(); + }); + }); }); describe('skipBreak$', () => { it('should dispatch startFocusSession when strategy.shouldAutoStartNextSession is true', (done) => { - actions$ = of(actions.skipBreak()); + actions$ = of(actions.skipBreak({ pausedTaskId: null })); store.overrideSelector(selectors.selectMode, FocusModeMode.Pomodoro); store.refreshState(); @@ -468,7 +489,7 @@ describe('FocusModeEffects', () => { }); it('should NOT dispatch startFocusSession when shouldAutoStartNextSession is false', (done) => { - actions$ = of(actions.skipBreak()); + actions$ = of(actions.skipBreak({ pausedTaskId: null })); store.overrideSelector(selectors.selectMode, FocusModeMode.Countdown); store.refreshState(); @@ -488,6 +509,25 @@ describe('FocusModeEffects', () => { }, }); }); + + it('should dispatch setCurrentTask when pausedTaskId is provided', (done) => { + const pausedTaskId = 'test-paused-task-id'; + actions$ = of(actions.skipBreak({ pausedTaskId })); + store.overrideSelector(selectors.selectMode, FocusModeMode.Countdown); + store.refreshState(); + + strategyFactoryMock.getStrategy.and.returnValue({ + initialSessionDuration: 25 * 60 * 1000, + shouldStartBreakAfterSession: false, + shouldAutoStartNextSession: false, + getBreakDuration: () => null, + }); + + effects.skipBreak$.pipe(take(1)).subscribe((action) => { + expect(action).toEqual(setCurrentTask({ id: pausedTaskId })); + done(); + }); + }); }); describe('cancelSession$', () => { @@ -613,6 +653,24 @@ describe('FocusModeEffects', () => { done(); }, 50); }); + + it('should NOT dispatch showFocusOverlay when isFocusModeEnabled is false', (done) => { + store.overrideSelector(selectFocusModeConfig, { + isSyncSessionWithTracking: true, + isSkipPreparation: false, + }); + store.overrideSelector(selectIsFocusModeEnabled, false); + store.refreshState(); + + effects = TestBed.inject(FocusModeEffects); + + currentTaskId$.next('task-123'); + + setTimeout(() => { + // If we get here without the effect emitting, test passes + done(); + }, 50); + }); }); describe('syncTrackingStartToSession$', () => { @@ -741,6 +799,27 @@ describe('FocusModeEffects', () => { done(); }, 50); }); + + it('should NOT dispatch when isFocusModeEnabled is false', (done) => { + store.overrideSelector(selectFocusModeConfig, { + isSyncSessionWithTracking: true, + isSkipPreparation: false, + }); + store.overrideSelector(selectors.selectTimer, createMockTimer()); + store.overrideSelector(selectors.selectMode, FocusModeMode.Pomodoro); + store.overrideSelector(selectors.selectCurrentScreen, FocusScreen.Main); + store.overrideSelector(selectIsFocusModeEnabled, false); + store.refreshState(); + + effects = TestBed.inject(FocusModeEffects); + + currentTaskId$.next('task-123'); + + setTimeout(() => { + // Should not start session when focus mode feature is disabled + done(); + }, 50); + }); }); describe('syncTrackingStopToSession$', () => { @@ -868,6 +947,32 @@ describe('FocusModeEffects', () => { done(); }, 50); }); + + it('should NOT dispatch when isFocusModeEnabled is false', (done) => { + store.overrideSelector(selectFocusModeConfig, { + isSyncSessionWithTracking: true, + isSkipPreparation: false, + }); + store.overrideSelector( + selectors.selectTimer, + createMockTimer({ isRunning: true, purpose: 'work' }), + ); + store.overrideSelector(selectIsFocusModeEnabled, false); + store.refreshState(); + + effects = TestBed.inject(FocusModeEffects); + + currentTaskId$.next('task-123'); + + setTimeout(() => { + currentTaskId$.next(null); + }, 10); + + setTimeout(() => { + // Should not pause session when focus mode feature is disabled + done(); + }, 50); + }); }); describe('syncSessionPauseToTracking$', () => { diff --git a/src/app/features/focus-mode/store/focus-mode.effects.ts b/src/app/features/focus-mode/store/focus-mode.effects.ts index 6d74836d0..ec07532c3 100644 --- a/src/app/features/focus-mode/store/focus-mode.effects.ts +++ b/src/app/features/focus-mode/store/focus-mode.effects.ts @@ -28,6 +28,7 @@ import { openIdleDialog } from '../../idle/store/idle.actions'; import { LS } from '../../../core/persistence/storage-keys.const'; import { selectFocusModeConfig, + selectIsFocusModeEnabled, selectPomodoroConfig, } from '../../config/store/global-config.reducer'; import { updateGlobalConfigSection } from '../../config/store/global-config.actions'; @@ -54,11 +55,15 @@ export class FocusModeEffects { // Auto-show overlay when task is selected (if sync session with tracking is enabled) // Skip showing overlay if isStartInBackground is enabled + // Only triggers when focus mode feature is enabled autoShowOverlay$ = createEffect(() => - this.store.select(selectFocusModeConfig).pipe( + combineLatest([ + this.store.select(selectFocusModeConfig), + this.store.select(selectIsFocusModeEnabled), + ]).pipe( skipDuringSync(), - switchMap((cfg) => - cfg?.isSyncSessionWithTracking && !cfg?.isStartInBackground + switchMap(([cfg, isFocusModeEnabled]) => + isFocusModeEnabled && cfg?.isSyncSessionWithTracking && !cfg?.isStartInBackground ? this.taskService.currentTaskId$.pipe( distinctUntilChanged(), filter((id) => !!id), @@ -70,11 +75,14 @@ export class FocusModeEffects { ); // Sync: When tracking starts → start/unpause focus session - // Only triggers when isSyncSessionWithTracking is enabled + // Only triggers when isSyncSessionWithTracking is enabled and focus mode feature is enabled syncTrackingStartToSession$ = createEffect(() => - this.store.select(selectFocusModeConfig).pipe( - switchMap((cfg) => - cfg?.isSyncSessionWithTracking + combineLatest([ + this.store.select(selectFocusModeConfig), + this.store.select(selectIsFocusModeEnabled), + ]).pipe( + switchMap(([cfg, isFocusModeEnabled]) => + isFocusModeEnabled && cfg?.isSyncSessionWithTracking ? this.taskService.currentTaskId$.pipe( distinctUntilChanged(), filter((taskId) => !!taskId), @@ -104,15 +112,18 @@ export class FocusModeEffects { // Sync: When tracking stops → pause focus session // Uses pairwise to capture the previous task ID before it's lost + // Only triggers when focus mode feature is enabled syncTrackingStopToSession$ = createEffect(() => this.taskService.currentTaskId$.pipe( pairwise(), withLatestFrom( this.store.select(selectFocusModeConfig), this.store.select(selectors.selectTimer), + this.store.select(selectIsFocusModeEnabled), ), filter( - ([[prevTaskId, currTaskId], cfg, timer]) => + ([[prevTaskId, currTaskId], cfg, timer, isFocusModeEnabled]) => + isFocusModeEnabled && !!cfg?.isSyncSessionWithTracking && timer.purpose === 'work' && timer.isRunning && @@ -298,14 +309,13 @@ export class FocusModeEffects { ); // Handle break completion + // Note: pausedTaskId is passed in action payload to avoid race condition + // (reducer clears pausedTaskId before effect reads state) breakComplete$ = createEffect(() => this.actions$.pipe( ofType(actions.completeBreak), - withLatestFrom( - this.store.select(selectors.selectMode), - this.store.select(selectors.selectPausedTaskId), - ), - switchMap(([_, mode, pausedTaskId]) => { + withLatestFrom(this.store.select(selectors.selectMode)), + switchMap(([action, mode]) => { const strategy = this.strategyFactory.getStrategy(mode); const actionsToDispatch: any[] = []; @@ -313,8 +323,8 @@ export class FocusModeEffects { this._notifyUser(); // Resume task tracking if we paused it during break - if (pausedTaskId) { - actionsToDispatch.push(setCurrentTask({ id: pausedTaskId })); + if (action.pausedTaskId) { + actionsToDispatch.push(setCurrentTask({ id: action.pausedTaskId })); } // Auto-start next session if configured @@ -329,20 +339,18 @@ export class FocusModeEffects { ); // Handle skip break + // Note: pausedTaskId is passed in action payload to avoid race condition skipBreak$ = createEffect(() => this.actions$.pipe( ofType(actions.skipBreak), - withLatestFrom( - this.store.select(selectors.selectMode), - this.store.select(selectors.selectPausedTaskId), - ), - switchMap(([_, mode, pausedTaskId]) => { + withLatestFrom(this.store.select(selectors.selectMode)), + switchMap(([action, mode]) => { const strategy = this.strategyFactory.getStrategy(mode); const actionsToDispatch: any[] = []; // Resume task tracking if we paused it during break - if (pausedTaskId) { - actionsToDispatch.push(setCurrentTask({ id: pausedTaskId })); + if (action.pausedTaskId) { + actionsToDispatch.push(setCurrentTask({ id: action.pausedTaskId })); } // Auto-start next session if configured @@ -527,6 +535,7 @@ export class FocusModeEffects { ); // Update banner when session or break state changes + // Only shows banner when focus mode feature is enabled updateBanner$ = createEffect( () => combineLatest([ @@ -539,6 +548,7 @@ export class FocusModeEffects { this.store.select(selectors.selectIsOverlayShown), this.store.select(selectors.selectTimer), this.store.select(selectFocusModeConfig), + this.store.select(selectIsFocusModeEnabled), ]).pipe( skipDuringSync(), tap( @@ -552,9 +562,10 @@ export class FocusModeEffects { isOverlayShown, timer, focusModeConfig, + isFocusModeEnabled, ]) => { - // Only show banner when overlay is hidden - if (isOverlayShown) { + // Only show banner when overlay is hidden and focus mode feature is enabled + if (isOverlayShown || !isFocusModeEnabled) { this.bannerService.dismiss(BannerId.FocusMode); return; } @@ -694,18 +705,39 @@ export class FocusModeEffects { label: T.F.FOCUS_MODE.B.START, icon: 'play_arrow', fn: () => { - // Start a new session using the current mode's strategy - this.store - .select(selectors.selectMode) - .pipe(take(1)) - .subscribe((mode) => { - const strategy = this.strategyFactory.getStrategy(mode); - this.store.dispatch( - actions.startFocusSession({ - duration: strategy.initialSessionDuration, - }), - ); - }); + // When starting from break completion, first properly complete/skip the break + // to resume task tracking and clean up state + if (isBreakTimeUp) { + combineLatest([ + this.store.select(selectors.selectMode), + this.store.select(selectors.selectPausedTaskId), + ]) + .pipe(take(1)) + .subscribe(([mode, pausedTaskId]) => { + // Skip break (with pausedTaskId to resume tracking) + this.store.dispatch(actions.skipBreak({ pausedTaskId })); + // Then start new session + const strategy = this.strategyFactory.getStrategy(mode); + this.store.dispatch( + actions.startFocusSession({ + duration: strategy.initialSessionDuration, + }), + ); + }); + } else { + // Start a new session using the current mode's strategy + this.store + .select(selectors.selectMode) + .pipe(take(1)) + .subscribe((mode) => { + const strategy = this.strategyFactory.getStrategy(mode); + this.store.dispatch( + actions.startFocusSession({ + duration: strategy.initialSessionDuration, + }), + ); + }); + } }, } : { diff --git a/src/app/features/focus-mode/store/focus-mode.reducer.spec.ts b/src/app/features/focus-mode/store/focus-mode.reducer.spec.ts index 283575940..aa2f67dc1 100644 --- a/src/app/features/focus-mode/store/focus-mode.reducer.spec.ts +++ b/src/app/features/focus-mode/store/focus-mode.reducer.spec.ts @@ -354,7 +354,7 @@ describe('FocusModeReducer', () => { currentScreen: FocusScreen.Break, }; - const action = a.skipBreak(); + const action = a.skipBreak({ pausedTaskId: null }); const result = focusModeReducer(breakState, action); expect(result.currentScreen).toBe(FocusScreen.Main); @@ -376,7 +376,7 @@ describe('FocusModeReducer', () => { currentScreen: FocusScreen.Break, }; - const action = a.completeBreak(); + const action = a.completeBreak({ pausedTaskId: null }); const result = focusModeReducer(breakState, action); expect(result.currentScreen).toBe(FocusScreen.Main); diff --git a/src/app/features/idle/store/idle.effects.ts b/src/app/features/idle/store/idle.effects.ts index 5d970bdec..b580aaf7a 100644 --- a/src/app/features/idle/store/idle.effects.ts +++ b/src/app/features/idle/store/idle.effects.ts @@ -157,7 +157,7 @@ export class IdleEffects { // ALL IDLE SIDE EFFECTS // --------------------- if (IS_ELECTRON) { - this._uiHelperService.focusApp(); + this._uiHelperService.focusAppAfterNotification(); } // untrack current task time und unselect diff --git a/src/app/features/reminder/reminder.module.ts b/src/app/features/reminder/reminder.module.ts index 180ac34c6..711b3cd62 100644 --- a/src/app/features/reminder/reminder.module.ts +++ b/src/app/features/reminder/reminder.module.ts @@ -86,6 +86,11 @@ export class ReminderModule { this._showNotification(reminders); + // Skip dialog on Android - native notifications handle reminders + if (IS_ANDROID_WEB_VIEW) { + return; + } + const oldest = reminders[0]; const taskId = oldest.id; diff --git a/src/app/features/take-a-break/take-a-break.service.ts b/src/app/features/take-a-break/take-a-break.service.ts index a0737dfbb..ec4c11e83 100644 --- a/src/app/features/take-a-break/take-a-break.service.ts +++ b/src/app/features/take-a-break/take-a-break.service.ts @@ -285,7 +285,7 @@ export class TakeABreakService { this._triggerFullscreenBlocker$.next(true); } if (IS_ELECTRON && cfg.takeABreak.isFocusWindow) { - this._uiHelperService.focusApp(); + this._uiHelperService.focusAppAfterNotification(); } this._bannerService.open({ diff --git a/src/app/features/task-repeat-cfg/store/task-repeat-cfg.effects.spec.ts b/src/app/features/task-repeat-cfg/store/task-repeat-cfg.effects.spec.ts index 0ac42129f..415e34244 100644 --- a/src/app/features/task-repeat-cfg/store/task-repeat-cfg.effects.spec.ts +++ b/src/app/features/task-repeat-cfg/store/task-repeat-cfg.effects.spec.ts @@ -229,13 +229,16 @@ describe('TaskRepeatCfgEffects - Repeatable Subtasks', () => { it('should update task dueDay when first occurrence differs from current (#5594)', () => { // Scenario: Task is created today, but repeat config only matches future days - // Calculate next Monday from today + // Use a day that is 3 days from today (guaranteed to not be today) const today = new Date(); const todayStr = getDbDateStr(today); - const daysUntilMonday = (8 - today.getDay()) % 7 || 7; // Next Monday (not today even if today is Monday) - const nextMonday = new Date(today); - nextMonday.setDate(today.getDate() + daysUntilMonday); - const mondayStr = getDbDateStr(nextMonday); + const todayDayOfWeek = today.getDay(); // 0=Sun, 1=Mon, ..., 6=Sat + + // Pick a weekday that is 3 days from now (guaranteed to not be today) + const targetDayOfWeek = (todayDayOfWeek + 3) % 7; + const targetDate = new Date(today); + targetDate.setDate(today.getDate() + 3); + const targetDateStr = getDbDateStr(targetDate); const taskCreatedToday: TaskWithSubTasks = { ...mockTask, @@ -244,18 +247,19 @@ describe('TaskRepeatCfgEffects - Repeatable Subtasks', () => { created: today.getTime(), }; + // Create weekday booleans with only the target day set to true const weeklyRepeatCfg: TaskRepeatCfgCopy = { ...mockRepeatCfg, repeatCycle: 'WEEKLY', repeatEvery: 1, startDate: todayStr, - monday: true, - tuesday: false, - wednesday: false, - thursday: false, - friday: false, - saturday: false, - sunday: false, + sunday: targetDayOfWeek === 0, + monday: targetDayOfWeek === 1, + tuesday: targetDayOfWeek === 2, + wednesday: targetDayOfWeek === 3, + thursday: targetDayOfWeek === 4, + friday: targetDayOfWeek === 5, + saturday: targetDayOfWeek === 6, }; const action = addTaskRepeatCfgToTask({ @@ -270,9 +274,9 @@ describe('TaskRepeatCfgEffects - Repeatable Subtasks', () => { effects.updateTaskAfterMakingItRepeatable$.subscribe().unsubscribe(); - // Verify that update was called with next Monday + // Verify that update was called with the target day (3 days from today) expect(taskService.update).toHaveBeenCalledWith('parent-task-id', { - dueDay: mondayStr, + dueDay: targetDateStr, }); }); @@ -405,13 +409,16 @@ describe('TaskRepeatCfgEffects - Repeatable Subtasks', () => { it('should use task created date as fallback when dueDay is missing', () => { // Scenario: Task has no dueDay, should use created date for comparison - // Calculate next Monday from today + // Use a day that is 3 days from today (guaranteed to not be today) const today = new Date(); - const daysUntilMonday = (8 - today.getDay()) % 7 || 7; // Next Monday (not today even if today is Monday) - const nextMonday = new Date(today); - nextMonday.setDate(today.getDate() + daysUntilMonday); - const mondayStr = getDbDateStr(nextMonday); const todayStr = getDbDateStr(today); + const todayDayOfWeek = today.getDay(); // 0=Sun, 1=Mon, ..., 6=Sat + + // Pick a weekday that is 3 days from now (guaranteed to not be today) + const targetDayOfWeek = (todayDayOfWeek + 3) % 7; + const targetDate = new Date(today); + targetDate.setDate(today.getDate() + 3); + const targetDateStr = getDbDateStr(targetDate); const taskWithoutDueDay: TaskWithSubTasks = { ...mockTask, @@ -420,18 +427,19 @@ describe('TaskRepeatCfgEffects - Repeatable Subtasks', () => { created: today.getTime(), }; + // Create weekday booleans with only the target day set to true const weeklyRepeatCfg: TaskRepeatCfgCopy = { ...mockRepeatCfg, repeatCycle: 'WEEKLY', repeatEvery: 1, startDate: todayStr, - monday: true, - tuesday: false, - wednesday: false, - thursday: false, - friday: false, - saturday: false, - sunday: false, + sunday: targetDayOfWeek === 0, + monday: targetDayOfWeek === 1, + tuesday: targetDayOfWeek === 2, + wednesday: targetDayOfWeek === 3, + thursday: targetDayOfWeek === 4, + friday: targetDayOfWeek === 5, + saturday: targetDayOfWeek === 6, }; const action = addTaskRepeatCfgToTask({ @@ -446,11 +454,80 @@ describe('TaskRepeatCfgEffects - Repeatable Subtasks', () => { effects.updateTaskAfterMakingItRepeatable$.subscribe().unsubscribe(); - // Verify that update was called with next Monday + // Verify that update was called with the target day (3 days from today) expect(taskService.update).toHaveBeenCalledWith('parent-task-id', { - dueDay: mondayStr, + dueDay: targetDateStr, }); }); + + it('should update dueDay for Mon/Wed/Fri pattern when today is not a match (#5594 exact scenario)', () => { + // This test replicates the exact bug scenario from issue #5594: + // User creates Mon/Wed/Fri repeat, but dueDay incorrectly stays as today + const today = new Date(); + const todayStr = getDbDateStr(today); + const todayDayOfWeek = today.getDay(); + + // Mon=1, Wed=3, Fri=5 + const isMonWedFri = + todayDayOfWeek === 1 || todayDayOfWeek === 3 || todayDayOfWeek === 5; + + // Calculate expected first occurrence + let expectedDate: Date; + if (isMonWedFri) { + expectedDate = new Date(today); + } else { + expectedDate = new Date(today); + const daysToAdd = [1, 3, 5] + .map((d) => (d - todayDayOfWeek + 7) % 7) + .filter((d) => d > 0) + .sort((a, b) => a - b)[0]; + expectedDate.setDate(expectedDate.getDate() + daysToAdd); + } + const expectedDateStr = getDbDateStr(expectedDate); + + const taskCreatedToday: TaskWithSubTasks = { + ...mockTask, + subTasks: [], + dueDay: todayStr, // Task starts with today's date + created: today.getTime(), + }; + + const monWedFriRepeatCfg: TaskRepeatCfgCopy = { + ...mockRepeatCfg, + repeatCycle: 'WEEKLY', + repeatEvery: 1, + startDate: todayStr, + monday: true, + tuesday: false, + wednesday: true, + thursday: false, + friday: true, + saturday: false, + sunday: false, + }; + + const action = addTaskRepeatCfgToTask({ + taskRepeatCfg: monWedFriRepeatCfg, + taskId: 'parent-task-id', + }); + + actions$ = of(action); + taskService.getByIdWithSubTaskData$.and.returnValue(of(taskCreatedToday)); + + spyOn(effects as any, '_updateRegularTaskInstance'); + + effects.updateTaskAfterMakingItRepeatable$.subscribe().unsubscribe(); + + // If today is Mon/Wed/Fri, no update needed (dueDay already correct) + // If today is Tue/Thu/Sat/Sun, dueDay should be updated to next Mon/Wed/Fri + if (isMonWedFri) { + expect(taskService.update).not.toHaveBeenCalled(); + } else { + expect(taskService.update).toHaveBeenCalledWith('parent-task-id', { + dueDay: expectedDateStr, + }); + } + }); }); describe('updateStartDateOnComplete$', () => { diff --git a/src/app/features/tasks/store/task-ui.effects.spec.ts b/src/app/features/tasks/store/task-ui.effects.spec.ts new file mode 100644 index 000000000..2c88a9006 --- /dev/null +++ b/src/app/features/tasks/store/task-ui.effects.spec.ts @@ -0,0 +1,242 @@ +import { TestBed } from '@angular/core/testing'; +import { provideMockActions } from '@ngrx/effects/testing'; +import { Observable, of } from 'rxjs'; +import { TaskUiEffects } from './task-ui.effects'; +import { provideMockStore } from '@ngrx/store/testing'; +import { TaskService } from '../task.service'; +import { SnackService } from '../../../core/snack/snack.service'; +import { SnackParams } from '../../../core/snack/snack.model'; +import { WorkContextService } from '../../work-context/work-context.service'; +import { NavigateToTaskService } from '../../../core-ui/navigate-to-task/navigate-to-task.service'; +import { NotifyService } from '../../../core/notify/notify.service'; +import { BannerService } from '../../../core/banner/banner.service'; +import { GlobalConfigService } from '../../config/global-config.service'; +import { Router } from '@angular/router'; +import { TaskSharedActions } from '../../../root-store/meta/task-shared.actions'; +import { T } from '../../../t.const'; +import { Task } from '../task.model'; +import { WorkContextType } from '../../work-context/work-context.model'; +import { selectProjectById } from '../../project/store/project.selectors'; + +describe('TaskUiEffects', () => { + let effects: TaskUiEffects; + let actions$: Observable; + let snackServiceMock: jasmine.SpyObj; + let taskServiceMock: jasmine.SpyObj; + let navigateToTaskServiceMock: jasmine.SpyObj; + + const createMockTask = (overrides: Partial = {}): Task => + ({ + id: 'task-123', + title: 'Test Task', + projectId: null, + tagIds: [], + subTaskIds: [], + parentId: null, + timeSpentOnDay: {}, + timeSpent: 0, + timeEstimate: 0, + isDone: false, + notes: '', + doneOn: null, + plannedAt: null, + reminderId: null, + repeatCfgId: null, + issueId: null, + issueType: null, + issueProviderId: null, + issueWasUpdated: false, + issueLastUpdated: null, + issueTimeTracked: null, + attachments: [], + created: Date.now(), + ...overrides, + }) as Task; + + const createAddTaskAction = ( + task: Task, + ): ReturnType => + TaskSharedActions.addTask({ + task, + workContextId: 'ctx-1', + workContextType: WorkContextType.PROJECT, + isAddToBacklog: false, + isAddToBottom: false, + }); + + describe('taskCreatedSnack$ with visible task', () => { + beforeEach(() => { + snackServiceMock = jasmine.createSpyObj('SnackService', ['open']); + taskServiceMock = jasmine.createSpyObj('TaskService', ['setSelectedId']); + navigateToTaskServiceMock = jasmine.createSpyObj('NavigateToTaskService', [ + 'navigate', + ]); + + const workContextServiceMock = { + mainListTaskIds$: of(['existing-task-1', 'existing-task-2']), + }; + + TestBed.configureTestingModule({ + providers: [ + TaskUiEffects, + provideMockActions(() => actions$), + provideMockStore({ + initialState: {}, + selectors: [{ selector: selectProjectById, value: null }], + }), + { provide: SnackService, useValue: snackServiceMock }, + { provide: TaskService, useValue: taskServiceMock }, + { provide: NavigateToTaskService, useValue: navigateToTaskServiceMock }, + { provide: WorkContextService, useValue: workContextServiceMock }, + { + provide: NotifyService, + useValue: jasmine.createSpyObj('NotifyService', ['notify']), + }, + { + provide: BannerService, + useValue: jasmine.createSpyObj('BannerService', ['open', 'dismiss']), + }, + { + provide: GlobalConfigService, + useValue: { sound$: of({ doneSound: null }) }, + }, + { provide: Router, useValue: jasmine.createSpyObj('Router', ['navigate']) }, + ], + }); + + effects = TestBed.inject(TaskUiEffects); + }); + + it('should show snack with action button when task is visible on current page', (done) => { + const task = createMockTask({ id: 'existing-task-1' }); + actions$ = of(createAddTaskAction(task)); + + effects.taskCreatedSnack$.subscribe(() => { + expect(snackServiceMock.open).toHaveBeenCalled(); + const snackParams = snackServiceMock.open.calls.mostRecent() + .args[0] as SnackParams; + expect(snackParams.actionStr).toBe(T.F.TASK.S.GO_TO_TASK); + expect(snackParams.actionFn).toBeDefined(); + done(); + }); + }); + + it('should call taskService.setSelectedId when action clicked for visible task', (done) => { + const task = createMockTask({ id: 'existing-task-1' }); + actions$ = of(createAddTaskAction(task)); + + effects.taskCreatedSnack$.subscribe(() => { + const snackParams = snackServiceMock.open.calls.mostRecent() + .args[0] as SnackParams; + snackParams.actionFn!(); + expect(taskServiceMock.setSelectedId).toHaveBeenCalledWith('existing-task-1'); + expect(navigateToTaskServiceMock.navigate).not.toHaveBeenCalled(); + done(); + }); + }); + + it('should show TASK_CREATED message for task visible on current page', (done) => { + const task = createMockTask({ id: 'existing-task-1' }); + actions$ = of(createAddTaskAction(task)); + + effects.taskCreatedSnack$.subscribe(() => { + const snackParams = snackServiceMock.open.calls.mostRecent() + .args[0] as SnackParams; + expect(snackParams.msg).toBe(T.F.TASK.S.TASK_CREATED); + done(); + }); + }); + }); + + describe('taskCreatedSnack$ with non-visible task', () => { + beforeEach(() => { + snackServiceMock = jasmine.createSpyObj('SnackService', ['open']); + taskServiceMock = jasmine.createSpyObj('TaskService', ['setSelectedId']); + navigateToTaskServiceMock = jasmine.createSpyObj('NavigateToTaskService', [ + 'navigate', + ]); + + const workContextServiceMock = { + mainListTaskIds$: of(['existing-task-1', 'existing-task-2']), + }; + + TestBed.configureTestingModule({ + providers: [ + TaskUiEffects, + provideMockActions(() => actions$), + provideMockStore({ + initialState: {}, + selectors: [ + { + selector: selectProjectById, + value: { id: 'project-1', title: 'Test Project' }, + }, + ], + }), + { provide: SnackService, useValue: snackServiceMock }, + { provide: TaskService, useValue: taskServiceMock }, + { provide: NavigateToTaskService, useValue: navigateToTaskServiceMock }, + { provide: WorkContextService, useValue: workContextServiceMock }, + { + provide: NotifyService, + useValue: jasmine.createSpyObj('NotifyService', ['notify']), + }, + { + provide: BannerService, + useValue: jasmine.createSpyObj('BannerService', ['open', 'dismiss']), + }, + { + provide: GlobalConfigService, + useValue: { sound$: of({ doneSound: null }) }, + }, + { provide: Router, useValue: jasmine.createSpyObj('Router', ['navigate']) }, + ], + }); + + effects = TestBed.inject(TaskUiEffects); + }); + + it('should show snack with action button when task is NOT visible on current page', (done) => { + const task = createMockTask({ id: 'new-task-456', projectId: 'project-1' }); + actions$ = of(createAddTaskAction(task)); + + effects.taskCreatedSnack$.subscribe(() => { + expect(snackServiceMock.open).toHaveBeenCalled(); + const snackParams = snackServiceMock.open.calls.mostRecent() + .args[0] as SnackParams; + expect(snackParams.actionStr).toBe(T.F.TASK.S.GO_TO_TASK); + expect(snackParams.actionFn).toBeDefined(); + done(); + }); + }); + + it('should call navigateToTaskService.navigate when action clicked for non-visible task', (done) => { + const task = createMockTask({ id: 'new-task-456', projectId: 'project-1' }); + actions$ = of(createAddTaskAction(task)); + + effects.taskCreatedSnack$.subscribe(() => { + const snackParams = snackServiceMock.open.calls.mostRecent() + .args[0] as SnackParams; + snackParams.actionFn!(); + expect(navigateToTaskServiceMock.navigate).toHaveBeenCalledWith( + 'new-task-456', + false, + ); + expect(taskServiceMock.setSelectedId).not.toHaveBeenCalled(); + done(); + }); + }); + + it('should show CREATED_FOR_PROJECT message for task in different project', (done) => { + const task = createMockTask({ id: 'new-task-456', projectId: 'project-1' }); + actions$ = of(createAddTaskAction(task)); + + effects.taskCreatedSnack$.subscribe(() => { + const snackParams = snackServiceMock.open.calls.mostRecent() + .args[0] as SnackParams; + expect(snackParams.msg).toBe(T.F.TASK.S.CREATED_FOR_PROJECT); + done(); + }); + }); + }); +}); diff --git a/src/app/features/tasks/store/task-ui.effects.ts b/src/app/features/tasks/store/task-ui.effects.ts index fa60bc04e..0d24d8ad2 100644 --- a/src/app/features/tasks/store/task-ui.effects.ts +++ b/src/app/features/tasks/store/task-ui.effects.ts @@ -79,14 +79,14 @@ export class TaskUiEffects { ? T.F.TASK.S.CREATED_FOR_PROJECT : T.F.TASK.S.TASK_CREATED, ico: 'add', - ...(task.projectId && !isTaskVisibleOnCurrentPage - ? { - actionFn: () => { - this._navigateToTaskService.navigate(task.id, false); - }, - actionStr: T.F.TASK.S.GO_TO_TASK, - } - : {}), + actionStr: T.F.TASK.S.GO_TO_TASK, + actionFn: () => { + if (isTaskVisibleOnCurrentPage) { + this._taskService.setSelectedId(task.id); + } else { + this._navigateToTaskService.navigate(task.id, false); + } + }, }); }); }), diff --git a/src/app/features/tracking-reminder/tracking-reminder.service.ts b/src/app/features/tracking-reminder/tracking-reminder.service.ts index 3c17086fe..18a3555fe 100644 --- a/src/app/features/tracking-reminder/tracking-reminder.service.ts +++ b/src/app/features/tracking-reminder/tracking-reminder.service.ts @@ -216,6 +216,6 @@ export class TrackingReminderService { } private _focusWindow(): void { - this._uiHelperService.focusApp(); + this._uiHelperService.focusAppAfterNotification(); } } diff --git a/src/app/features/ui-helper/ui-helper.service.ts b/src/app/features/ui-helper/ui-helper.service.ts index 6fe1cbb18..dd9baeeb4 100644 --- a/src/app/features/ui-helper/ui-helper.service.ts +++ b/src/app/features/ui-helper/ui-helper.service.ts @@ -54,6 +54,29 @@ export class UiHelperService { } } + /** + * Focus app after a delay to prevent accidental input. + * Used for "surprise" focus scenarios (tracking reminder, idle, take-a-break) + * where user might still be typing in another app. + */ + focusAppAfterNotification(): void { + if (!IS_ELECTRON) { + return; + } + + const FOCUS_DELAY_MS = 1500; + + setTimeout(() => { + window.ea.showOrFocus(); + // Blur after focus to prevent any task input from receiving keystrokes + setTimeout(() => { + if (document.activeElement && document.activeElement !== document.body) { + (document.activeElement as HTMLElement).blur(); + } + }, 100); + }, FOCUS_DELAY_MS); + } + private _zoomFactorMinMax(zoomFactor: number): number { zoomFactor = Math.min(Math.max(zoomFactor, 0.1), 4); zoomFactor = Math.round(zoomFactor * 1000) / 1000; diff --git a/src/app/imex/sync/sync-wrapper.service.ts b/src/app/imex/sync/sync-wrapper.service.ts index f67f5b3d2..9425a6af7 100644 --- a/src/app/imex/sync/sync-wrapper.service.ts +++ b/src/app/imex/sync/sync-wrapper.service.ts @@ -49,6 +49,7 @@ import { UserInputWaitStateService } from './user-input-wait-state.service'; import { LegacySyncProvider } from './legacy-sync-provider.model'; import { SYNC_WAIT_TIMEOUT_MS, SYNC_REINIT_DELAY_MS } from './sync.const'; import { SuperSyncStatusService } from '../../core/persistence/operation-log/sync/super-sync-status.service'; +import { IS_ELECTRON } from '../../app.constants'; /** * Converts LegacySyncProvider to SyncProviderId. @@ -302,7 +303,7 @@ export class SyncWrapperService { return 'HANDLED_ERROR'; } else if (this._isPermissionError(error)) { this._snackService.open({ - msg: T.F.SYNC.S.ERROR_PERMISSION, + msg: this._getPermissionErrorMessage(), type: 'ERROR', config: { duration: 12000 }, }); @@ -515,6 +516,16 @@ export class SyncWrapperService { return /EROFS|EACCES|EPERM|read-only file system|permission denied/i.test(errStr); } + private _getPermissionErrorMessage(): string { + if (IS_ELECTRON && window.ea?.isFlatpak?.()) { + return T.F.SYNC.S.ERROR_PERMISSION_FLATPAK; + } + if (IS_ELECTRON && window.ea?.isSnap?.()) { + return T.F.SYNC.S.ERROR_PERMISSION_SNAP; + } + return T.F.SYNC.S.ERROR_PERMISSION; + } + private lastConflictDialog?: MatDialogRef; private _openConflictDialog$( diff --git a/src/app/pages/config-page/config-page.component.ts b/src/app/pages/config-page/config-page.component.ts index b8d2221ed..427cc5c15 100644 --- a/src/app/pages/config-page/config-page.component.ts +++ b/src/app/pages/config-page/config-page.component.ts @@ -34,6 +34,7 @@ import { SYNC_FORM } from '../../features/config/form-cfgs/sync-form.const'; import { PfapiService } from '../../pfapi/pfapi.service'; import { map, tap } from 'rxjs/operators'; import { SyncConfigService } from '../../imex/sync/sync-config.service'; +import { WebdavApi } from '../../pfapi/api/sync/providers/webdav/webdav-api'; import { AsyncPipe } from '@angular/common'; import { PluginManagementComponent } from '../../plugins/ui/plugin-management/plugin-management.component'; import { CollapsibleComponent } from '../../ui/collapsible/collapsible.component'; @@ -84,57 +85,7 @@ export class ConfigPageComponent implements OnInit, OnDestroy { globalConfigFormCfg: ConfigFormConfig; globalImexFormCfg: ConfigFormConfig; globalProductivityConfigFormCfg: ConfigFormConfig; - globalSyncConfigFormCfg = { - ...SYNC_FORM, - items: [ - ...SYNC_FORM.items!, - { - hideExpression: (m, v, field) => !m.isEnabled || !field?.form?.valid, - key: '___', - type: 'btn', - className: 'mt3 block', - templateOptions: { - text: T.F.SYNC.BTN_SYNC_NOW, - required: false, - onClick: () => { - this._syncWrapperService.sync(); - }, - }, - }, - { - hideExpression: (m: any) => - !m.isEnabled || m.syncProvider !== LegacySyncProvider.SuperSync, - key: '____', - type: 'btn', - className: 'mt2 block', - templateOptions: { - text: T.F.SYNC.BTN_RESTORE_FROM_HISTORY, - btnType: 'stroked', - required: false, - onClick: () => { - this._openRestoreDialog(); - }, - }, - }, - { - hideExpression: (m: any) => - !m.isEnabled || - m.syncProvider !== LegacySyncProvider.SuperSync || - !m.superSync?.isEncryptionEnabled, - key: '_____', - type: 'btn', - className: 'mt2 block', - templateOptions: { - text: T.F.SYNC.FORM.SUPER_SYNC.L_CHANGE_ENCRYPTION_PASSWORD, - btnType: 'stroked', - required: false, - onClick: () => { - this._openChangePasswordDialog(); - }, - }, - }, - ], - }; + globalSyncConfigFormCfg = this._buildSyncFormConfig(); globalCfg?: GlobalConfigState; @@ -259,6 +210,116 @@ export class ConfigPageComponent implements OnInit, OnDestroy { this._subs.unsubscribe(); } + private _buildSyncFormConfig(): typeof SYNC_FORM { + // Deep clone the SYNC_FORM items to avoid mutating the original + const items = SYNC_FORM.items!.map((item) => { + // Find the WebDAV fieldGroup and add the Test Connection button + if (item.key === 'webDav' && item.fieldGroup) { + return { + ...item, + fieldGroup: [ + ...item.fieldGroup, + { + type: 'btn', + className: 'mt3 block', + templateOptions: { + text: T.F.SYNC.FORM.WEB_DAV.L_TEST_CONNECTION, + required: false, + onClick: async (_field: any, _form: any, model: any) => { + const webDavCfg = model; + if ( + !webDavCfg?.baseUrl || + !webDavCfg?.userName || + !webDavCfg?.password || + !webDavCfg?.syncFolderPath + ) { + this._snackService.open({ + type: 'ERROR', + msg: T.F.SYNC.FORM.WEB_DAV.S_FILL_ALL_FIELDS, + }); + return; + } + + // Create a temporary WebdavApi instance for testing + const api = new WebdavApi(async () => webDavCfg); + const result = await api.testConnection(webDavCfg); + + if (result.success) { + this._snackService.open({ + type: 'SUCCESS', + msg: T.F.SYNC.FORM.WEB_DAV.S_TEST_SUCCESS, + translateParams: { url: result.fullUrl }, + }); + } else { + this._snackService.open({ + type: 'ERROR', + msg: T.F.SYNC.FORM.WEB_DAV.S_TEST_FAIL, + translateParams: { + error: result.error || 'Unknown error', + url: result.fullUrl, + }, + }); + } + }, + }, + }, + ], + }; + } + return item; + }); + + return { + ...SYNC_FORM, + items: [ + ...items, + { + hideExpression: (m: any, _v: any, field: any) => + !m.isEnabled || !field?.form?.valid, + type: 'btn', + className: 'mt3 block', + templateOptions: { + text: T.F.SYNC.BTN_SYNC_NOW, + required: false, + onClick: () => { + this._syncWrapperService.sync(); + }, + }, + }, + { + hideExpression: (m: any) => + !m.isEnabled || m.syncProvider !== LegacySyncProvider.SuperSync, + type: 'btn', + className: 'mt2 block', + templateOptions: { + text: T.F.SYNC.BTN_RESTORE_FROM_HISTORY, + btnType: 'stroked', + required: false, + onClick: () => { + this._openRestoreDialog(); + }, + }, + }, + { + hideExpression: (m: any) => + !m.isEnabled || + m.syncProvider !== LegacySyncProvider.SuperSync || + !m.superSync?.isEncryptionEnabled, + type: 'btn', + className: 'mt2 block', + templateOptions: { + text: T.F.SYNC.FORM.SUPER_SYNC.L_CHANGE_ENCRYPTION_PASSWORD, + btnType: 'stroked', + required: false, + onClick: () => { + this._openChangePasswordDialog(); + }, + }, + }, + ], + } as typeof SYNC_FORM; + } + async saveGlobalCfg($event: { sectionKey: GlobalConfigSectionKey | ProjectCfgFormKey; config: any; diff --git a/src/app/pfapi/api/sync/providers/webdav/webdav-api.ts b/src/app/pfapi/api/sync/providers/webdav/webdav-api.ts index 66f060e69..15b863334 100644 --- a/src/app/pfapi/api/sync/providers/webdav/webdav-api.ts +++ b/src/app/pfapi/api/sync/providers/webdav/webdav-api.ts @@ -280,19 +280,37 @@ export class WebdavApi { uploadError.response.status === WebDavHttpStatus.CONFLICT) ) { PFLog.debug( - `${WebdavApi.L}.upload() got 409, attempting to create parent directory`, + `${WebdavApi.L}.upload() got 409 Conflict for ${fullPath}. ` + + `This often indicates the sync folder path is misconfigured. ` + + `Attempting to create parent directory...`, ); // Try to create parent directory await this._ensureParentDirectory(fullPath); // Retry the upload - response = await this._makeRequest({ - url: fullPath, - method: WebDavHttpMethod.PUT, - body: data, - headers, - }); + try { + response = await this._makeRequest({ + url: fullPath, + method: WebDavHttpMethod.PUT, + body: data, + headers, + }); + } catch (retryError) { + // If retry also fails with 409, log a helpful error message + if ( + retryError instanceof HttpNotOkAPIError && + retryError.response && + retryError.response.status === WebDavHttpStatus.CONFLICT + ) { + PFLog.err( + `${WebdavApi.L}.upload() 409 Conflict persists for ${fullPath} after creating parent directory. ` + + `Verify your syncFolderPath is relative to the WebDAV server root, ` + + `not your server's internal directory path.`, + ); + } + throw retryError; + } } else { throw uploadError; } @@ -384,6 +402,49 @@ export class WebdavApi { } } + async testConnection( + cfg: WebdavPrivateCfg, + ): Promise<{ success: boolean; error?: string; fullUrl: string }> { + const fullPath = this._buildFullPath(cfg.baseUrl, cfg.syncFolderPath || '/'); + PFLog.verbose(`${WebdavApi.L}.testConnection() testing ${fullPath}`); + + try { + // Build authorization header + const auth = btoa(`${cfg.userName}:${cfg.password}`); + const headers = { + [WebDavHttpHeader.AUTHORIZATION]: `Basic ${auth}`, + [WebDavHttpHeader.CONTENT_TYPE]: 'application/xml; charset=utf-8', + [WebDavHttpHeader.DEPTH]: '0', + }; + + // Try PROPFIND on the sync folder path + const response = await this.httpAdapter.request({ + url: fullPath, + method: WebDavHttpMethod.PROPFIND, + headers, + body: WebdavXmlParser.PROPFIND_XML, + }); + + if ( + response.status === WebDavHttpStatus.MULTI_STATUS || + response.status === WebDavHttpStatus.OK + ) { + PFLog.verbose(`${WebdavApi.L}.testConnection() success for ${fullPath}`); + return { success: true, fullUrl: fullPath }; + } + + return { + success: false, + error: `Unexpected status ${response.status}`, + fullUrl: fullPath, + }; + } catch (e) { + const errorMessage = e instanceof Error ? e.message : 'Unknown error occurred'; + PFLog.warn(`${WebdavApi.L}.testConnection() failed for ${fullPath}`, e); + return { success: false, error: errorMessage, fullUrl: fullPath }; + } + } + private async _makeRequest({ url, method, diff --git a/src/app/t.const.ts b/src/app/t.const.ts index 93e64ebce..5904b98e9 100644 --- a/src/app/t.const.ts +++ b/src/app/t.const.ts @@ -1201,11 +1201,16 @@ const T = { TITLE: 'F.SYNC.FORM.TITLE', WEB_DAV: { CORS_INFO: 'F.SYNC.FORM.WEB_DAV.CORS_INFO', + D_SYNC_FOLDER_PATH: 'F.SYNC.FORM.WEB_DAV.D_SYNC_FOLDER_PATH', INFO: 'F.SYNC.FORM.WEB_DAV.INFO', L_BASE_URL: 'F.SYNC.FORM.WEB_DAV.L_BASE_URL', L_PASSWORD: 'F.SYNC.FORM.WEB_DAV.L_PASSWORD', L_SYNC_FOLDER_PATH: 'F.SYNC.FORM.WEB_DAV.L_SYNC_FOLDER_PATH', + L_TEST_CONNECTION: 'F.SYNC.FORM.WEB_DAV.L_TEST_CONNECTION', L_USER_NAME: 'F.SYNC.FORM.WEB_DAV.L_USER_NAME', + S_FILL_ALL_FIELDS: 'F.SYNC.FORM.WEB_DAV.S_FILL_ALL_FIELDS', + S_TEST_FAIL: 'F.SYNC.FORM.WEB_DAV.S_TEST_FAIL', + S_TEST_SUCCESS: 'F.SYNC.FORM.WEB_DAV.S_TEST_SUCCESS', }, }, S: { @@ -1222,6 +1227,8 @@ const T = { ERROR_CORS: 'F.SYNC.S.ERROR_CORS', ERROR_DATA_IS_CURRENTLY_WRITTEN: 'F.SYNC.S.ERROR_DATA_IS_CURRENTLY_WRITTEN', ERROR_PERMISSION: 'F.SYNC.S.ERROR_PERMISSION', + ERROR_PERMISSION_FLATPAK: 'F.SYNC.S.ERROR_PERMISSION_FLATPAK', + ERROR_PERMISSION_SNAP: 'F.SYNC.S.ERROR_PERMISSION_SNAP', ERROR_FALLBACK_TO_BACKUP: 'F.SYNC.S.ERROR_FALLBACK_TO_BACKUP', ERROR_INVALID_DATA: 'F.SYNC.S.ERROR_INVALID_DATA', ERROR_NO_REV: 'F.SYNC.S.ERROR_NO_REV', diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index e3f910ace..e13da8221 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -1182,11 +1182,16 @@ "TITLE": "Sync", "WEB_DAV": { "CORS_INFO": "Making it work in the browser: To make this work in the browser you need to whitelist Super Productivity for CORS requests for your webdav server. This can have negative security implications! For nextcloud please refer to this thread for more information. One approach to make this work on mobile is whitelisting \"https://app.super-productivity.com\" via the nextcloud app webapppassword. Use at your own risk!

", + "D_SYNC_FOLDER_PATH": "Path relative to the WebDAV server root where sync files will be stored (e.g., '/super-productivity' or '/'). This is NOT your server's internal directory path.", "INFO": "WebDAV implementations differ wildly unfortunately. Super Productivity is known to work well with Nextcloud, but it might not work with your provider.", "L_BASE_URL": "Base Url", "L_PASSWORD": "Password", "L_SYNC_FOLDER_PATH": "Sync Folder Path", - "L_USER_NAME": "Username" + "L_TEST_CONNECTION": "Test Connection", + "L_USER_NAME": "Username", + "S_FILL_ALL_FIELDS": "Please fill in all WebDAV fields first", + "S_TEST_FAIL": "Connection test failed: {{error}} - Target URL: {{url}}", + "S_TEST_SUCCESS": "Connection test successful! Target URL: {{url}}" } }, "S": { @@ -1202,7 +1207,9 @@ "DATA_REPAIRED": "Data automatically repaired ({{count}} issues fixed)", "ERROR_CORS": "WebDAV Sync Error: Network request failed.\n\nThis might be a CORS issue. Please ensure:\n• Your WebDAV server allows Cross-Origin requests\n• The server URL is correct and accessible\n• You have a working internet connection", "ERROR_DATA_IS_CURRENTLY_WRITTEN": "Remote Data is currently being written", - "ERROR_PERMISSION": "File access denied. If using Flatpak/Snap, grant filesystem permission via Flatseal or use a path inside ~/.var/app/", + "ERROR_PERMISSION": "File access denied. Please check your filesystem permissions.", + "ERROR_PERMISSION_FLATPAK": "File access denied. Grant filesystem permission via Flatseal or use a path inside ~/.var/app/", + "ERROR_PERMISSION_SNAP": "File access denied. Run 'snap connect super-productivity:home' or use a path inside ~/snap/super-productivity/common/", "ERROR_FALLBACK_TO_BACKUP": "Something went wrong while importing the data. Falling back to local backup.", "ERROR_INVALID_DATA": "Error while syncing. Invalid data", "ERROR_NO_REV": "No valid rev for remote file", diff --git a/src/assets/i18n/es.json b/src/assets/i18n/es.json index f9fdb0b1d..744febddc 100644 --- a/src/assets/i18n/es.json +++ b/src/assets/i18n/es.json @@ -1,8 +1,8 @@ { "ANDROID": { "NOTIFICATIONS": { - "SYNC_CONFLICT_TITLE": "Ha habido un error de sincronización", - "SYNC_CONFLICT_MSG": "Por favor, revisa y decide qué hacer." + "SYNC_CONFLICT_MSG": "Por favor, revisa y decide qué hacer.", + "SYNC_CONFLICT_TITLE": "Ocurrió un conflicto de sincronización" }, "PERMANENT_NOTIFICATION_MSGS": { "INITIAL": "La aplicación se ejecuta en segundo plano para permitir la sincronización si está habilitada", @@ -17,21 +17,23 @@ "MSG": "¿Quieres instalar Super Productivity como una PWA?" }, "B_OFFLINE": "Estás desconectado de internet. La sincronización y la solicitud de datos al proveedor de incidencias no funcionarán.", + "CONTEXT_MENU": { + "CHANGE_BACKGROUND": "Cambiar fondo" + }, "UPDATE_MAIN_MODEL": "¡Super Productivity ha recibido una gran actualización! Se requieren algunas migraciones de tus datos. Ten en cuenta que esto hace que tus datos sean incompatibles con versiones anteriores de la aplicación.", - "UPDATE_MAIN_MODEL_NO_UPDATE": "No se ha elegido ninguna actualización del modelo. Ten en cuenta que debes volver a la versión anterior si no deseas realizar la actualización del modelo.", + "UPDATE_MAIN_MODEL_NO_UPDATE": "No se eligió ninguna actualización del modelo. Ten en cuenta que debes volver a la versión anterior si no deseas realizar la actualización del modelo.", "UPDATE_WEB_APP": "Nueva versión disponible. ¿Cargar nueva versión?" }, "BL": { "NO_TASKS": "Actualmente no hay tareas en tu backlog" }, "BN": { - "SHOW_ISSUE_PANEL": "Mostrar panel de problemas", + "SHOW_ISSUE_PANEL": "Mostrar panel de incidencias", "SHOW_NOTES": "Mostrar notas del proyecto", - "SHOW_TASK_VIEW_CUSTOMIZER_PANEL": "Mostrar panel de filtro/grupo/ordenar" + "SHOW_TASK_VIEW_CUSTOMIZER_PANEL": "Mostrar panel de Filtro/Grupo/Ordenar" }, "CONFIRM": { "AUTO_FIX": "Tus datos parecen estar dañados (\"{{validityError}}\"). ¿Quieres intentar arreglarlos automáticamente? Esto podría resultar en una pérdida parcial de datos.", - "DELETE_STRAY_BACKUP": "¿Quieres eliminar la copia para evitar ver este diálogo?", "RELOAD_AFTER_IDB_ERROR": "No se puede acceder a la base de datos :( Las causas posibles son una actualización de la aplicación en segundo plano o poco espacio en disco. Si instalaste la aplicación en Linux como snap, también querrás habilitar 'snap set core experimental.refresh-app-awareness=true' hasta que arreglen este problema por su parte. Pulsa OK para recargar la aplicación (puede requerir reiniciar manualmente la aplicación en algunas plataformas).", "RESTORE_FILE_BACKUP": "Parece que NO HAY DATOS, pero hay copias de seguridad disponibles en \"{{dir}}\". ¿Quieres restaurar la última copia de seguridad de {{from}}?", "RESTORE_FILE_BACKUP_ANDROID": "Parece que NO HAY DATOS, pero hay una copia de seguridad disponible. ¿Quieres cargarla?", @@ -51,6 +53,11 @@ "SEARCH_PLACEHOLDER": "p. ej., naturaleza, montañas, abstracto", "TITLE": "Seleccionar imagen de fondo de Unsplash" }, + "DONATE_PAGE": { + "BUTTON_TEXT": "Donar a través de GitHub Sponsors", + "INTRO_1": "Super Productivity está financiada enteramente por la comunidad. No hay rastreo, ni anuncios, ni recolección de datos — tus tareas se quedan en tu dispositivo.", + "INTRO_2": "Si valoras este enfoque y quieres mantener el proyecto saludable y en evolución, se agradece enormemente una donación voluntaria." + }, "F": { "ATTACHMENT": { "DIALOG_EDIT": { @@ -69,30 +76,44 @@ } } }, - "BOOKMARK": { - "BAR": { - "ADD": "Añadir marcador", - "DROP": "Suelta aquí para añadir un marcador", - "EDIT": "Editar marcadores", - "NO_BOOKMARKS": "No tienes marcadores de proyecto. Añade uno arrastrando y soltando o haciendo clic en el botón 'más'." + "BOARDS": { + "DEFAULT": { + "DONE": "Hecho", + "EISENHAUER_MATRIX": "Matriz de Eisenhower", + "IMPORTANT": "Importante", + "IN_PROGRESS": "En Progreso", + "KANBAN": "Kanban", + "NOT_URGENT_IMPORTANT": "No Urgente e Importante", + "NOT_URGENT_NOT_IMPORTANT": "No Urgente y No Importante", + "TO_DO": "Por Hacer", + "URGENT": "Urgente", + "URGENT_IMPORTANT": "Urgente e Importante", + "URGENT_NOT_IMPORTANT": "Urgente y No Importante" }, - "DIALOG_EDIT": { - "ADD_BOOKMARK": "Añadir marcador", - "EDIT_BOOKMARK": "Editar marcador", - "LABELS": { - "COMMAND": "Comando", - "FILE": "Ruta del archivo", - "IMG": "Imagen", - "LINK": "Url" - }, - "SELECT_ICON": "Selecciona un icono", - "SELECT_TYPE": "Selecciona un tipo", - "TYPES": { - "COMMAND": "Comando (comando de shell personalizado)", - "FILE": "Archivo (abierto por la app predeterminada del sistema)", - "IMG": "Imagen (mostrada como miniatura)", - "LINK": "Enlace (se abre en el navegador)" - } + "FORM": { + "ADD_NEW_PANEL": "Añadir nuevo panel", + "BACKLOG_TASK_FILTER_ALL": "Todo", + "BACKLOG_TASK_FILTER_NO_BACKLOG": "Excluir", + "BACKLOG_TASK_FILTER_ONLY_BACKLOG": "Solo backlog", + "BACKLOG_TASK_FILTER_TYPE": "Tareas del backlog", + "COLUMNS": "Columnas", + "TAGS_EXCLUDED": "Etiquetas excluidas", + "TAGS_REQUIRED": "Etiquetas requeridas", + "TASK_DONE_STATE": "Estado de tarea hecha", + "TASK_DONE_STATE_ALL": "Todo", + "TASK_DONE_STATE_DONE": "Hecho", + "TASK_DONE_STATE_UNDONE": "No hecho" + }, + "V": { + "ADD_NEW_BOARD": "Añadir nuevo tablero", + "CONFIRM_DELETE": "¿Realmente quieres eliminar este tablero?", + "CREATE_NEW_TAG_BTN": "Crear etiqueta", + "CREATE_NEW_TAG_MSG": "Se necesita crear 1 nueva etiqueta para que este tablero funcione", + "CREATE_NEW_TAGS_BTN": "Crear etiquetas", + "CREATE_NEW_TAGS_MSG": "Se necesitan crear {{nr}} nuevas etiquetas para que este tablero funcione", + "EDIT_BOARD": "Editar tablero", + "NO_PANELS_BTN": "Configurar tablero", + "NO_PANELS_MSG": "Este tablero no tiene paneles configurados. Añádele algunos paneles." } }, "CALDAV": { @@ -105,9 +126,6 @@ "CALDAV_RESOURCE": "El nombre del recurso CalDav (el calendario)", "CALDAV_URL": "URL de CalDav (la URL base)", "CALDAV_USER": "Tu nombre de usuario de CalDav", - "IS_AUTO_IMPORT_ISSUES": "Añadir automáticamente tareas de CalDav no completadas a tu backlog", - "IS_AUTO_POLL": "Sondear automáticamente las tareas importadas en busca de cambios", - "IS_SEARCH_ISSUES_FROM_CALDAV": "Mostrar tareas de CalDav no completadas como sugerencia al añadir nuevas tareas", "IS_TRANSITION_ISSUES_ENABLED": "Completar automáticamente tareas de CalDav al completar la tarea" }, "FORM_SECTION": { @@ -116,14 +134,17 @@ }, "ISSUE_CONTENT": { "ASSIGNEE": "Asignado", - "AT": "EN", + "AT": "en", "ATTACHMENTS": "Adjuntos", - "CHANGED": "Cambiado", + "CHANGED": "cambiado", "COMMENTS": "Comentarios", "COMPONENTS": "Componentes", "DESCRIPTION": "Descripción", "LABELS": "Categorías", + "LIST_OF_CHANGES": "Lista de cambios", "MARK_AS_CHECKED": "Marcar actualizaciones como revisadas", + "ON": "el", + "RELATED": "Relacionado", "STATUS": "Estado", "STORY_POINTS": "Puntos de historia", "SUB_TASKS": "Subtareas", @@ -140,16 +161,15 @@ "CALENDARS": { "BANNER": { "ADD_AS_TASK": "Agregar como tarea", - "FOCUS_TASK": "Enfocar tarea", "SHOW_TASK": "Mostrar tarea", - "TXT": "{{title}} comienza a {{start}}!", - "TXT_MULTIPLE": "{{title}} comienza a {{start}}!
(y {{nrOfOtherBanners}} otros eventos están pendientes)", - "TXT_PAST": "{{title}} comenzó a {{start}}!", - "TXT_PAST_MULTIPLE": "{{title}} comenzó a {{start}}!
(y {{nrOfOtherBanners}} otros eventos están pendientes)" + "TXT": "¡{{title}} comienza a las {{start}}!", + "TXT_MULTIPLE": "¡{{title}} comienza a las {{start}}!
(y {{nrOfOtherBanners}} otros eventos están pendientes)", + "TXT_PAST": "¡{{title}} comenzó a las {{start}}!", + "TXT_PAST_MULTIPLE": "¡{{title}} comenzó a las {{start}}!
(y {{nrOfOtherBanners}} otros eventos están pendientes)" }, "EVENT_STRINGS": { - "EVENT_STR": "Evento", - "EVENTS_STR": "Eventos" + "EVENT_STR": "evento", + "EVENTS_STR": "eventos" }, "S": { "CAL_PROVIDER_ERROR": "Error del proveedor de calendario: {{errTxt}}" @@ -172,6 +192,8 @@ "L_INTERVAL": "Intervalo para repetir la frase", "L_TEXT": "Texto", "L_TEXT_DESCRIPTION": "P. ej.: \"¡Trabaja en ${currentTaskTitle}!\"", + "L_VOICE": "Seleccionar una voz", + "L_VOICE_DESCRIPTION": "Elige una voz", "TITLE": "Modo Dómina" } }, @@ -182,7 +204,8 @@ "AUTH_ERROR": "Dropbox: Token de acceso proporcionado inválido", "AUTH_ERROR_ACTION": "Cambiar Token", "OFFLINE": "Dropbox: No se puede sincronizar porque estás desconectado", - "SYNC_ERROR": "Dropbox: Error al sincronizar" + "SYNC_ERROR": "Dropbox: Error al sincronizar", + "UNABLE_TO_GENERATE_PKCE_CHALLENGE": "Dropbox: No se puede generar el desafío PKCE." } }, "FINISH_DAY_BEFORE_EXIT": { @@ -190,77 +213,14 @@ "FINISH_DAY_BEFORE_EXIT": "Hay {{nr}} tareas hechas en tu lista de hoy que aún no se han movido al archivo. ¿Realmente quieres salir sin finalizar tu día?" } }, - "GITEA": { - "DIALOG_INITIAL": { - "TITLE": "Configurar Gitea para el proyecto" - }, - "FORM": { - "FILTER_USER": "Nombre de usuario (por ejemplo, para filtrar cambios realizados por ti mismo)", - "HOST": "Host (por ejemplo: https://try.gitea.io)", - "REPO_FULL_NAME": "Nombre de usuario o nombre de la organización/proyecto", - "REPO_FULL_NAME_DESCRIPTION": "Puede encontrarse como parte de la url, al visualizar el proyecto en el Navegador.", - "SCOPE": "Alcance", - "SCOPE_ALL": "Todos", - "SCOPE_ASSIGNED": "Asignado a mí", - "SCOPE_CREATED": "Creado por mí", - "TOKEN": "Token de acceso" - }, - "FORM_SECTION": { - "HELP": "

Aquí puede configurar SuperProductivity para listar las incidencias de Gitea abiertas para un repositorio específico en el panel de creación de tareas en la vista de planificación diaria. Se listarán como sugerencias y proporcionarán un enlace a la incidencia así como más información sobre ella.

Además, puede añadir e importar automáticamente todas las incidencias abiertas.

Para superar los límites de uso y acceder puede proporcionar un token de acceso.", - "TITLE": "Gitea" - }, - "ISSUE_CONTENT": { - "ASSIGNEE": "Responsable", - "AT": "EN", - "DESCRIPTION": "Descripción", - "LABELS": "Etiquetas", - "MARK_AS_CHECKED": "Marcar actualizaciones como verificadas", - "PROJECT": "Poyecto", - "STATUS": "Estado", - "SUMMARY": "Resumen", - "WRITE_A_COMMENT": "Escribir un comentario" - }, - "S": { - "ERR_UNKNOWN": "Gitea: Error desconocido {statusCode} {errorMsg}. ¿Límite de tasa de la API excedido?" - } - }, - "REDMINE": { - "DIALOG_INITIAL": { - "TITLE": "Configurar Redmine para el proyecto" - }, - "FORM": { - "API_KEY": "Clave de acceso a la API", - "HOST": "Host (por ejemplo: https://redmine.org)", - "PROJECT_ID": "Identificador del proyecto", - "PROJECT_ID_DESCRIPTION": "Se puede encontrar como parte de la URL al ver el proyecto en el navegador.", - "SCOPE": "Alcance", - "SCOPE_ALL": "Todo", - "SCOPE_ASSIGNED": "Asignado a mí", - "SCOPE_CREATED": "Creado por mí" - }, - "FORM_SECTION": { - "HELP": "

Aquí puedes configurar SuperProductivity para listar los problemas abiertos de Redmine (ya sea la versión en línea o una instancia autohospedada) para un proyecto específico en el panel de creación de tareas en la vista de planificación diaria. Se mostrarán como sugerencias y proporcionarán un enlace al problema así como más información sobre el mismo.

Además, puedes importar automáticamente todos los problemas abiertos.

", - "TITLE": "Redmine" - }, - "ISSUE_CONTENT": { - "AUTHOR": "Autor", - "DESCRIPTION": "Descripción", - "MARK_AS_CHECKED": "Marcar actualizaciones como revisadas", - "PRIORITY": "Prioridad", - "STATUS": "Estado" - }, - "S": { - "ERR_UNKNOWN": "Redmine: Error desconocido {statusCode} {errorMsg}." - } - }, "FOCUS_MODE": { - "ADD_TIME_MINUTE": "Agregar 1 minuto", + "ADD_TIME_MINUTE": "Añadir 1 minuto", "B": { "BREAK_RUNNING": "El descanso está en curso", "POMODORO_BREAK_RUNNING": "El descanso #{{cycleNr}} está en curso", "POMODORO_SESSION_RUNNING": "La sesión Pomodoro #{{cycleNr}} está en curso", "SESSION_RUNNING": "La sesión de enfoque está en curso", - "TO_FOCUS_OVERLAY": "Para superposición de enfoque" + "TO_FOCUS_OVERLAY": "A superposición de enfoque" }, "BACK_TO_PLANNING": "Volver a la planificación", "BREAK_RELAX_MSG": "Tómate un momento para relajarte", @@ -268,7 +228,6 @@ "COMPLETE_FOCUS_SESSION": "Completar sesión de enfoque", "COMPLETE_SESSION": "Completar sesión", "CONGRATS": "¡Felicidades por completar esta sesión!", - "CONTINUE_FOCUS_SESSION": "Continuar sesión de enfoque", "CONTINUE_SESSION": "Continuar sesión", "CONTINUE_TO_NEXT_SESSION": "Continuar a la siguiente sesión", "COUNTDOWN": "Cuenta atrás", @@ -285,16 +244,17 @@ "LONG_BREAK": "Descanso largo", "LONG_BREAK_TITLE": "Descanso largo - Ciclo {{cycle}}", "NEXT": "Siguiente", - "ON": "Activado", - "OPEN_ISSUE_IN_BROWSER": "Abrir tarea en el navegador", + "ON": "activado", + "OPEN_ISSUE_IN_BROWSER": "Abrir incidencia en el navegador", "PAUSE_SESSION": "Pausar sesión", "PAUSE_TRACKING": "Pausar seguimiento", "PAUSE_TRACKING_FOR_CURRENT_TASK": "Pausar seguimiento de la tarea actual", "POMODORO": "Pomodoro", "POMODORO_HINT": "Sprints estructurados con descansos planificados", "POMODORO_SESSION_COMPLETED": "¡Sesión Pomodoro completada!", + "POMODORO_SETTINGS": "Ajustes de Pomodoro", "PREP_GET_MENTALLY_READY": "Prepárate mentalmente para estar enfocado y ser productivo", - "PREP_SIT_UPRIGHT": "\"Sé (o mantente) erguido\"", + "PREP_SIT_UPRIGHT": "Siéntate (o mantente) erguido", "PREP_STRETCH": "Haz algunos estiramientos suaves", "REMOVE_TIME_MINUTE": "Quitar 1 minuto", "RESUME_SESSION": "Reanudar sesión", @@ -303,7 +263,7 @@ "SELECT_TASK": "Selecciona la tarea en la que concentrarte", "SELECT_TASK_TO_FOCUS": "Seleccionar tarea para enfocar", "SESSION_COMPLETED": "¡Sesión de enfoque completada!", - "SET_FOCUS_SESSION_DURATION": "Establecer la duración de la sesión de enfoque", + "SET_FOCUS_SESSION_DURATION": "Establecer duración de la sesión de enfoque", "SHORT_BREAK": "Descanso corto", "SHORT_BREAK_TITLE": "Descanso corto - Ciclo {{cycle}}", "SHOW_HIDE_NOTES_AND_ATTACHMENTS": "Mostrar/ocultar notas y adjuntos de la tarea", @@ -313,19 +273,50 @@ "SWITCH_TASK": "Cambiar tarea", "WORKED_FOR": "Trabajaste durante" }, + "GITEA": { + "DIALOG_INITIAL": { + "TITLE": "Configurar Gitea para el proyecto" + }, + "FORM": { + "FILTER_USER": "Nombre de usuario (p. ej., para filtrar cambios hechos por ti mismo)", + "HOST": "Host (p. ej.: https://try.gitea.io)", + "REPO_FULL_NAME": "Nombre de usuario o nombre de la organización/proyecto", + "REPO_FULL_NAME_DESCRIPTION": "Puede encontrarse como parte de la url, al visualizar el proyecto en el Navegador.", + "SCOPE": "Alcance", + "SCOPE_ALL": "Todos", + "SCOPE_ASSIGNED": "Asignado a mí", + "SCOPE_CREATED": "Creado por mí", + "TOKEN": "Token de acceso" + }, + "FORM_SECTION": { + "HELP": "

Aquí puedes configurar SuperProductivity para listar las incidencias de Gitea abiertas para un repositorio específico en el panel de creación de tareas en la vista de planificación diaria. Se listarán como sugerencias y proporcionarán un enlace a la incidencia así como más información sobre ella.

Además, puedes añadir e importar automáticamente todas las incidencias abiertas.

Para superar los límites de uso y acceder puedes proporcionar un token de acceso.", + "TITLE": "Gitea" + }, + "ISSUE_CONTENT": { + "ASSIGNEE": "Responsable", + "AT": "en", + "DESCRIPTION": "Descripción", + "LABELS": "Etiquetas", + "MARK_AS_CHECKED": "Marcar actualizaciones como verificadas", + "PROJECT": "Proyecto", + "STATUS": "Estado", + "SUMMARY": "Resumen", + "WRITE_A_COMMENT": "Escribir un comentario" + }, + "S": { + "ERR_UNKNOWN": "Gitea: Error desconocido {{statusCode}} {{errorMsg}}. ¿Límite de tasa de la API excedido?" + } + }, "GITHUB": { "DIALOG_INITIAL": { "TITLE": "Configurar GitHub para el Proyecto" }, "FORM": { "FILTER_USER": "Nombre de usuario (p. ej., para filtrar cambios hechos por ti mismo)", - "IS_AUTO_IMPORT_ISSUES": "Importar automáticamente incidencias no resueltas de GitHub", - "IS_AUTO_POLL": "Sondear automáticamente las incidencias git importadas en busca de cambios", - "IS_SEARCH_ISSUES_FROM_GITHUB": "Mostrar incidencias de git como sugerencias al añadir nuevas tareas", - "REPO": "\"nombreUsuario/nombreRepositorio\" para el repositorio git que deseas rastrear", - "TOKEN": "Token de Acceso", - "TOKEN_DESCRIPTION": "Necesario para el acceso a repositorios privados", - "IS_ASSIGNEE_FILTER": "Filtrar Incidencias Asignadas a Mí" + "INVALID_TOKEN_MESSAGE": "Token de GitHub no válido. Debe comenzar con \"ghp_\".", + "IS_ASSIGNEE_FILTER": "Importar solo incidencias asignadas a mí al backlog", + "REPO": "nombreUsuario/nombreRepositorio", + "TOKEN": "Token de Acceso" }, "FORM_SECTION": { "HELP": "

Aquí puedes configurar SuperProductivity para listar incidencias abiertas de GitHub para un repositorio específico en el panel de creación de tareas en la vista de planificación diaria. Se listarán como sugerencias y proporcionarán un enlace a la incidencia así como más información sobre ella.

Además puedes importar automáticamente todas las incidencias abiertas.

Para superar los límites de uso y para acceder puedes proporcionar un token de acceso. Más info sobre sus ámbitos se puede encontrar aquí.", @@ -345,8 +336,8 @@ "WRITE_A_COMMENT": "Escribir un comentario" }, "S": { - "ERR_UNKNOWN": "GitHub: Error desconocido {{statusCode}} {{errorMsg}}. ¿Se ha excedido el límite de tasa de la API?", - "CONFIG_ERROR": "GitHub: Error al mapear los datos. ¿Es correcto el nombre de tu repositorio?" + "CONFIG_ERROR": "GitHub: Error al mapear los datos. ¿Es correcto el nombre de tu repositorio?", + "ERR_UNKNOWN": "GitHub: Error desconocido {{statusCode}} {{errorMsg}}. ¿Se ha excedido el límite de tasa de la API?" } }, "GITLAB": { @@ -358,16 +349,16 @@ "T_ALREADY_TRACKED": "Ya rastreado", "T_TITLE": "Título", "T_TO_BE_SUBMITTED": "Por enviar", - "TITLE": "Enviar tiempo dedicado a los problemas a GitLab", + "TITLE": "Enviar tiempo dedicado a las incidencias a GitLab", "TOTAL_MSG": "Vas a enviar {totalTimeToSubmit} de tiempo trabajado en total para hoy a {nrOfTasksToSubmit} problemas diferentes." }, "FORM": { - "FILTER_USER": "Nombre de usuario (p. ej., para filtrar cambios hechos por ti mismo)", + "FILTER": "Filtro personalizado", + "FILTER_DESCRIPTION": "Ver https://docs.gitlab.com/ee/api/issues.html#list-issues. Se pueden combinar varios con &", + "FILTER_USER": "Filtrar usuario", "GITLAB_BASE_URL": "URL base de GitLab personalizada (opcional)", - "IS_AUTO_IMPORT_ISSUES": "Importar automáticamente incidencias de GitLab", - "IS_AUTO_POLL": "Sondear automáticamente las incidencias git importadas en busca de cambios", - "IS_SEARCH_ISSUES_FROM_GITLAB": "Mostrar incidencias de git como sugerencias al añadir nuevas tareas", - "PROJECT": "(predeterminado) ID del proyecto o nombre de usuario/proyecto", + "PROJECT": "nombre de usuario/proyecto", + "PROJECT_HINT": "p. ej. johannesjo/super-productivity", "SCOPE": "Ámbito", "SCOPE_ALL": "Todo", "SCOPE_ASSIGNED": "Asignado a mí", @@ -376,6 +367,8 @@ "SOURCE_GLOBAL": "Todo", "SOURCE_GROUP": "Grupo", "SOURCE_PROJECT": "Proyecto", + "SUBMIT_TIMELOGS": "Enviar registros de tiempo a Gitlab", + "SUBMIT_TIMELOGS_DESCRIPTION": "Mostrar diálogo de seguimiento de tiempo después de hacer clic en finalizar día", "TOKEN": "Token de Acceso" }, "FORM_SECTION": { @@ -398,12 +391,49 @@ } }, "ISSUE": { - "CROSS_ORIGIN_BROWSER_WARNING": "Es probable que esta integración no funcione en tu navegador. Descárgate la versión de Superproductividad para escritorio o Android.", + "CROSS_ORIGIN_BROWSER_WARNING": "Es probable que esta integración no funcione en tu navegador. ¡Por favor, descarga la versión de escritorio o Android de Super Productivity!", "DEFAULT": { - "ISSUES_STR": "incidencias", - "ISSUE_STR": "incidencia" + "ISSUE_STR": "incidencia", + "ISSUES_STR": "incidencias" + }, + "DEFAULT_PROJECT_DESCRIPTION": "Proyecto asignado a tareas creadas desde incidencias.", + "DEFAULT_PROJECT_LABEL": "Proyecto predeterminado de Super Productivity", + "HOW_TO_GET_A_TOKEN": "¿Cómo obtener un token?", + "ISSUE_CONTENT": { + "ASSIGNEE": "Asignado", + "AT": "a las", + "ATTACHMENTS": "Adjuntos", + "AUTHOR": "Autor", + "CATEGORY": "Categoría", + "CHANGED": "cambiado", + "COMMENTS": "Comentarios", + "COMPONENTS": "Componentes", + "DESCRIPTION": "Descripción", + "DONE_RATIO": "Ratio de finalización", + "DUE_DATE": "Fecha de vencimiento", + "LABELS": "Etiquetas", + "LAST_COMMENT": "Último comentario", + "LIST_OF_CHANGES": "Lista de cambios", + "LOAD_ALL_COMMENTS": "Cargar todos los {{nr}} comentarios", + "LOAD_DESCRIPTION_AND_ALL_COMMENTS": "Cargar descripción y todos los comentarios", + "LOCATION": "Ubicación", + "MARK_AS_CHECKED": "Marcar actualizaciones como revisadas", + "ON": "el", + "PRIORITY": "Prioridad", + "RELATED": "Relacionado", + "START": "Inicio", + "STATUS": "Estado", + "STORY_POINTS": "Puntos de Historia", + "SUB_TASKS": "Subtareas", + "SUMMARY": "Resumen", + "TIME_SPENT": "Tiempo Dedicado", + "TYPE": "Tipo", + "VERSION": "Versión", + "WORKLOG": "Registro de trabajo", + "WRITE_A_COMMENT": "Escribir un comentario" }, "S": { + "ERR_GENERIC": "Error de {{issueProviderName}}: {{errTxt}}", "ERR_NETWORK": "{{issueProviderName}}: La solicitud falló debido a un error de red del lado del cliente", "ERR_NOT_CONFIGURED": "{{issueProviderName}}: No configurado correctamente", "IMPORTED_MULTIPLE_ISSUES": "{{issueProviderName}}: Importadas {{nr}} nuevas {{issuesStr}}", @@ -431,8 +461,8 @@ }, "CFG_CMP": { "ALWAYS_ASK": "Abrir siempre diálogo", - "DONE": "Estado para completar tarea", "DO_NOT": "No transicionar", + "DONE": "Estado para completar tarea", "ENABLE": "Habilitar integración con Jira", "ENABLE_TRANSITIONS": "Habilitar Gestión de Transiciones", "IN_PROGRESS": "Estado para comenzar tarea", @@ -475,17 +505,9 @@ "TIME_SPENT_TOOLTIP": "Añadir tiempos diferentes", "TITLE": "Jira: Enviar Registro de Trabajo" }, - "FORM": { - "IS_AUTO_IMPORT_ISSUES": "Importar automáticamente incidencias no resueltas de GitHub", - "IS_AUTO_POLL": "Sondear automáticamente las incidencias git importadas en busca de cambios", - "IS_SEARCH_ISSUES_FROM_GITHUB": "Mostrar incidencias de git como sugerencias al añadir nuevas tareas", - "REPO": "\"nombreUsuario/nombreRepositorio\" para el repositorio git que deseas rastrear" - }, "FORM_ADV": { "AUTO_ADD_BACKLOG_JQL_QUERY": "JQL usado para importar incidencias a Super Productivity automáticamente", "IS_ADD_WORKLOG_ON_SUB_TASK_DONE": "Abrir diálogo para enviar registro de trabajo a Jira cuando se complete una subtarea", - "IS_AUTO_IMPORT_ISSUES": "Importar automáticamente incidencias de Jira", - "IS_AUTO_POLL_TICKETS": "Comprobar cambios en incidencias importadas automáticamente y notificar", "IS_CHECK_TO_RE_ASSIGN_TICKET_ON_TASK_START": "Comprobar si la incidencia en la que se trabaja actualmente está asignada al usuario actual", "IS_WORKLOG_ENABLED": "Abrir diálogo para enviar registro de trabajo a Jira cuando se complete la tarea", "SEARCH_JQL_QUERY": "Consulta JQL para limitar la búsqueda de tareas", @@ -499,13 +521,12 @@ "ALLOW_SELF_SIGNED": "Permitir certificado autofirmado", "HOST": "Host (p. ej.: http://mi-host.es:1234)", "PASSWORD": "Token / Contraseña", - "USE_PAT": "Usar Token de Acceso Personal en lugar de contraseña", + "USE_PAT": "Usar Token de Acceso Personal en lugar de contraseña (LEGADO)", "USER_NAME": "Email / Nombre de usuario", "WONKY_COOKIE_MODE": "Autenticación de respaldo mediante Cookies (modo inestable, solo aplicación de escritorio)" }, "FORM_SECTION": { "ADV_CFG": "Configuración Avanzada", - "CREDENTIALS": "Credenciales", "HELP_ARR": { "H1": "Configuración básica", "H2": "Ajustes de registro de trabajo", @@ -551,8 +572,8 @@ "NO_VALID_TRANSITION": "Jira: No hay transición válida configurada", "TIMED_OUT": "Jira: La solicitud agotó el tiempo de espera", "TRANSITION": "Jira: Establecer incidencia \"{{issueKey}}\" a \"{{name}}\"", - "TRANSITIONS_LOADED": "Jira: Transiciones cargadas. Usa los selectores de abajo para asignarlas", "TRANSITION_SUCCESS": "Jira: Establecida incidencia {{issueKey}} a {{chosenTransition}}", + "TRANSITIONS_LOADED": "Jira: Transiciones cargadas. Usa los selectores de abajo para asignarlas", "UNABLE_TO_REASSIGN": "Jira: No se puede reasignar el ticket a ti mismo, porque no especificaste un nombre de usuario. Por favor visita los ajustes." }, "STEPPER": { @@ -576,6 +597,7 @@ "CHECK": "¡Lo hice!" }, "CMP": { + "ACTIVITY_HEATMAP": "Mapa de calor de actividad", "AVG_BREAKS_PER_DAY": "Promedio de pausas por día", "AVG_TASKS_PER_DAY_WORKED": "Promedio de tareas por día trabajado", "AVG_TIME_SPENT_ON_BREAKS": "Promedio de tiempo dedicado a pausas", @@ -583,39 +605,77 @@ "AVG_TIME_SPENT_PER_TASK": "Promedio de tiempo dedicado por tarea", "COUNTING_SUBTASKS": "(contando subtareas)", "DAYS_WORKED": "Días trabajados", + "FOCUS_SESSION_TRENDS": "Sesiones de enfoque a lo largo del tiempo", "GLOBAL_METRICS": "Métricas Globales", - "IMPROVEMENT_SELECTION_COUNT": "Número de veces que se seleccionó un factor de mejora", "MOOD_PRODUCTIVITY_OVER_TIME": "Estado de ánimo y productividad a lo largo del tiempo", "NO_ADDITIONAL_DATA_YET": "Aún no se han recopilado datos adicionales. Usa el formulario en el panel de \"Evaluación\" del resumen diario para hacerlo.", - "OBSTRUCTION_SELECTION_COUNT": "Número de veces que se seleccionó un factor de obstrucción", + "PRODUCTIVITY_BREAKDOWN_OVER_TIME": "Productividad y Sostenibilidad a lo largo del tiempo", "SIMPLE_CLICK_COUNTERS_OVER_TIME": "Contadores de Clics a lo largo del tiempo", - "SIMPLE_COUNTERS": "Contadores Simples", + "SIMPLE_COUNTERS": "Contadores Simples y Rastreo de Hábitos", "SIMPLE_STOPWATCH_COUNTERS_OVER_TIME": "Contadores de Cronómetro a lo largo del tiempo", "TASKS_DONE_CREATED": "Tareas (hechas/creadas)", "TIME_ESTIMATED": "Tiempo Estimado", + "TIME_FRAME_1_MONTH": "1 mes", + "TIME_FRAME_2_WEEKS": "2 semanas", + "TIME_FRAME_LABEL": "Marco de tiempo", + "TIME_FRAME_MAX": "Máx", "TIME_SPENT": "Tiempo Dedicado" }, "EVAL_FORM": { "ADD_NOTE_FOR_TOMORROW": "Añadir Nota para mañana", - "DISABLE_REPEAT_EVERY_DAY": "Deshabilitar repetir todos los días", - "ENABLE_REPEAT_EVERY_DAY": "Repetir todos los días", - "HELP_H1": "¿Por qué debería importarme?", - "HELP_LINK_TXT": "Ir a la sección de métricas", - "HELP_P1": "¡Hora de una pequeña autoevaluación! Tus respuestas aquí se guardan y te proporcionan un poco de estadísticas sobre cómo trabajas en la sección de métricas. Además, las sugerencias para mañana aparecerán encima de tu lista de tareas al día siguiente.", - "HELP_P2": "Esto pretende ser menos sobre calcular métricas exactas o volverse eficiente como una máquina en todo lo que haces que sobre mejorar cómo te sientes respecto a tu trabajo. Puede ser útil evaluar los puntos de dolor en tu rutina diaria, así como encontrar factores que te ayudan. Ser solo un poco sistemático al respecto ayuda con suerte a tener un mejor control sobre estos y mejorar lo que puedas.", - "IMPROVEMENTS": "¿Qué mejoró tu productividad?", - "IMPROVEMENTS_TOMORROW": "¿Qué podrías hacer para mejorar mañana?", - "MOOD": "¿Cómo te sientes?", - "MOOD_HINT": "1: Fatal – 10: Espléndido", - "NOTES": "Notas para mañana", - "OBSTRUCTIONS": "¿Qué obstaculizó tu productividad?", - "PRODUCTIVITY": "¿Cuán eficientemente trabajaste?", - "PRODUCTIVITY_HINT": "1: Ni siquiera he empezado – 10: Enormemente eficiente" + "DAILY_STATE": "Estado Diario", + "DAILY_STATE_TOOLTIP": "Sistema de cuadrantes basado en ambas puntuaciones (≥50 es alto): Flujo Profundo (alto/alto), Sobrecarga (alto/bajo), Recuperación (bajo/alto), Deriva (bajo/bajo)", + "ENERGY_LEVEL": "¿Cómo está tu energía?", + "ENERGY_LEVEL_HINT": "😫 Agotado – 😐 Bien – 😊 Bien", + "FOCUS_WORK_TIME": "Tiempo de trabajo enfocado", + "IMPACT_OF_WORK": "¿Cómo calificas el impacto de tu trabajo hoy?", + "IMPACT_OF_WORK_HINT": "1: Sin progreso significativo – 4: Impacto significativo", + "PRODUCTIVITY_SCORE": "Puntuación de Productividad", + "PRODUCTIVITY_SCORE_TOOLTIP": "65% Impacto (escala 1-4) • 30% Enfoque hacia meta de 4h • 5% trabajo total (límite en 10h)", + "SCORE_BREAKDOWN_TITLE_PRODUCTIVITY": "Desglose de Productividad de 7 Días", + "SCORE_BREAKDOWN_TITLE_SUSTAINABILITY": "Desglose de Sostenibilidad de 7 Días", + "STATE_DEEP_FLOW_HEADLINE": "Flujo Profundo: Excelente enfoque y ritmo sostenible.", + "STATE_DEEP_FLOW_HINT": "Estás en el punto óptimo — reflexiona sobre qué hizo posible esta combinación.", + "STATE_DRIFT_HEADLINE": "Deriva: Bajo enfoque y baja energía.", + "STATE_DRIFT_HINT": "Está bien. Reflexiona sobre qué te distrajo y ajusta suavemente — cada reinicio es una oportunidad para aprender.", + "STATE_IMPACT_MISMATCH_HEADLINE": "Bajo Impacto: Mucho enfoque, poca recompensa.", + "STATE_IMPACT_MISMATCH_HINT": "Invertiste un enfoque sólido — ahora dirígelo hacia trabajo de mayor impacto para sentir la diferencia.", + "STATE_OVERDRIVE_HEADLINE": "Sobrecarga: Productivo, pero con un coste.", + "STATE_OVERDRIVE_HINT": "Hiciste mucho — ahora asegúrate de recargar antes de que el agotamiento se cuele.", + "STATE_RECOVERY_HEADLINE": "Recuperación: Descansando y recargando.", + "STATE_RECOVERY_HINT": "Estás dando espacio a tu mente para recuperarse. Mañana tendrás la energía para profundizar de nuevo.", + "STATE_STEADY_HEADLINE": "Ritmo Constante: Progreso consistente y sostenible.", + "STATE_STEADY_HINT": "Buen equilibrio. Estás manteniendo el impulso sin extralimitarte — sigue nutriendo lo que hace que este ritmo funcione.", + "SUSTAINABILITY_SCORE": "Puntuación de Sostenibilidad", + "SUSTAINABILITY_SCORE_TOOLTIP": "45% Nivel de energía • 40% Horas de trabajo razonables (0 a las 10h) • 15% Equilibrio de enfoque (óptimo a las 4h)", + "TOTAL_WORK_TIME": "Tiempo total de trabajo hoy" }, "FOCUS_SESSION_DIALOG": { "ADD_BTN": "Agregar", + "ADD_SESSION": "Agregar Sesión", + "CHART_LABEL": "Tiempo de Enfoque (min)", "NEW_SESSION_DURATION": "Duración", - "SESSIONS_LIST": "Sesiones" + "NO_SESSIONS": "No hay sesiones de enfoque para este día", + "SESSIONS_LIST": "Sesiones", + "TITLE": "Sesiones de Enfoque", + "TOTAL_SESSIONS": "Total Sesiones", + "TOTAL_TIME": "Tiempo Total" + }, + "REFLECTION": { + "HISTORY_BTN": "Ver reflexiones pasadas", + "HISTORY_EMPTY": "No hay reflexiones guardadas todavía. Captura la nota de hoy para iniciar una racha.", + "HISTORY_TITLE": "Historial de reflexión", + "PLACEHOLDER_1": "¿Qué funcionó bien hoy? ¿Qué debería cambiar mañana?", + "PLACEHOLDER_2": "¿Pequeña mejora para mañana?", + "PLACEHOLDER_3": "¿Dónde se perdió el enfoque - y cómo puedes protegerlo?", + "PLACEHOLDER_4": "¿Qué tarea te dio energía? ¿Cuál te la drenó?", + "PLACEHOLDER_5": "Una cosa para repetir. Una cosa para cambiar.", + "REMIND_LABEL": "Recuérdame mañana sobre esto", + "REMINDER_CREATED": "Recordatorio de reflexión programado para mañana", + "REMINDER_ERROR": "No se pudo programar el recordatorio", + "REMINDER_NEEDS_TEXT": "Escribe una reflexión antes de pedir un recordatorio", + "REMINDER_TASK_TITLE": "Revisitar nota de reflexión", + "TITLE": "Nota de Reflexión" }, "S": { "SAVE_METRIC": "Métrica guardada exitosamente" @@ -626,32 +686,35 @@ "NOTE_LABEL": "Introduce algún texto para guardar como nota (puedes usar markdown)..." }, "D_FULLSCREEN": { - "VIEW_TEXT_ONLY": "Ver como texto sin analizar", + "VIEW_PARSED": "Ver como markdown analizado (no editable)", "VIEW_SPLIT": "Ver markdown analizado y sin analizar en vista dividida", - "VIEW_PARSED": "Ver como markdown analizado (no editable)" + "VIEW_TEXT_ONLY": "Ver como texto sin analizar" + }, + "NOTE_CMP": { + "DISABLE_PARSE": "Deshabilitar análisis de markdown para vista previa", + "ENABLE_PARSE": "Habilitar análisis de markdown" }, "NOTES_CMP": { "ADD_BTN": "Añadir nueva Nota", "DROP_TO_ADD": "Suelta aquí para añadir nueva nota", "NO_NOTES": "Actualmente no hay notas" }, - "NOTE_CMP": { - "DISABLE_PARSE": "Deshabilitar análisis de markdown para vista previa", - "ENABLE_PARSE": "Habilitar análisis de markdown" + "S": { + "NOTE_ADDED": "Nota guardada" } }, "OPEN_PROJECT": { "CFG_CMP": { "ALWAYS_ASK": "Abrir siempre diálogo", - "DONE": "Estado para completar tarea", "DO_NOT": "No transicionar", + "DONE": "Estado para completar tarea", "ENABLE": "Habilitar integración con Openproject", "ENABLE_TRANSITIONS": "Habilitar Gestión de Transiciones", "IN_PROGRESS": "Estado para comenzar tarea", "OPEN": "Estado para pausar tarea", "PROGRESS_ON_SAVE": "Progreso predeterminado al guardar", - "TRANSITION": "Gestión de Transición", - "SELECT_ISSUE_FOR_TRANSITIONS": "Selecciona incidencia para cargar transiciones disponibles" + "SELECT_ISSUE_FOR_TRANSITIONS": "Selecciona incidencia para cargar transiciones disponibles", + "TRANSITION": "Gestión de Transición" }, "DIALOG_INITIAL": { "TITLE": "Configurar OpenProject para el Proyecto" @@ -676,21 +739,18 @@ "UPDATE_STATUS": "Actualizar estado" }, "FORM": { - "FILTER_USER": "Nombre de usuario (p. ej., para filtrar cambios hechos por ti mismo)", + "FILTER_USER": "Filtrar nombre de usuario", "HOST": "Host (p. ej.: https://www.openproject.org/)", - "IS_AUTO_IMPORT_ISSUES": "Importar automáticamente paquetes de trabajo no resueltos de OpenProject", - "IS_AUTO_POLL": "Sondear automáticamente los paquetes de trabajo importados de OpenProject en busca de cambios", - "IS_SEARCH_ISSUES_FROM_OPEN_PROJECT": "Mostrar paquetes de trabajo como sugerencias al añadir nuevas tareas", "IS_SHOW_TIME_TRACKING_DIALOG": "Mostrar diálogo de seguimiento de tiempo para informar a OpenProject", "IS_SHOW_TIME_TRACKING_DIALOG_DESCRIPTION": "Requiere que el módulo de seguimiento de tiempo esté habilitado para el proyecto de OpenProject", "IS_SHOW_TIME_TRACKING_DIALOG_FOR_EACH_SUB_TASK": "Mostrar diálogo de seguimiento de tiempo cuando se completen subtareas", "PROJECT_ID": "ID del Proyecto", "PROJECT_ID_DESCRIPTION": "Se puede encontrar como parte de la url, al ver el proyecto en el Navegador.", - "TOKEN": "Token de acceso", "SCOPE": "Ámbito", "SCOPE_ALL": "Todo", "SCOPE_ASSIGNED": "Asignado a mí", - "SCOPE_CREATED": "Creado por mí" + "SCOPE_CREATED": "Creado por mí", + "TOKEN": "Token de acceso" }, "FORM_SECTION": { "HELP": "

Aquí puedes configurar SuperProductivity para listar paquetes de trabajo abiertos de OpenProject. Ten en cuenta que para que esto funcione en el navegador probablemente necesitas configurar CORS para tu servidor OpenProject, para permitir el acceso desde app.super-productivity.com

", @@ -698,6 +758,7 @@ }, "ISSUE_CONTENT": { "ASSIGNEE": "Asignado", + "ATTACHMENTS": "Adjuntos", "DESCRIPTION": "Descripción", "MARK_AS_CHECKED": "Marcar actualizaciones como revisadas", "STATUS": "Estado", @@ -706,10 +767,11 @@ "UPLOAD_ATTACHMENT": "Subir a la tarea" }, "ISSUE_STRINGS": { - "ISSUES_STR": "paquetes de trabajo", - "ISSUE_STR": "paquete de trabajo" + "ISSUE_STR": "paquete de trabajo", + "ISSUES_STR": "paquetes de trabajo" }, "S": { + "ERR_NO_FILE": "Ningún archivo seleccionado", "ERR_UNKNOWN": "OpenProject: Error desconocido {{statusCode}} {{errorMsg}}. ¿Está CORS configurado correctamente para el servidor?", "POST_TIME_SUCCESS": "OpenProject: Entrada de tiempo creada exitosamente para {{issueTitle}}", "TRANSITION": "OpenProject: Establecer incidencia \"{{issueKey}}\" a \"{{name}}\"", @@ -758,9 +820,9 @@ "L3": "¿A qué te recuerdan?", "L4": "¿Qué pasa con el pensamiento de procrastinar mientras lo observas? ¿Se intensifica? ¿Se disipa? ¿Hace surgir otras emociones?", "L5": "¿Cómo cambian las sensaciones en tu cuerpo a medida que continúas descansando tu conciencia en ellas?", - "TITLE": "Curiosidad", + "PROCRASTINATION_TRIGGERS_TEXT": "Otro método muy efectivo es registrar qué desencadenó tu necesidad de procrastinar. Por ejemplo, yo personalmente a menudo tengo la necesidad de saltar rápidamente a reddit o a mi sitio de noticias favorito cada vez que la ventana de mi navegador entra en foco. Desde que comencé a escribir mis desencadenantes en un documento de texto simple vacío, me volví consciente de cuán arraigado estaba este patrón y me ayudó a experimentar con diferentes contramedidas.", "PROCRASTINATION_TRIGGERS_TITLE": "Escribir tus desencadenantes de procrastinación", - "PROCRASTINATION_TRIGGERS_TEXT": "Otro método muy efectivo es registrar qué desencadenó tu necesidad de procrastinar. Por ejemplo, yo personalmente a menudo tengo la necesidad de saltar rápidamente a reddit o a mi sitio de noticias favorito cada vez que la ventana de mi navegador entra en foco. Desde que comencé a escribir mis desencadenantes en un documento de texto simple vacío, me volví consciente de cuán arraigado estaba este patrón y me ayudó a experimentar con diferentes contramedidas." + "TITLE": "Curiosidad" }, "H1": "¡No seas tan duro contigo mismo!", "INTRO": { @@ -796,12 +858,15 @@ "EDIT": "Editar Proyecto", "SETUP_CALDAV": "Configurar Integración Caldav", "SETUP_GIT": "Configurar Integración GitHub", + "SETUP_GITEA_PROJECT": "Configurar Integración Gitea", "SETUP_GITLAB": "Configurar Integración GitLab", "SETUP_JIRA": "Configurar Integración Jira", "SETUP_OPEN_PROJECT": "Configurar Integración OpenProject", - "SETUP_GITEA_PROJECT": "Configurar Integración Gitea", "SETUP_REDMINE_PROJECT": "Configurar Integración Redmine" }, + "D_DELETE": { + "MSG": "¿Estás seguro de que quieres eliminar el proyecto \"{{title}}\"?" + }, "FORM_BASIC": { "L_ENABLE_BACKLOG": "Habilitar Backlog del Proyecto", "L_IS_HIDDEN_FROM_MENU": "Ocultar proyecto del menú", @@ -838,48 +903,95 @@ } }, "PROJECT_FOLDER": { + "CONFIRM_DELETE": "¿Estás seguro de que quieres eliminar la carpeta \"{{title}}\"? Todos los proyectos en esta carpeta se moverán al nivel raíz.", "DIALOG": { "CREATE_TITLE": "Crear carpeta", "EDIT_TITLE": "Editar carpeta", - "NAME_LABEL": "Nombre de la carpeta" + "NAME_LABEL": "Nombre de la carpeta", + "NAME_PLACEHOLDER": "Introduce el nombre de la carpeta", + "NAME_REQUIRED": "El nombre de la carpeta es obligatorio", + "NO_PARENT": "Sin padre (nivel raíz)", + "PARENT_LABEL": "Carpeta padre" }, "SELECT": { "LABEL": "Carpeta", + "NO_PARENT": "Sin carpeta (nivel raíz)", "PLACEHOLDER": "Seleccionar carpeta" - } + }, + "TOOLTIP_CREATE": "Crear carpeta de proyectos", + "TOOLTIP_VISIBILITY": "Mostrar/ocultar proyectos" }, "QUICK_HISTORY": { "NO_DATA": "Sin datos para el año actual", "PAGE_TITLE": "Historial Rápido", "WEEK_TITLE": "Semana {{nr}} ({{timeSpent}})" }, + "REDMINE": { + "DIALOG_INITIAL": { + "TITLE": "Configurar Redmine para el proyecto" + }, + "FORM": { + "API_KEY": "Clave de acceso a la API", + "HOST": "Host (por ejemplo: https://redmine.org)", + "PROJECT_ID": "Identificador del proyecto", + "PROJECT_ID_DESCRIPTION": "Se puede encontrar como parte de la URL al ver el proyecto en el navegador.", + "SCOPE": "Alcance", + "SCOPE_ALL": "Todo", + "SCOPE_ASSIGNED": "Asignado a mí", + "SCOPE_CREATED": "Creado por mí" + }, + "FORM_SECTION": { + "HELP": "

Aquí puedes configurar SuperProductivity para listar los problemas abiertos de Redmine (ya sea la versión en línea o una instancia autohospedada) para un proyecto específico en el panel de creación de tareas en la vista de planificación diaria. Se mostrarán como sugerencias y proporcionarán un enlace al problema así como más información sobre el mismo.

Además, puedes importar automáticamente todos los problemas abiertos.

", + "TITLE": "Redmine" + }, + "ISSUE_CONTENT": { + "AUTHOR": "Autor", + "DESCRIPTION": "Descripción", + "MARK_AS_CHECKED": "Marcar actualizaciones como revisadas", + "PRIORITY": "Prioridad", + "STATUS": "Estado" + }, + "S": { + "ERR_UNKNOWN": "Redmine: Error desconocido {{statusCode}} {{errorMsg}}." + } + }, "REFLECTION_NOTE": { - "ACTION_DISMISS": "Descartar" + "ACTION_DISMISS": "Descartar", + "MSG": "Nota de Reflexión: ({{date}}): {{content}}" }, "REMINDER": { + "COUNTDOWN_BANNER": { + "HIDE": "Ocultar", + "START_NOW": "Empezar ahora", + "TXT": "¡{{title}} comienza a las {{start}}!", + "TXT_MULTIPLE": "¡{{title}} comienza a las {{start}}!
(y {{nrOfOtherBanners}} otras tareas vencen)" + }, + "S_ACTIVE_TASK_DUE": "¡La tarea en la que estás trabajando ha vencido!
({{title}})", "S_REMINDER_ERR": "Error de la interfaz de recordatorios" }, "SCHEDULE": { - "CONTINUED": "Continuado", + "CONTINUED": "continuado", "D_INITIAL": { - "TEXT": "

La idea de la línea de tiempo es proporcionar una mejor imagen de cómo se desarrollan las tareas planificadas a lo largo del tiempo. Se genera automáticamente a partir de sus Tareas y distingue dos cosas diferentes: Tareas programadas, que se muestran en el momento planificado y Tareas regulares que deberían fluir alrededor de esos eventos fijos. Todas las tareas tienen en cuenta las estimaciones de tiempo que les ha asignado.

Además de esto, también puede proporcionar una hora de inicio y finalización del trabajo. Si se configuran, las tareas regulares nunca aparecerán fuera de estas. Tenga en cuenta que el cronograma solo incluye los próximos 30 días.

", + "TEXT": "

La idea de la Línea de Tiempo es proporcionar una mejor imagen de cómo se desarrollan las tareas planificadas a lo largo del tiempo. Se genera automáticamente a partir de tus Tareas y requiere solo estimaciones de tiempo en ellas para funcionar.

Se distinguen dos cosas diferentes: Tareas Programadas, que se muestran en su hora planificada y Tareas Regulares que deberían fluir alrededor de esos eventos fijos.

Si proporcionas una hora de inicio y fin de trabajo (recomendado), las tareas regulares nunca aparecerán fuera de estos límites.

", "TITLE": "Línea de tiempo" }, - "END": "Fin del trabajo", + "END": "Fin del Trabajo", "INSERT_BEFORE": "Antes", "LUNCH_BREAK": "Pausa comida", "MONTH": "Mes", - "NO_TASKS": "Actualmente no hay tareas. Agregue algunas tareas a través del botón + en la barra superior.", - "NOW": "ahora", - "PLAN_END_DAY": "Plan al final del día", - "PLAN_START_DAY": "Plan al comienzo del día", - "START": "Inicio del trabajo", + "NO_TASKS": "Actualmente no hay tareas. Por favor añade algunas tareas a través del Botón + en la barra superior.", + "NOW": "Ahora", + "PLAN_END_DAY": "Fin de {{date}}", + "PLAN_START_DAY": "Inicio de {{date}}", + "SHIFT_KEY_INFO": "Mantén pulsado Shift para alternar el modo de planificación diaria", + "START": "Inicio del Trabajo", "TASK_PROJECTION_INFO": "Proyección futura de una tarea repetible programada", "WEEK": "Semana" }, "SEARCH_BAR": { - "INFO": "Haz clic en el icono de lista para buscar tareas archivadas", + "INFO": "Empieza a escribir para buscar tareas", "INFO_ARCHIVED": "Haz clic en el icono de archivo para buscar tareas normales", + "NO_RESULTS": "No se encontraron tareas que coincidan con tu búsqueda", "PLACEHOLDER": "Buscar tarea o descripción de tarea", "PLACEHOLDER_ARCHIVED": "Buscar tareas archivadas", "TOO_MANY_RESULTS": "Demasiados resultados, por favor acota tu búsqueda" @@ -890,40 +1002,48 @@ "OK": "¡Hazlo!" }, "D_EDIT": { - "L_COUNTER": "Cuenta", - "TITLE": "Editar Contador Simple" + "CURRENT_STREAK": "Racha Actual", + "DAILY_GOAL": "Objetivo Diario", + "DAYS": "Días", + "L_COUNTER": "Cuenta" }, "FORM": { - "ADD_NEW": "Añadir contador simple", - "HELP": "Aquí puedes configurar botones simples que aparecerán arriba a la derecha. Pueden ser temporizadores o simplemente un contador, que se incrementa al hacer clic en él.", - "L_AUTO_COUNT_UP": "Activar cuenta progresiva automáticamente para", - "L_AUTO_SWITCH_OFF": "Activar apagado automático para", - "L_AUTO_SWITCH_ON": "Activar encendido automático para", + "ADD_NEW": "Añadir contador simple/hábito", + "HELP": "Aquí puedes configurar botones simples que aparecerán arriba a la derecha. Pueden ser temporizadores o simplemente un contador, que se incrementa al hacer clic en él. Al habilitar 'Rastrear Rachas' puedes usarlos para el seguimiento de hábitos.", + "L_COUNTDOWN_DURATION": "Duración de cuenta atrás", + "L_DAILY_GOAL": "Objetivo diario para racha exitosa", "L_ICON": "Icono", "L_ICON_ON": "Icono cuando está activado", "L_IS_ENABLED": "Habilitado", "L_TITLE": "Título", - "L_TRACK_STREAKS": "Rachas de pista", + "L_TRACK_STREAKS": "Rastrear Rachas", "L_TYPE": "Tipo", - "TITLE": "Contadores Simples", + "L_WEEKDAYS": "Días de la semana para comprobar racha", + "TITLE": "Contadores Simples y Rastreo de Hábitos", "TYPE_CLICK_COUNTER": "Contador de Clics", + "TYPE_REPEATED_COUNTDOWN": "Cuenta Atrás Repetida", "TYPE_STOPWATCH": "Cronómetro" + }, + "S": { + "GOAL_REACHED_1": "¡Alcanzaste tu objetivo para hoy!", + "GOAL_REACHED_2": "Duración actual de la racha: " } }, "SYNC": { "A": { - "ARCHIVE_ONLY_UPLOADED": "Tus datos sólo se han cargado parcialmente. Inténtalo de nuevo más tarde. De lo contrario, no podrás sincronizar tus datos con otros dispositivos.", + "ARCHIVE_ONLY_UPLOADED": "Tus datos sólo se han cargado parcialmente. ¡Inténtalo de nuevo más tarde! De lo contrario, no podrás sincronizar tus datos con otros dispositivos.", "POSSIBLE_LEGACY_DATA": "Super Productivity ha mejorado la sincronización utilizando ahora dos archivos separados en lugar de uno solo, lo que permite una transferencia de datos mucho menor. Se recomienda actualizar todas las instancias de Super Productivity y sincronizar primero los datos de la instancia de la aplicación donde los datos son más recientes. Si estos son los datos de su dispositivo local, ignore esta advertencia y proceda a cargarlos confirmando el siguiente cuadro de diálogo.", "REMOTE_MODEL_VERSION_NEWER": "La versión del modelo remoto es más reciente que la local. ¡Actualice su aplicación local a la versión más reciente!" }, + "BTN_SYNC_NOW": "Sincronizar Ahora", "C": { "EMPTY_SYNC": "Estás intentando sincronizar un objeto de datos vacío. Si estás intentando configurar la sincronización desde una nueva instancia de la app, simplemente pulsa OK para cargar los datos desde el servidor. De lo contrario, por favor comprueba tus datos.", - "FORCE_IMPORT": "¿Importar datos remotos de todos modos?", - "FORCE_UPLOAD": "¿Subir datos locales de todos modos?", + "FORCE_UPLOAD": "Forzar una subida de tus datos podría llevar a la pérdida de datos. ¿Continuar?", "FORCE_UPLOAD_AFTER_ERROR": "Ocurrió un error al subir tus datos locales. ¿Intentar forzar la actualización?", "MIGRATE_LEGACY": "Detectados datos heredados al importar, ¿quieres intentar migrarlos?", "NO_REMOTE_DATA": "No se encontraron datos remotos. ¿Subir local a Remoto?", - "TRY_LOAD_REMOTE_AGAIN": "¿Intentar re-cargar datos desde remoto una vez más?" + "TRY_LOAD_REMOTE_AGAIN": "¿Intentar re-cargar datos desde remoto una vez más?", + "UNABLE_TO_LOAD_REMOTE_DATA": "No se pueden cargar datos desde el remoto. ¿Quieres intentar sobrescribir los datos remotos con tus datos locales? Todos los datos remotos se perderán en el proceso." }, "D_AUTH_CODE": { "FOLLOW_LINK": "Por favor abre el siguiente enlace y copia el código de autenticación proporcionado allí en el campo de entrada a continuación.", @@ -932,50 +1052,69 @@ "TITLE": "Inicio de sesión: {{provider}}" }, "D_CONFLICT": { + "ADDITIONAL_INFO": "Información Adicional", + "CHANGES": "Cambios", + "CHANGES_SINCE_LAST_SYNC": "Cambios desde la última sincronización", + "COMPARISON_RESULT": "Resultado de la Comparación", + "DATE": "Fecha", + "LAMPORT_CLOCK": "Revisión", "LAST_CHANGE": "Último Cambio:", "LAST_SYNC": "Última Sincronización:", + "LAST_SYNCED": "Último Sincronizado", + "LAST_WRITE": "Última Escritura", "LOCAL": "Local", "LOCAL_REMOTE": "Local -> Remoto", + "NEVER": "Nunca", + "OVERWRITE_WARNING": "ADVERTENCIA: Los datos de {{targetName}} contienen aproximadamente {{targetChanges}} cambios mientras que los datos de {{sourceName}} solo tienen {{sourceChanges}} cambios. ¿Estás seguro de que quieres sobrescribir los datos de {{targetName}} con la versión de {{sourceName}}? Esta acción no se puede deshacer.", "REMOTE": "Remoto", + "RESULT": "Resultado", "TEXT": "

Actualización desde remoto. Tanto los datos locales como los remotos parecen haber sido modificados.

", + "TIME": "Hora", + "TIMESTAMP": "Marca de tiempo", "TITLE": "Sincronización: Datos en Conflicto", "USE_LOCAL": "Mantener local", - "USE_REMOTE": "Mantener remoto" - }, - "D_PERMISSION": { - "DISABLE_SYNC": "Deshabilitar sincronización", - "PERM_FILE": "Dar permiso", - "TEXT": "

Su permiso de archivo para sincronización local ha sido revocado.

", - "TITLE": "Sincronización: permiso de archivo local denegado" + "USE_REMOTE": "Mantener remoto", + "VECTOR_CLOCK": "Reloj Vectorial", + "VECTOR_CLOCK_HEADING": "Reloj Vectorial", + "VECTOR_COMPARISON_CONCURRENT": "Concurrente (Conflicto Real)", + "VECTOR_COMPARISON_EQUAL": "Igual", + "VECTOR_COMPARISON_LOCAL_GREATER": "Local > Remoto", + "VECTOR_COMPARISON_LOCAL_LESS": "Local < Remoto" }, "D_DECRYPT_ERROR": { - "BTN_OVER_WRITE_REMOTE": "Cambiar y sobrescribir remoto", - "CHANGE_PW_AND_DECRYPT": "Cambiar e intentar descifrar", - "P1": "Sus datos están encriptados y el descifrado ha fallado. ¡Introduzca la contraseña correcta!", - "P2": "También puede cambiar su contraseña, lo que sobrescribirá todos los datos remotos.", + "BTN_OVER_WRITE_REMOTE": "Cambiar y Sobrescribir Remoto", + "CHANGE_PW_AND_DECRYPT": "Cambiar e Intentar Descifrar", + "P1": "Tus datos remotos están encriptados y el descifrado ha fallado. ¡Por favor introduce la contraseña correcta!", + "P2": "O también puedes cambiar tu contraseña, lo que sobrescribirá todos los datos remotos.", "PASSWORD": "Contraseña" }, "D_INCOMPLETE_SYNC": { - "BTN_CLOSE_APP": "Cerrar aplicación", - "BTN_DOWNLOAD_BACKUP": "Descargar copia de seguridad local", - "BTN_FORCE_UPLOAD": "Forzar carga local", + "BTN_CLOSE_APP": "Cerrar Aplicación", + "BTN_DOWNLOAD_BACKUP": "Descargar Copia de Seguridad Local", + "BTN_FORCE_UPLOAD": "Forzar Subida Local", "P1": "¡Los datos de sincronización remota son incoherentes!", "P2": "Modelo afectado:", "P3": "Tienes 2 opciones:", - "P4": "1. Vaya a su otro dispositivo e intente hacer una sincronización completa allí.", + "P4": "1. Ve a tu otro dispositivo e intenta hacer una sincronización completa allí.", "P5": "2. Sobrescribe los datos remotos con tus datos locales. ¡Todos los cambios remotos se perderán!", - "P6": "Se recomienda crear una copia de seguridad de los datos sobrescritos!!", - "T1": "¡La última sincronización no se completo!", - "T2": "Sus datos de archivo no se cargaron correctamente durante la última sincronización:", + "P6": "¡¡¡Se recomienda crear una copia de seguridad de los datos que sobrescribas!!!", + "T1": "¡La última sincronización no se completó!", + "T2": "Tus datos de archivo no se cargaron correctamente durante la última sincronización:", "T3": "Tienes 2 opciones:", "T4": "1. Ve a tu otro dispositivo y completa la sincronización allí.", "T5": "2. O puedes sobrescribir los datos remotos con los locales. Todos los cambios remotos\n se perderán.", - "T6": "¡¡¡Se recomienda crear una copia de seguridad de los datos que sobrescriba!!!" + "T6": "¡¡¡Se recomienda crear una copia de seguridad de los datos que sobrescribas!!!" }, "D_INITIAL_CFG": { "SAVE_AND_ENABLE": "Guardar y Habilitar Sincronización", "TITLE": "Configurar Sincronización" }, + "D_PERMISSION": { + "DISABLE_SYNC": "Deshabilitar sincronización", + "PERM_FILE": "Dar permiso", + "TEXT": "

Tu permiso de archivo para sincronización local ha sido revocado.

", + "TITLE": "Sincronización: permiso de archivo local denegado" + }, "FORM": { "DROPBOX": { "L_ACCESS_TOKEN": "Token de Acceso (generado desde Código de Auth)" @@ -983,32 +1122,42 @@ "GOOGLE": { "L_SYNC_FILE_NAME": "Nombre del Archivo de Sincronización" }, - "LOCAL_FILE": { - "L_SYNC_FILE_PATH": "Ruta del archivo de sincronización", - "L_SYNC_FILE_PATH_DESCRIPTION": "Ruta absoluta al archivo que se debe utilizar para la sincronización (se creará).", - "L_SYNC_FILE_PATH_PERMISSION_VALIDATION": "Necesita permiso de acceso al archivo" - }, - "L_ENABLE_SYNCING": "Habilitar Sincronización", "L_ENABLE_COMPRESSION": "Habilitar Compresión (transferencia de datos más rápida)", + "L_ENABLE_ENCRYPTION": "Habilitar cifrado de extremo a extremo (experimental) – Haz que tus datos sean inaccesibles para tu proveedor de sincronización", + "L_ENABLE_SYNCING": "Habilitar Sincronización", + "L_ENCRYPTION_NOTES": "NOTAS IMPORTANTES: Necesitarás establecer la misma contraseña en tus otros dispositivos ANTES de la próxima sincronización para que todo funcione. Por favor selecciona una contraseña que sea segura. Ten en cuenta también que NO podrás acceder a tus datos si olvidas esta contraseña. NO hay recuperación posible, ya que solo tú tienes la clave. El cifrado probablemente será lo suficientemente bueno para frustrar a la mayoría de los atacantes, pero no hay garantía.", + "L_ENCRYPTION_PASSWORD": "Contraseña de Cifrado (NO OLVIDAR)", "L_SYNC_INTERVAL": "Intervalo de Sincronización", "L_SYNC_PROVIDER": "Proveedor de Sincronización", + "LOCAL_FILE": { + "L_SYNC_FILE_PATH_PERMISSION_VALIDATION": "Necesita permiso de acceso al archivo", + "L_SYNC_FOLDER_PATH": "Ruta de la carpeta de sincronización" + }, "TITLE": "Sincronización", "WEB_DAV": { - "CORS_INFO": "Hacerlo funcionar en el navegador: Para hacer que esto funcione en el navegador necesitas poner en la lista blanca a Super Productivity para solicitudes CORS en tu instancia de Nextcloud. ¡Esto puede tener implicaciones negativas de seguridad! Por favor consulta este hilo para más información. Un enfoque para hacer que esto funcione en el móvil es poner en la lista blanca \"https://app.super-productivity.com\" a través de la app nextcloud webapppassword. ¡Úsalo bajo tu propio riesgo!

", + "CORS_INFO": "Hacerlo funcionar en el navegador: Para hacer que esto funcione en el navegador necesitas poner en la lista blanca a Super Productivity para solicitudes CORS en tu servidor webdav. ¡Esto puede tener implicaciones negativas de seguridad! Para nextcloud por favor
consulta este hilo para más información. Un enfoque para hacer que esto funcione en el móvil es poner en la lista blanca \"https://app.super-productivity.com\" a través de la app nextcloud webapppassword. ¡Úsalo bajo tu propio riesgo!

", + "INFO": "Las implementaciones de WebDAV difieren mucho desafortunadamente. Se sabe que Super Productivity funciona bien con Nextcloud, pero podría no funcionar con tu proveedor.", "L_BASE_URL": "Url Base", "L_PASSWORD": "Contraseña", - "L_SYNC_FILE_PATH": "Ruta del Archivo de Sincronización", + "L_SYNC_FOLDER_PATH": "Ruta de la Carpeta de Sincronización", "L_USER_NAME": "Nombre de usuario" } }, "S": { + "ALREADY_IN_SYNC": "Ya sincronizado", + "ALREADY_IN_SYNC_NO_LOCAL_CHANGES": "Sin cambios locales – Ya sincronizado", + "BTN_CONFIGURE": "Configurar", + "BTN_FORCE_OVERWRITE": "Forzar Sobrescritura", + "ERROR_CORS": "Error de Sincronización WebDAV: Solicitud de red fallida.\n\nEsto podría ser un problema de CORS. Por favor asegúrate de:\n• Tu servidor WebDAV permite solicitudes de Origen Cruzado\n• La URL del servidor es correcta y accesible\n• Tienes una conexión a internet funcional", + "ERROR_DATA_IS_CURRENTLY_WRITTEN": "Los datos remotos se están escribiendo actualmente", "ERROR_FALLBACK_TO_BACKUP": "Algo salió mal al importar los datos. Recurriendo a la copia de seguridad local.", "ERROR_INVALID_DATA": "Error al sincronizar. Datos inválidos", "ERROR_NO_REV": "No hay rev válida para el archivo remoto", - "ERROR_UNABLE_TO_READ_REMOTE_DATA": "Error al sincronizar. No se pueden leer los datos remotos. ¿Quizá has activado la encriptación y tu contraseña local no coincide con la utilizada para encriptar los datos remotos?", + "ERROR_UNABLE_TO_READ_REMOTE_DATA": "Error al sincronizar. No se pueden leer los datos remotos. ¿Quizá has habilitado el cifrado y tu contraseña local no coincide con la utilizada para cifrar los datos remotos?", "IMPORTING": "Importando datos", "INCOMPLETE_CFG": "La autenticación para sincronización falló. ¡Por favor comprueba tu Configuración!", "INITIAL_SYNC_ERROR": "La Sincronización Inicial falló", + "SUCCESS_DOWNLOAD": "Datos sincronizados desde remoto", "SUCCESS_IMPORT": "Datos importados", "SUCCESS_VIA_BUTTON": "Datos sincronizados exitosamente", "UNKNOWN_ERROR": "Error de Sincronización Desconocido: {{err}}", @@ -1016,39 +1165,38 @@ } }, "SAFETY_BACKUP": { - "BACKUP_NOT_FOUND": "No se encontró la copia de seguridad con ID {{backupId}}", - "BTN_CLEAR_ALL": "Borrar todo", - "BTN_CREATE_MANUAL": "Crear copia manual", - "BTN_DELETE": "Eliminar", - "BTN_REFRESH": "Actualizar", - "BTN_RESTORE": "Restaurar", - "CLEAR_FAILED": "Error al borrar las copias de seguridad", - "CLEARED_SUCCESS": "Todas las copias de seguridad borradas con éxito", - "CREATE_FAILED": "Error al crear la copia de seguridad", - "CREATED_SUCCESS": "Copia de seguridad manual creada con éxito", - "DELETE_FAILED": "Error al eliminar la copia de seguridad", - "DELETED_SUCCESS": "Copia de seguridad eliminada con éxito", - "DESCRIPTION": "Las copias de seguridad automáticas se crean antes de descargar datos remotos durante las operaciones de sincronización. Las copias se organizan en 4 espacios inteligentes: 2 más recientes, 1 primera de hoy y 1 primera de un día anterior a hoy.", - "INVALID_ID_ERROR": "ID de copia de seguridad generado inválido", - "LAST_CHANGE_PREFIX": "Último cambio:", - "LOADING": "Cargando...", - "NO_BACKUPS": "Aún no hay copias de seguridad disponibles. Las copias se crean automáticamente antes de las operaciones de sincronización.", - "REASON_BEFORE_UPDATE": "Copia automática antes de sincronizar", - "REASON_MANUAL": "Copia manual", - "RESTORE_CONFIRM_MSG": "¿Estás seguro de que quieres restaurar la copia de seguridad de {{timestamp}}?\n\n¡Esto REEMPLAZARÁ COMPLETAMENTE todos tus datos actuales!\n\nRazón: {{reason}}\n\nHaz clic en Aceptar para proceder o Cancelar para abortar.", - "RESTORE_CONFIRM_TITLE": "Restaurar copia de seguridad", - "RESTORE_FAILED": "Error al restaurar la copia de seguridad: {{error}}", - "RESTORED_SUCCESS": "Copia de seguridad restaurada con éxito", - "SLOT_BEFORE_TODAY": "Primera copia anterior a hoy", - "SLOT_RECENT": "Copia reciente", - "SLOT_TODAY": "Primera copia de hoy", - "TITLE": "Copias de seguridad de sincronización", - "TOOLTIP_CLEAR_ALL": "Eliminar todas las copias de seguridad", - "TOOLTIP_CREATE_MANUAL": "Crear una copia de seguridad manual de todos tus datos", - "TOOLTIP_DELETE": "Eliminar esta copia de seguridad", - "TOOLTIP_REFRESH": "Actualizar la lista de copias de seguridad", - "TOOLTIP_RESTORE": "Restaurar esta copia (reemplazará todos los datos actuales)" - } + "BACKUP_NOT_FOUND": "No se encontró la copia de seguridad con ID {{backupId}}", + "BTN_CLEAR_ALL": "Borrar todo", + "BTN_CREATE_MANUAL": "Crear copia manual", + "BTN_DELETE": "Eliminar", + "BTN_REFRESH": "Actualizar", + "BTN_RESTORE": "Restaurar", + "CLEAR_FAILED": "Error al borrar las copias de seguridad", + "CLEARED_SUCCESS": "Todas las copias de seguridad borradas con éxito", + "CREATE_FAILED": "Error al crear la copia de seguridad", + "CREATED_SUCCESS": "Copia de seguridad manual creada con éxito", + "DELETE_FAILED": "Error al eliminar la copia de seguridad", + "DELETED_SUCCESS": "Copia de seguridad eliminada con éxito", + "DESCRIPTION": "Las copias de seguridad automáticas se crean antes de descargar datos remotos durante las operaciones de sincronización. Las copias se organizan en 4 espacios inteligentes: 2 más recientes, 1 primera de hoy y 1 primera de un día anterior a hoy.", + "INVALID_ID_ERROR": "ID de copia de seguridad generado inválido", + "LAST_CHANGE_PREFIX": "Último cambio:", + "LOADING": "Cargando...", + "NO_BACKUPS": "Aún no hay copias de seguridad disponibles. Las copias se crean automáticamente antes de las operaciones de sincronización.", + "REASON_BEFORE_UPDATE": "Copia automática antes de sincronizar", + "REASON_MANUAL": "Copia manual", + "RESTORE_CONFIRM_MSG": "¿Estás seguro de que quieres restaurar la copia de seguridad de {{timestamp}}?\n\n¡Esto REEMPLAZARÁ COMPLETAMENTE todos tus datos actuales!\n\nRazón: {{reason}}\n\nHaz clic en Aceptar para proceder o Cancelar para abortar.", + "RESTORE_CONFIRM_TITLE": "Restaurar copia de seguridad", + "RESTORE_FAILED": "Error al restaurar la copia de seguridad: {{error}}", + "RESTORED_SUCCESS": "Copia de seguridad restaurada con éxito", + "SLOT_BEFORE_TODAY": "Primera copia anterior a hoy", + "SLOT_RECENT": "Copia reciente", + "SLOT_TODAY": "Primera copia de hoy", + "TITLE": "Copias de seguridad de sincronización", + "TOOLTIP_CLEAR_ALL": "Eliminar todas las copias de seguridad", + "TOOLTIP_CREATE_MANUAL": "Crear una copia de seguridad manual de todos tus datos", + "TOOLTIP_DELETE": "Eliminar esta copia de seguridad", + "TOOLTIP_REFRESH": "Actualizar la lista de copias de seguridad", + "TOOLTIP_RESTORE": "Restaurar esta copia (reemplazará todos los datos actuales)" }, "TAG": { "D_CREATE": { @@ -1071,8 +1219,29 @@ }, "S": { "UPDATED": "Los Ajustes de Etiqueta fueron actualizados" + }, + "TTL": { + "ADD_NEW_TAG": "Añadir nueva Etiqueta" } }, + "TAG_FOLDER": { + "CONFIRM_DELETE": "¿Estás seguro de que quieres eliminar la carpeta \"{{title}}\"? Todas las etiquetas en esta carpeta se moverán al nivel raíz.", + "DIALOG": { + "CREATE_TITLE": "Crear carpeta", + "EDIT_TITLE": "Editar carpeta", + "NAME_LABEL": "Nombre de la carpeta", + "NAME_PLACEHOLDER": "Introduce el nombre de la carpeta", + "NAME_REQUIRED": "El nombre de la carpeta es obligatorio", + "NO_PARENT": "Sin padre (nivel raíz)", + "PARENT_LABEL": "Carpeta padre" + }, + "SELECT": { + "LABEL": "Carpeta", + "NO_PARENT": "Sin carpeta (nivel raíz)", + "PLACEHOLDER": "Seleccionar carpeta" + }, + "TOOLTIP_CREATE": "Crear carpeta de etiquetas" + }, "TASK": { "ADD_TASK_BAR": { "ADD_EXISTING_TASK": "Añadir tarea existente \"{{taskTitle}}\"", @@ -1081,14 +1250,38 @@ "ADD_TASK_TO_BOTTOM_OF_TODAY": "Añadir tarea al final de la lista", "ADD_TASK_TO_TOP_OF_BACKLOG": "Añadir tarea al principio del backlog", "ADD_TASK_TO_TOP_OF_TODAY": "Añadir tarea al principio de la lista", + "CREATE_NEW_TAGS": "Crear nuevas etiquetas", "CREATE_TASK": "Crear nueva tarea", - "EXAMPLE": "Ejemplo: \"Título de tarea @vie 4pm +nombreProyecto #alguna etiqueta #otra etiqueta 10m/3h\"", - "START": "Presiona enter una vez más para comenzar" + "DUE_BUTTON": "Vencimiento", + "ESTIMATE_BUTTON": "Estimación", + "EXAMPLE": "Ejemplo: \"Título de tarea @vie 4pm +nombreProyecto #alguna-etiqueta #otra-etiqueta 10m/3h\"", + "NO_DATE": "Sin Fecha", + "NO_TIME": "Sin Hora", + "PLACEHOLDER_CREATE": "Un título de tarea #etiqueta @16:00", + "PLACEHOLDER_SEARCH": "Añadir tarea existente o incidencias...", + "SEARCH_INFO_TEXT": "Buscar y añadir incidencias y tareas del archivo y otros proyectos", + "START": "Presiona enter una vez más para comenzar", + "TAGS_BUTTON": "Etiquetas", + "TODAY": "Hoy", + "TOGGLE_ADD_TO_BACKLOG_TODAY": "Alternar añadir tarea al backlog / lista de hoy", + "TOGGLE_ADD_TOP_OR_BOTTOM": "Alternar añadir tarea al principio y final de la lista", + "TOMORROW": "Mañana", + "TOOLTIP_ADD_TASK": "Añadir tarea", + "TOOLTIP_ADD_TO_BACKLOG": "Añadir al backlog", + "TOOLTIP_ADD_TO_BOTTOM": "Añadir al final (Ctrl+1)", + "TOOLTIP_ADD_TO_TODAY": "Añadir a hoy", + "TOOLTIP_ADD_TO_TOP": "Añadir al principio (Ctrl+1)", + "TOOLTIP_CLEAR_DATE": "Borrar fecha", + "TOOLTIP_CLEAR_ESTIMATE": "Borrar estimación", + "TOOLTIP_CLEAR_TAGS": "Borrar etiquetas", + "TOOLTIP_DISABLE_SEARCH": "Deshabilitar búsqueda de incidencias (Ctrl+2)", + "TOOLTIP_ENABLE_SEARCH": "Habilitar búsqueda de incidencias (Ctrl+2)" }, "ADDITIONAL_INFO": { "ADD_ATTACHMENT": "Añadir adjunto", "ADD_SUB_TASK": "Añadir Subtarea", "ATTACHMENTS": "Adjuntos ({{nr}})", + "DUE": "Planificada para", "FROM_PARENT": "(del Padre)", "LOCAL_ATTACHMENTS": "Adjuntos Locales", "NOTES": "Descripción", @@ -1097,7 +1290,8 @@ "REPEAT": "Repetir", "SCHEDULE_TASK": "Programar Tarea", "SUB_TASKS": "Subtareas ({{nr}})", - "TIME": "Tiempo" + "TIME": "Tiempo", + "TITLE_PLACEHOLDER": "Introduce un título" }, "B": { "ADD_HALF_HOUR": "Añadir 1/2 hora", @@ -1105,59 +1299,58 @@ }, "CMP": { "ADD_SUB_TASK": "Añadir subtarea", - "ADD_TO_MY_DAY": "Añadir a Mi Día", + "ADD_TO_MY_DAY": "Añadir a Hoy", "ADD_TO_PROJECT": "Añadir a un Proyecto", "CONVERT_TO_PARENT_TASK": "Convertir a Tarea padre", "DELETE": "Eliminar Tarea", "DELETE_REPEAT_INSTANCE": "Eliminar Instancia de Tarea repetida", "DROP_ATTACHMENT": "Suelta aquí para adjuntar a \"{{title}}\"", "DUPLICATE": "Duplicar", - "EDIT_SCHEDULED": "Editar recordatorio", + "EDIT_SCHEDULED": "Reprogramar", "EDIT_TAGS": "Editar etiquetas", + "EDIT_TASK_TITLE": "Editar título", + "FOCUS_SESSION": "Iniciar Sesión de Enfoque", "MARK_DONE": "Marcar como hecha", "MARK_UNDONE": "Marcar como no hecha", "MOVE_TO_BACKLOG": "Mover al backlog", - "MOVE_TO_OTHER_PROJECT": "Mover a otro Proyecto", - "MOVE_TO_TODAY": "Mover a la lista de hoy", + "MOVE_TO_OTHER_PROJECT": "Mover a Proyecto", + "MOVE_TO_REGULAR": "Mover a lista regular", + "MOVE_TO_TOP": "Mover al Principio", "OPEN_ATTACH": "Adjuntar archivo o enlace", "OPEN_ISSUE": "Abrir en navegador", "OPEN_TIME": "Seguimiento de Tiempo", - "REMOVE_FROM_MY_DAY": "Eliminar de Mi Día", + "REMOVE_FROM_MY_DAY": "Eliminar de Hoy", "REPEAT_EDIT": "Editar config de tarea repetida", "SCHEDULE": "Programar tarea", "SHOW_UPDATES": "Mostrar actualizaciones", - "TOGGLE_ADDITIONAL": "Mostrar/Ocultar info adicional", "TOGGLE_ATTACHMENTS": "Mostrar/Ocultar adjuntos", + "TOGGLE_DETAIL_PANEL": "Mostrar/Ocultar info adicional", "TOGGLE_DONE": "Marcar como hecha/no hecha", "TOGGLE_SUB_TASK_VISIBILITY": "Alternar visibilidad de subtareas", + "TOGGLE_TAGS": "Alternar Etiquetas", "TRACK_TIME": "Iniciar seguimiento de tiempo", "TRACK_TIME_STOP": "Pausar seguimiento de tiempo", + "UNSCHEDULE_TASK": "Desprogramar tarea", "UPDATE_ISSUE_DATA": "Actualizar datos de incidencia" }, - "D_REMINDER_ADD": { - "CONFIRM_REPEAT_OK": "Programar", - "CONFIRM_REPEAT_TXT": "Estás intentando programar esta tarea repetida para otro día. Las tareas repetidas generalmente están destinadas a ser recreadas en días específicos y solo programadas en ese día. ¿Quieres proceder de todos modos?", - "DATETIME_FOR": "Fecha y hora para el recordatorio", - "EDIT": "Editar Recordatorio", - "MOVE_TO_BACKLOG": "Mover tarea al backlog hasta que esté programada", - "REMIND_AT": "Recordar a las", - "RO_10M": "10 minutos antes de que empiece", - "RO_15M": "15 minutos antes de que empiece", - "RO_1H": "1 hora antes de que empiece", - "RO_30M": "30 minutos antes de que empiece", - "RO_5M": "5 minutos antes de que empiece", - "RO_START": "cuando empiece", - "SCHEDULE": "Programar", - "UNSCHEDULE": "Desprogramar" + "D_CONFIRM_SHORT_SYNTAX_NEW_TAG": { + "MSG": "¿Quieres crear la nueva etiqueta {{tagsTxt}}?", + "OK": "Crear etiqueta" + }, + "D_CONFIRM_SHORT_SYNTAX_NEW_TAGS": { + "MSG": "¿Quieres crear las nuevas etiquetas {{tagsTxt}}?", + "OK": "Crear etiquetas" }, "D_REMINDER_VIEW": { "ADD_ALL_TO_TODAY": "Añadir todo a hoy", "ADD_TO_TODAY": "Añadir a hoy", - "DISMISS": "Descartar Recordatorio", - "DISMISS_ALL": "Descartar todo", + "COMPLETE": "Completar", + "COMPLETE_ALL": "Completar todo", + "DISMISS_ALL_REMINDERS_KEEP_TODAY": "Descartar Todos los Recordatorios (Mantener en Hoy)", + "DISMISS_REMINDER_KEEP_TODAY": "Descartar Recordatorio (Mantener en Hoy)", "DONE": "Hecho", - "DUE_TASK": "Tarea vencida", - "DUE_TASKS": "Tareas vencidas", + "DUE_TASK": "Recordatorio para tarea planificada", + "DUE_TASKS": "Recordatorio para tareas planificadas", "FOR_CURRENT": "La tarea ha vencido. ¿Quieres empezar a trabajar en ella?", "FOR_OTHER": "La tarea ha vencido. ¿Quieres empezar a trabajar en ella?", "FROM_PROJECT": "Del Proyecto: \"{{title}}\"", @@ -1167,7 +1360,32 @@ "SNOOZE": "Posponer", "SNOOZE_ALL": "Posponer todo", "START": "Empezar", - "SWITCH_CONTEXT_START": "Cambiar Contexto y Empezar" + "SWITCH_CONTEXT_START": "Cambiar Contexto y Empezar", + "UNSCHEDULE": "Desprogramar", + "UNSCHEDULE_ALL": "Desprogramar todo" + }, + "D_SCHEDULE_TASK": { + "MOVE_TO_BACKLOG": "Mover tarea al backlog hasta que esté programada", + "QA_NEXT_MONTH": "Programar Próximo Mes", + "QA_NEXT_WEEK": "Programar Próxima Semana", + "QA_REMOVE_TODAY": "Quitar tarea de hoy", + "QA_TODAY": "Programar Hoy", + "QA_TOMORROW": "Programar Mañana", + "REMIND_AT": "Recordar a las", + "RO_1H": "1 hora antes de que empiece", + "RO_5M": "5 minutos antes de que empiece", + "RO_10M": "10 minutos antes de que empiece", + "RO_15M": "15 minutos antes de que empiece", + "RO_30M": "30 minutos antes de que empiece", + "RO_NEVER": "Nunca", + "RO_START": "cuando empiece", + "SCHEDULE": "Programar", + "UNSCHEDULE": "Quitar" + }, + "D_SELECT_DATE_AND_TIME": { + "DATE": "Fecha", + "TIME": "Hora", + "TITLE": "Seleccionar Fecha y Hora" }, "D_TIME": { "ADD_FOR_OTHER_DAY": "Añadir tiempo dedicado para otro día", @@ -1175,28 +1393,33 @@ "ESTIMATE": "Estimación", "TIME_SPENT": "Tiempo Dedicado", "TIME_SPENT_ON": "Tiempo Dedicado {{date}}", - "TITLE": "Tiempo dedicado / Estimaciones" + "TITLE": "Duración" }, "D_TIME_FOR_DAY": { "ADD_ENTRY_FOR": "Añadir nueva entrada para {{date}}", "DATE": "Fecha para nueva entrada", - "HELP": "Ejemplos:
30m => 30 minutos
2h => 2 horas
2h 30m => 2 horas y 30 minutos", + "HELP": "Ejemplos:
30m => 30 minutos
2h => 2 horas
2h 30m => 2 horas y 30 minutos
1.5h => 1 hora y 30 minutos
03:15 => 3 horas y 15 minutos", "TINE_SPENT": "Tiempo Dedicado", "TITLE": "Añadir para Día" }, "N": { - "ESTIMATE_EXCEEDED": "¡Tiempo estimado superado!", + "ESTIMATE_EXCEEDED": "¡Estimación de tiempo excedida!", "ESTIMATE_EXCEEDED_BODY": "Has excedido tu tiempo estimado para \"{{title}}\"." }, "S": { "CANNOT_ASSIGN_PROJECT_FOR_REPEATABLE_TASK": "¡No se puede asignar proyecto vía sintaxis corta para tareas repetibles!", + "CREATED_FOR_PROJECT": "Tarea creada \"{{taskTitle}}\" para proyecto \"{{projectTitle}}\"", + "CREATED_FOR_PROJECT_ACTION": "Ir al Proyecto", "DELETED": "Tarea eliminada \"{{title}}\"", - "FOUND_MOVE_FROM_BACKLOG": "Se movió la tarea existente {{title}} a la lista de tareas de hoy", + "FOUND_MOVE_FROM_BACKLOG": "Se movió la tarea {{title}} del backlog a la lista de tareas de hoy", "FOUND_MOVE_FROM_OTHER_LIST": "Se añadió la tarea {{title}} de {{contextTitle}} a la lista actual", "FOUND_RESTORE_FROM_ARCHIVE": "Tarea {{title}} restaurada relacionada con incidencia desde el archivo", + "GO_TO_TASK": "Ir a la Tarea", "LAST_TAG_DELETION_WARNING": "¡Estás intentando eliminar la última etiqueta de una tarea no perteneciente a un proyecto. ¡Esto no está permitido!", - "MOVE_TO_PROJECT_NOT_ALLOWED_FOR_ISSUE_TASK": "Mover tareas de incidencia a otro proyecto no está permitido", - "REMINDER_ADDED": "Tarea programada \"{{title}}\"", + "MOVED_TO_ARCHIVE": "Movidas {{nr}} tareas al archivo", + "MOVED_TO_PROJECT": "Tarea movida \"{{taskTitle}}\" al proyecto \"{{projectTitle}}\"", + "MOVED_TO_PROJECT_ACTION": "Ir al Proyecto", + "REMINDER_ADDED": "Programada {{title}} el {{date}}", "REMINDER_DELETED": "Recordatorio eliminado para tarea", "REMINDER_UPDATED": "Recordatorio actualizado para tarea \"{{title}}\"", "TASK_CREATED": "Tarea creada \"{{taskTitle}}\"" @@ -1205,7 +1428,7 @@ "SUMMARY_TABLE": { "ESTIMATE": "Estimación", "SPENT_TODAY": "Dedicado Hoy", - "SPENT_TOTAL": "Dedicado en Total", + "SPENT_TOTAL": "Dedicado Total", "TASK": "Tarea", "TOGGLE_DONE": "des-/marcar como hecha" } @@ -1224,15 +1447,19 @@ "EVERY_X_MONTHLY_AND_TIME": "Cada {{x}} meses, {{timeStr}}", "EVERY_X_YEARLY": "Cada {{x}} años", "EVERY_X_YEARLY_AND_TIME": "Cada {{x}} años, {{timeStr}}", - "MONTHLY_CURRENT_DATE": "Mensualmente el día {{dateDayStr}}", - "MONTHLY_CURRENT_DATE_AND_TIME": "Mensualmente el día {{dateDayStr}}, {{timeStr}}", "MONDAY_TO_FRIDAY": "Lun-Vie", "MONDAY_TO_FRIDAY_AND_TIME": "Lun-Vie, {{timeStr}}", + "MONTHLY_CURRENT_DATE": "Mensualmente el día {{dateDayStr}}", + "MONTHLY_CURRENT_DATE_AND_TIME": "Mensualmente el día {{dateDayStr}}, {{timeStr}}", "WEEKLY_CURRENT_WEEKDAY": "Semanalmente el {{weekdayStr}}", "WEEKLY_CURRENT_WEEKDAY_AND_TIME": "Semanalmente el {{weekdayStr}}, {{timeStr}}", "YEARLY_CURRENT_DATE": "Anualmente el {{dayAndMonthStr}}", "YEARLY_CURRENT_DATE_AND_TIME": "Anualmente el {{dayAndMonthStr}}, {{timeStr}}" }, + "D_CONFIRM_MOVE_TO_PROJECT": { + "MSG": "Hay {{tasksNr}} instancias creadas para esta tarea repetible. ¿Quieres moverlas todas al proyecto \"{{projectName}}\"?", + "OK": "Actualizar todas las instancias" + }, "D_CONFIRM_REMOVE": { "MSG": "Eliminar la configuración de repetición convertirá todas las instancias anteriores de esta tarea en tareas normales. ¿Estás seguro de que quieres proceder?", "OK": "Eliminar completamente" @@ -1242,9 +1469,9 @@ "MSG": "Hay {{tasksNr}} instancias creadas para esta tarea repetible. ¿Desea actualizarlas todas con los nuevos valores predeterminados o sólo las tareas futuras?", "OK": "Actualizar todas las instancias" }, - "D_CONFIRM_MOVE_TO_PROJECT": { - "MSG": "Hay {{tasksNr}} instancias creadas para esta tarea repetible. ¿Quieres moverlas todas al proyecto \"{{projectName}}\"?", - "OK": "Actualizar todas las instancias" + "D_DELETE_INSTANCE": { + "MSG": "¿Eliminar la instancia de tarea repetida el {{date}}? Esto evitará que la tarea se cree solo en esta fecha.", + "OK": "Eliminar Instancia" }, "D_EDIT": { "ADD": "Añadir Config de Tarea Repetida", @@ -1262,7 +1489,11 @@ "C_WEEK": "Semana", "C_YEAR": "Año", "DEFAULT_ESTIMATE": "Estimación Predeterminada", + "DISABLE_AUTO_UPDATE_SUBTASKS": "Deshabilitar actualización automática de subtareas", + "DISABLE_AUTO_UPDATE_SUBTASKS_DESCRIPTION": "No actualizar subtareas heredadas automáticamente cuando la instancia más nueva cambia", "FRIDAY": "Viernes", + "INHERIT_SUBTASKS": "Heredar subtareas", + "INHERIT_SUBTASKS_DESCRIPTION": "Cuando está habilitado, las subtareas de la instancia de tarea más reciente se recrearán con la tarea repetitiva", "IS_ADD_TO_BOTTOM": "Mover tarea al final de la lista", "MONDAY": "Lunes", "NOTES": "Notas predeterminadas", @@ -1270,16 +1501,23 @@ "ORDER_DESCRIPTION": "Orden de creación de tareas repetibles. Solo afecta a tareas repetibles creadas al mismo tiempo. Un valor más bajo significa que una tarea se creará más arriba en la lista, un número más alto que estará más abajo. Un valor mayor que 0 significa que los elementos se crean al final de las tareas normales.", "Q_CUSTOM": "Configuración de repetición personalizada", "Q_DAILY": "Todos los días", - "Q_MONTHLY_CURRENT_DATE": "Cada mes el {{dateDayStr}}", "Q_MONDAY_TO_FRIDAY": "De lunes a viernes", + "Q_MONTHLY_CURRENT_DATE": "Cada mes el {{dateDayStr}}", "Q_WEEKLY_CURRENT_WEEKDAY": "Cada semana el {{weekdayStr}}", "Q_YEARLY_CURRENT_DATE": "Cada año el {{dayAndMonthStr}}", "QUICK_SETTING": "Configuración de Repetición", "REMIND_AT": "Recordar a las", "REMIND_AT_PLACEHOLDER": "Selecciona cuándo recordar", + "REMOVE_FOR_DATE": "Eliminar para {{date}}", + "REMOVE_INSTANCE": "Eliminar hoy", "REPEAT_CYCLE": "Ciclo de repetición", "REPEAT_EVERY": "Repetir cada", + "REPEAT_FROM_COMPLETION_DATE": "Repetir, cuando esté hecha", + "REPEAT_FROM_COMPLETION_DATE_DESCRIPTION": "La siguiente tarea se crea a partir de tu fecha de finalización, no de la fecha de inicio. (p. ej., 'Cada 7 días' = 7 días después de completar)", "SATURDAY": "Sábado", + "SCHEDULE_TYPE_FIXED": "Horario fijo (p. ej., cada lunes, 1ro del mes)", + "SCHEDULE_TYPE_FLEXIBLE": "Después de completar (p. ej., 7 días después de terminar)", + "SCHEDULE_TYPE_LABEL": "Tipo de horario", "START_DATE": "Fecha de inicio", "START_TIME": "Hora de inicio programada", "START_TIME_DESCRIPTION": "P. ej. 15:00. Dejar en blanco para una tarea de todo el día", @@ -1290,21 +1528,44 @@ "WEDNESDAY": "Miércoles" } }, - "TIMELINE": { - "CONTINUED": "continuado", - "D_INITIAL": { - "TEXT": "

La idea de la Línea de Tiempo es proporcionar una mejor imagen de cómo se desarrollan las tareas planificadas a lo largo del tiempo. Se genera automáticamente a partir de tus Tareas y distingue dos cosas diferentes: Tareas Programadas, que se muestran en su hora planificada y Tareas Regulares que deberían fluir alrededor de esos eventos fijos. Todas las tareas consideran las estimaciones de tiempo que les has asignado.

Además de esto, también puedes proporcionar una hora de inicio y fin de trabajo. Si se configura, las tareas regulares nunca aparecerán fuera de estas. Ten en cuenta que la línea de tiempo solo incluye los próximos 30 días.

", - "TITLE": "Línea de tiempo" - }, - "END": "Fin del Trabajo", - "MENU_TITLE": "Línea de tiempo", - "NOW": "Ahora", - "NO_TASKS": "Actualmente no hay tareas. Por favor añade algunas tareas a través del Botón + en la barra superior.", - "S": { - "CAL_PROVIDER_ERROR": "Error del Proveedor de Cal de Línea de Tiempo: {{errTxt}}" - }, - "START": "Inicio del Trabajo", - "TASK_PROJECTION_INFO": "Proyección futura de una tarea repetible programada" + "TASK_VIEW": { + "CUSTOMIZER": { + "ENTER_PROJECT": "Filtrar Proyectos", + "ENTER_TAG": "Filtrar Etiqueta", + "ESTIMATED_TIME": "Tiempo Estimado", + "FILTER_BY": "Filtrar Por", + "FILTER_DEFAULT": "Sin Filtro", + "FILTER_ESTIMATED_TIME": "Tiempo Estimado", + "FILTER_PROJECT": "Proyecto", + "FILTER_SCHEDULED_DATE": "Fecha Programada", + "FILTER_TAG": "Etiqueta", + "FILTER_TIME_SPENT": "Tiempo Dedicado", + "GROUP_BY": "Agrupar Por", + "GROUP_DEFAULT": "Sin Grupo", + "GROUP_PROJECT": "Proyecto", + "GROUP_SCHEDULED_DATE": "Fecha Programada", + "GROUP_TAG": "Etiqueta", + "RESET_ALL": "Restablecer Todo", + "SCHEDULED_NEXT_MONTH": "Próximo Mes", + "SCHEDULED_NEXT_WEEK": "Próxima Semana", + "SCHEDULED_THIS_MONTH": "Este Mes", + "SCHEDULED_THIS_WEEK": "Esta Semana", + "SCHEDULED_TODAY": "Hoy", + "SCHEDULED_TOMORROW": "Mañana", + "SORT_CREATION_DATE": "Fecha de Creación", + "SORT_DEFAULT": "Predeterminado", + "SORT_NAME": "Nombre", + "SORT_PERMANENT": "Ordenar (permanente)", + "SORT_SCHEDULED_DATE": "Fecha Programada", + "SORT_TEMPORARY": "Ordenar (temporal)", + "TIME_1HOUR": "> 1 Hora", + "TIME_2HOUR": "> 2 Horas", + "TIME_10MIN": "> 10 Minutos", + "TIME_30MIN": "> 30 Minutos", + "FILTER_NOT_SPECIFIED": "No especificado", + "TIME_SPENT": "Tiempo Dedicado", + "TITLE": "Personalizar Vista de Tareas" + } }, "TIME_TRACKING": { "B": { @@ -1312,14 +1573,16 @@ "SNOOZE": "Posponer {{time}}" }, "B_TTR": { - "ADD_TO_TASK": "Añadir a la tarea", - "MSG": "No ha realizado un seguimiento del tiempo durante {{time}}" + "ADD_TO_TASK": "Añadir a la Tarea", + "MSG": "No has estado rastreando tiempo durante {{time}}", + "MSG_WITHOUT_TIME": "No has estado rastreando tiempo" }, "D_IDLE": { "ADD_ENTRY": "Añadir entrada para seguimiento", "BREAK": "Descanso", "CREATE_AND_TRACK": "Crear y rastrear en", "IDLE_FOR": "Has estado inactivo durante:", + "RESET_BREAK_REMINDER_TIMER": "Reiniciar temporizador de recordatorio de descanso", "SIMPLE_CONFIRM_COUNTER_CANCEL": "Omitir", "SIMPLE_CONFIRM_COUNTER_OK": "Rastrear", "SIMPLE_COUNTER_CONFIRM_TXT": "Seleccionaste omitir, pero activaste {{nr}} botón(es) de contador simple. ¿Quieres rastrear el tiempo inactivo en ellos?", @@ -1328,29 +1591,19 @@ "SKIP": "Omitir", "SPLIT_TIME": "Dividir tiempo en múltiples tareas y descansos", "TASK": "Tarea", - "TASK_BREAK": "Tarea+Descanso", - "TRACK_TO": "Rastrear en" + "TRACK_TO": "Rastrear en", + "WARN_SIMPLE_COUNTER": "El tiempo contará hacia los botones de contador simple activados.", + "WARN_SIMPLE_COUNTER_BREAK": "El tiempo TODAVÍA contará en los botones de contador simple activados." }, "D_TRACKING_REMINDER": { "CREATE_AND_TRACK": "Crear y rastrear en", "IDLE_FOR": "Has estado inactivo durante:", + "NOTIFICATION_TITLE": "¡Rastrea tu tiempo!", "TASK": "Tarea", "TRACK_TO": "Rastrear en:", "UNTRACKED_TIME": "Tiempo no rastreado:" } }, - "WELCOME": { - "D_WELCOME": { - "L1": "Empezando", - "L2": "Vídeo introductorio en youtube", - "L3": "Haz una pregunta", - "LETS_GO": "¡¡Vamos allá!!", - "MOBILE_INFO": "Desliza las tareas creadas a la derecha, para marcarlas como hechas.\n Deslízalas a la izquierda para programar. Mantén pulsado para más acciones.", - "T1": "¡Genial ver que decidiste darle una oportunidad a esta app! 🦄", - "T2": "Si tienes problemas para orientarte por aquí, hay varios lugares donde\n puedes encontrar ayuda:", - "TITLE": "¡Bienvenido a Super Productivity!" - } - }, "WORKLOG": { "CMP": { "DAYS_WORKED": "Días, trabajados:", @@ -1373,7 +1626,7 @@ "GROUP_BY": "Agrupar por", "O": { "DATE": "Fecha", - "ENDED_WORKING": "Termino del trabajo", + "ENDED_WORKING": "Terminó de Trabajar", "ESTIMATE_AS_CLOCK": "Estimar como reloj (por ejemplo, 5:23)", "ESTIMATE_AS_MILLISECONDS": "Estimar en milisegundos", "ESTIMATE_AS_STRING": "Estimar como cadena de texto (por ejemplo, 5h 23m)", @@ -1381,62 +1634,86 @@ "FULL_HOURS": "horas completas", "FULL_QUARTERS": "cuartos completos", "NOTES": "Descripciones de tareas", - "PARENT_TASK": "Tareas principales", + "PARENT_TASK": "Tarea principal", "PARENT_TASK_TITLES_ONLY": "Sólo títulos de tareas principales", "PROJECTS": "Nombres de proyectos", "STARTED_WORKING": "Comienzo del trabajo", "TAGS": "Etiquetas", - "TASK_SUBTASK": "Tarea / Subtarea", + "TASK_SUBTASK": "Tarea/Subtarea", "TIME_AS_CLOCK": "Tiempo como reloj (por ejemplo, 5:23)", - "TIME_AS_MILLISECONDS": "Tiempo como milisegundos.", + "TIME_AS_MILLISECONDS": "Tiempo como milisegundos", "TIME_AS_STRING": "Tiempo como cadena de texto (por ejemplo, 5h 23m)", - "TITLES_AND_SUB_TASK_TITLES": "Títulos y títulos de subtarea", + "TITLES_AND_SUB_TASK_TITLES": "Títulos y Títulos de Subtareas", "WORKLOG": "Registro de trabajo" }, "OPTIONS": "Opciones", - "ROUND_END_TIME_TO": "Tiempo de finalización de ronda a", - "ROUND_START_TIME_TO": "Hora de inicio de ronda para", - "ROUND_TIME_WORKED_TO": "Tiempo de ronda trabajado para", + "ROUND_END_TIME_TO": "Redondear tiempo de finalización a", + "ROUND_START_TIME_TO": "Redondear tiempo de inicio a", + "ROUND_TIME_WORKED_TO": "Redondear tiempo trabajado a", "SAVE_TO_FILE": "Guardar en archivo", "SEPARATE_TASKS_BY": "Tareas separadas por", "SHOW_AS_TEXT": "Mostrar como texto" }, "WEEK": { "EXPORT": "Exportar Datos de la Semana", + "FOCUS_SUMMARY": "Sesiones de enfoque (nr / tiempo)", "NO_DATA": "No hay tareas esta semana todavía.", "TITLE": "Título" } } }, "FILE_IMEX": { + "DIALOG_CONFIRM_URL_IMPORT": { + "INITIATED_MSG": "Se ha iniciado una importación automática de datos.", + "SOURCE_URL_DOMAIN": "Dominio de Origen", + "TITLE": "Confirmar Importación de Datos desde URL", + "WARNING_MSG": "Proceder sobrescribirá los datos y la configuración actuales de tu aplicación con el contenido de la URL especificada. Esta acción no se puede deshacer.", + "WARNING_TITLE": "Advertencia" + }, "EXPORT_DATA": "Exportar Datos", "IMPORT_FROM_FILE": "Importar desde archivo", + "IMPORT_FROM_URL": "Importar desde URL", + "IMPORT_FROM_URL_DIALOG_DESCRIPTION": "Por favor introduce la URL completa del archivo JSON de copia de seguridad de Super Productivity que quieres importar.", + "IMPORT_FROM_URL_DIALOG_TITLE": "Importar desde URL", + "OPEN_IMPORT_FROM_URL_DIALOG": "Importar desde URL", "PRIVACY_EXPORT": "Exportar datos anonimizados (para enviar a contact@super-productivity.com para depuración)", - "S_ERR_INVALID_DATA": "Importación fallida: JSON Inválido" + "S_BACKUP_DOWNLOADED": "Copia de seguridad descargada a la carpeta de documentos de android", + "S_ERR_IMPORT_FAILED": "Error al importar datos", + "S_ERR_INVALID_DATA": "Importación fallida: JSON Inválido", + "S_ERR_INVALID_URL": "Importación fallida: URL proporcionada inválida", + "S_ERR_NETWORK": "Importación fallida: Error de red al obtener datos de la URL", + "S_IMPORT_FROM_URL_ERR_DECODE": "Error: No se pudo decodificar el parámetro URL para importar. Por favor asegúrate de que esté formateado correctamente.", + "URL_PLACEHOLDER": "Introduce URL para importar" }, "G": { "ADD": "Agregar", - "ADVANCED_CFG": "Configuración avanzada", + "ADVANCED_CFG": "Configuración Avanzada", "CANCEL": "Cancelar", "CLOSE": "Cerrar", "COMPLETE": "Completo", "CONFIRM": "Confirmar", "DELETE": "Borrar", "DISMISS": "Descartar", - "DURATION_DESCRIPTION": "Por ejemplo, \"5h 23m\", que es equivalente a 5 horas y 23 minutos.", "DO_IT": "¡Hazlo!", + "DONT_SHOW_AGAIN": "No mostrar de nuevo", + "DURATION_DESCRIPTION": "p. ej. \"5h 23m\" que resulta en 5 horas y 23 minutos", "EDIT": "Editar", "ENABLED": "Habilitado", - "EXTENSION_INFO": "Por favor
descarga la extensión de chrome para permitir la comunicación con la Api de Jira y el Manejo de Tiempo Inactivo. Ten en cuenta que esto no funciona para móvil.", + "EXAMPLE_VAL": "p. ej. 32m", + "EXTENSION_INFO": "Por favor descarga la extensión de chrome para permitir la comunicación con la Api de Jira y el Manejo de Tiempo Inactivo. Ten en cuenta que esto no funciona para móvil. ¡¡Sin la extensión las funciones no funcionarán!!", + "HIDE": "Ocultar", + "ICON_INP_DESCRIPTION": "¡Todos los emojis utf-8 también son compatibles!", + "INBOX_PROJECT_TITLE": "Bandeja de entrada", "LOGIN": "Iniciar sesión", "LOGOUT": "Cerrar sesión", "MINUTES": "{{m}} minutos", "MOVE_BACKWARD": "Mover hacia atrás", "MOVE_FORWARD": "Mover hacia adelante", "NEXT": "Siguiente", - "NONE": "Ninguno", "NO_CON": "Actualmente estás desconectado. Por favor reconecta a internet.", + "NONE": "Ninguno", "OK": "Ok", + "OVERDUE": "Vencido", "PREVIOUS": "Anterior", "REMOVE": "Quitar", "RESET": "Reiniciar", @@ -1444,6 +1721,7 @@ "SUBMIT": "Enviar", "TITLE": "Título", "TODAY_TAG_TITLE": "Hoy", + "TRACKING_INTERVAL_DESCRIPTION": "Rastrear tiempo usando este intervalo en milisegundos. Es posible que desees cambiar esto para reducir las escrituras en disco. Ver issue #2355.", "UNDO": "Deshacer", "UPDATE": "Actualizar", "WITHOUT_PROJECT": "Sin Proyecto", @@ -1479,7 +1757,6 @@ "HELP": "

Cuando el manejo de tiempo inactivo está habilitado se abrirá un diálogo después de una cantidad de tiempo especificada para comprobar si y en qué tarea quieres rastrear tu tiempo, cuando has estado inactivo.

", "IS_ENABLE_IDLE_TIME_TRACKING": "Habilitar manejo de tiempo inactivo", "IS_ONLY_OPEN_IDLE_WHEN_CURRENT_TASK": "Solo activar diálogo de tiempo inactivo cuando hay una tarea actual seleccionada", - "IS_UN_TRACKED_IDLE_RESETS_BREAK_TIMER": "El tiempo inactivo no rastreado reinicia el temporizador de hacer un descanso", "MIN_IDLE_TIME": "Activar inactividad después de X", "TITLE": "Manejo de Inactividad" }, @@ -1489,6 +1766,7 @@ }, "KEYBOARD": { "ADD_NEW_NOTE": "Añadir nueva nota", + "ADD_NEW_PROJECT": "Añadir nuevo proyecto", "ADD_NEW_TASK": "Añadir Nueva Tarea", "APP_WIDE_SHORTCUTS": "Atajos Globales (toda la aplicación)", "COLLAPSE_SUB_TASKS": "Colapsar Subtareas", @@ -1499,18 +1777,19 @@ "GLOBAL_TOGGLE_TASK_START": "Alternar seguimiento de tiempo para última tarea activa", "GO_TO_DAILY_AGENDA": "Ir a Agenda", "GO_TO_FOCUS_MODE": "Ir a Modo Enfoque", + "GO_TO_SCHEDULE": "Ir a Cronograma", "GO_TO_SCHEDULED_VIEW": "Ir a Tareas programadas", "GO_TO_SETTINGS": "Ir a Ajustes", - "GO_TO_TIMELINE": "Ir a Línea de Tiempo", - "GO_TO_WORK_VIEW": "Ir a Vista de Trabajo", + "GO_TO_WORK_VIEW": "Ir a Vista de Trabajo y enfocar primera tarea", "HELP": "

Aquí puedes configurar todos los atajos de teclado.

Haz clic en la entrada de texto e introduce la combinación de teclado deseada. Pulsa enter para guardar y Escape para abortar.

Hay tres tipos de atajos:

  • Atajos globales: Cuando la aplicación se está ejecutando activará la acción desde cualquier otra aplicación.
  • Atajos a nivel de aplicación: Se activarán desde cada pantalla de la aplicación, pero no si estás editando actualmente un campo de texto.
  • Atajos a nivel de tarea: Solo se activarán si has seleccionado una tarea vía ratón o teclado y generalmente activan una acción específicamente relacionada con esa tarea.

Puedes pulsar Escape para eliminar un atajo.", "MOVE_TASK_DOWN": "Mueve la tarea hacia abajo en la lista", "MOVE_TASK_TO_BOTTOM": "Mover la tarea al final de la lista", "MOVE_TASK_TO_TOP": "Mover la tarea al principio de la lista", "MOVE_TASK_UP": "Mover la tarea hacia arriba en la lista", - "MOVE_TO_BACKLOG": "Mover la tarea al registro de trabajo", + "MOVE_TO_BACKLOG": "Mover la tarea al backlog", "MOVE_TO_REGULARS_TASKS": "Mueve la tarea a la lista de tareas de hoy", - "OPEN_PROJECT_NOTES": "Mostrar/Ocultar Notas del Proyecto", + "OPEN_PROJECT_NOTES": "Mostrar/Ocultar Notas", + "PLUGIN_SHORTCUTS": "Atajos de Plugins", "SAVE_NOTE": "Guardar nota", "SELECT_NEXT_TASK": "Seleccionar tarea siguiente", "SELECT_PREVIOUS_TASK": "Seleccionar tarea previa", @@ -1529,18 +1808,18 @@ "TASK_SHORTCUTS": "Tareas", "TASK_SHORTCUTS_INFO": "Los siguientes atajos se aplican a la tarea seleccionada actual (seleccionada a través de una pestaña o con el ratón).", "TASK_TOGGLE_DETAIL_PANEL_OPEN": "Mostrar/Ocultar información adicional de la tarea", - "TASK_TOGGLE_DONE": "Altenado realizado", + "TASK_TOGGLE_DONE": "Alternar Hecho", "TITLE": "Atajos de teclado", - "TOGGLE_BACKLOG": "Mostrar/Ocultar registro de la tarea", + "TOGGLE_BACKLOG": "Mostrar/Ocultar backlog de la tarea", "TOGGLE_BOOKMARKS": "Mostrar/Ocultar barra de marcadores", "TOGGLE_ISSUE_PANEL": "Mostrar/Ocultar panel de incidencias", "TOGGLE_PLAY": "Iniciar/Detener tarea", - "TOGGLE_SIDE_NAV": "Mostrar y enfocar / Ocultar Sidenav", - "TOGGLE_TASK_VIEW_CUSTOMIZER_PANEL": "Alternar panel de filtro/grupo/ordenar", + "TOGGLE_SIDE_NAV": "Enfocar Sidenav", + "TOGGLE_TASK_VIEW_CUSTOMIZER_PANEL": "Alternar panel de Filtro/Grupo/Ordenar", "TRIGGER_SYNC": "Sincronización de disparo (si está configurada)", - "ZOOM_DEFAULT": "Valor predeterminado de zoom (solo para escritorio)", - "ZOOM_IN": "Acercar (solo para escritorio)", - "ZOOM_OUT": "Alejar (solo para escritorio)" + "ZOOM_DEFAULT": "Zoom predeterminado (solo escritorio)", + "ZOOM_IN": "Acercar (solo escritorio)", + "ZOOM_OUT": "Alejar (solo escritorio)" }, "LANG": { "AR": "عربى", @@ -1555,38 +1834,67 @@ "IT": "Italiano", "JA": "Japonés", "KO": "Coreano", - "LABEL": "Por favor selecciona un idioma", + "LABEL": "Idioma", "NB": "Noruego Bokmål", "NL": "Holandés", "PL": "Polaco", "PT": "Portugués", + "PT_BR": "Portugués (Brasil)", "RU": "Ruso", "SK": "Eslovaco", - "TITLE": "Idioma", + "TITLE": "Localización", "TR": "Turco", - "ZH": "Chino Simplificado", - "ZH_TW": "Chino Tradicional" + "UK": "Ucraniano", + "ZH": "Chino (Simplificado)", + "ZH_TW": "Chino (Tradicional)", + "TIME_LOCALE": "Configuración regional de formato de fecha y hora", + "TIME_LOCALE_DESCRIPTION": "NOTA: ahora estas opciones no pueden cambiar el formato de entrada de hora (12/24 horas) porque está controlado por tu SO", + "TIME_LOCALE_AUTO": "Predeterminado del sistema", + "TIME_LOCALE_EN_US": "Inglés (EE.UU.): 12 horas AM/PM, MM/DD/AAAA", + "TIME_LOCALE_EN_GB": "Inglés (Reino Unido): 24 horas, DD/MM/AAAA", + "TIME_LOCALE_TR_TR": "Turco: 24 horas, DD.MM.AAAA", + "TIME_LOCALE_DE_DE": "Alemán: 24 horas, DD.MM.AAAA", + "TIME_LOCALE_FR_FR": "Francés: 24 horas, DD/MM/AAAA", + "TIME_LOCALE_ES_ES": "Español: 24 horas, DD/MM/AAAA", + "TIME_LOCALE_IT_IT": "Italiano: 24 horas, DD/MM/AAAA", + "TIME_LOCALE_PT_BR": "Portugués (Brasil): 24 horas, DD/MM/AAAA", + "TIME_LOCALE_RU_RU": "Ruso: 24 horas, DD.MM.AAAA", + "TIME_LOCALE_ZH_CN": "Chino (Simplificado): 24 horas, AAAA/MM/DD", + "TIME_LOCALE_JA_JP": "Japonés: 24 horas, AAAA/MM/DD", + "TIME_LOCALE_KO_KR": "Coreano: 12 horas AM/PM, AAAA. MM. DD" }, "MISC": { + "DARK_MODE": "Modo Oscuro", + "DARK_MODE_ARIA_LABEL": "Selección de modo oscuro", + "DARK_MODE_DARK": "Oscuro", + "DARK_MODE_LIGHT": "Claro", + "DARK_MODE_SYSTEM": "Sistema", "DEFAULT_PROJECT": "Proyecto predeterminado a usar para tareas si no se especifica ninguno", + "DEFAULT_START_PAGE": "Página de inicio predeterminada", "FIRST_DAY_OF_WEEK": "Primer día de la semana", - "START_OF_NEXT_DAY": "Hora de inicio del siguiente día", - "START_OF_NEXT_DAY_HINT": "desde cuándo (en hora) quieres contar que ha comenzado el siguiente día. predeterminado es medianoche que es 0.", "HELP": "

¿No ves Notificaciones de Escritorio? Para windows podrías querer comprobar Sistema > Notificaciones y acciones y comprobar si las notificaciones requeridas han sido habilitadas.

", - "IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Añadir automáticamente la etiqueta de hoy a las tareas completadas", + "IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Añadir automáticamente la etiqueta de hoy a las tareas trabajadas", "IS_AUTO_MARK_PARENT_AS_DONE": "Marcar la tarea principal como realizada, cuando se completen todas las subtareas", - "IS_AUTO_START_NEXT_TASK": "Iniciar seguimiento de siguiente tarea al marcar la actual como hecha", "IS_CONFIRM_BEFORE_EXIT": "Confirmar antes de salir de la aplicación", "IS_CONFIRM_BEFORE_EXIT_WITHOUT_FINISH_DAY": "Confirmar antes de salir de la aplicación sin finalizar el día primero", "IS_DARK_MODE": "Modo Oscuro", "IS_DISABLE_ANIMATIONS": "Deshabilitar todas las animaciones", + "IS_DISABLE_CELEBRATION": "Deshabilitar celebración en resumen diario", "IS_HIDE_NAV": "Ocultar navegación hasta que se pase el ratón por el encabezado principal (solo escritorio)", "IS_MINIMIZE_TO_TRAY": "Minimizar a la bandeja (solo escritorio)", - "IS_SHOW_TIP_LONGER": "Mostrar consejo de productividad en la aplicación iniciar un poco más de tiempo", + "IS_OVERLAY_INDICATOR_ENABLED": "Habilitar ventana de indicador de superposición (escritorio linux/gnome)", + "IS_SHOW_TIP_LONGER": "Mostrar consejo de productividad al inicio de la aplicación un poco más de tiempo", "IS_TRAY_SHOW_CURRENT_COUNTDOWN": "Mostrar cuenta regresiva actual en la bandeja / menú de estado (solo mac de escritorio)", - "IS_TRAY_SHOW_CURRENT_TASK": "Mostrar la tarea actual en el menú de bandeja / estado (solo escritorio)", - "IS_TURN_OFF_MARKDOWN": "Desactivar la sintaxis markdown para las notas", + "IS_TRAY_SHOW_CURRENT_TASK": "Mostrar la tarea actual en el menú de bandeja / estado (solo escritorio mac/windows)", + "IS_TURN_OFF_MARKDOWN": "Desactivar análisis de markdown para notas de tarea", + "IS_USE_CUSTOM_WINDOW_TITLE_BAR": "Usar barra de título personalizada (solo Windows/Linux)", + "IS_USE_CUSTOM_WINDOW_TITLE_BAR_HINT": "Requiere reinicio para surtir efecto", + "START_OF_NEXT_DAY": "Hora de inicio del siguiente día", + "START_OF_NEXT_DAY_HINT": "desde cuándo (en hora) quieres contar que ha comenzado el siguiente día. predeterminado es medianoche que es 0.", "TASK_NOTES_TPL": "Plantilla de descripción de tarea", + "THEME": "Tema", + "THEME_EXPERIMENTAL": "Tema (experimental)", + "THEME_SELECT_LABEL": "Seleccionar Tema", "TITLE": "Ajustes Varios" }, "POMODORO": { @@ -1597,32 +1905,34 @@ }, "REMINDER": { "COUNTDOWN_DURATION": "Mostrar banner X antes del recordatorio real", + "DEFAULT_TASK_REMIND_OPTION": "Opción de recordatorio predeterminada seleccionada al crear tareas", "IS_COUNTDOWN_BANNER_ENABLED": "Mostrar banner de cuenta atrás antes del vencimiento de los recordatorios", - "TITLE": "Recordatorios" + "TITLE": "Recordatorios", + "DISABLE_REMINDERS": "Deshabilitar todos los recordatorios" }, "SCHEDULE": { - "HELP": "La función de línea de tiempo debe proporcionarle una descripción general rápida de cómo se desarrollan sus tareas planificadas a lo largo del tiempo. Puede encontrarlo en el menú de la izquierda en \"Cronología\".", - "L_IS_LUNCH_BREAK_ENABLED": "Activar el descanso de la comida", - "L_IS_WORK_START_END_ENABLED": "Limite el flujo de tareas no programadas a tiempos de trabajo específicos", - "L_LUNCH_BREAK_END": "Fin del descanso de la comida", - "L_LUNCH_BREAK_START": "Inicio del descanso de la comida", - "L_WORK_END": "Fin del día laboral", - "L_WORK_START": "Inicio del día laboral", - "LUNCH_BREAK_START_END_DESCRIPTION": "p.ej. 13:00", + "HELP": "La característica de línea de tiempo debería proporcionarte una visión general rápida sobre cómo se desarrollan tus tareas planificadas a lo largo del tiempo. Puedes encontrarla en el menú de la izquierda bajo 'Cronograma'. ", + "L_IS_LUNCH_BREAK_ENABLED": "Habilitar pausa para comer", + "L_IS_WORK_START_END_ENABLED": "Limitar flujo de tareas no programadas a horas de trabajo específicas", + "L_LUNCH_BREAK_END": "Fin pausa comida", + "L_LUNCH_BREAK_START": "Inicio pausa comida", + "L_WORK_END": "Fin del Día de Trabajo", + "L_WORK_START": "Inicio del Día de Trabajo", + "LUNCH_BREAK_START_END_DESCRIPTION": "p. ej. 13:00", "MONTH": "Mes", - "TITLE": "Línea de tiempo", + "TITLE": "Cronograma", "WEEK": "Semana", - "WORK_START_END_DESCRIPTION": "p.ej. 17:00" + "WORK_START_END_DESCRIPTION": "p. ej. 17:00" }, "SHORT_SYNTAX": { - "HELP": "

Aquí puede controlar las opciones de sintaxis corta al crear una tarea

", - "IS_ENABLE_DUE": "Habilitar la sintaxis corta debida (@)", - "IS_ENABLE_PROJECT": "Activar la sintaxis corta del proyecto (+)", - "IS_ENABLE_TAG": "Habilitar sintaxis corta de etiquetas (#)", - "TITLE": "Sintaxis corta" + "HELP": "

Aquí puedes controlar las opciones de sintaxis corta al crear una tarea

", + "IS_ENABLE_DUE": "Habilitar la sintaxis corta debida (@)", + "IS_ENABLE_PROJECT": "Activar la sintaxis corta del proyecto (+)", + "IS_ENABLE_TAG": "Habilitar sintaxis corta de etiquetas (#)", + "TITLE": "Sintaxis Corta" }, "SOUND": { - "BREAK_REMINDER_SOUND": "Sonido de recordatorio de hacer un descanso", + "BREAK_REMINDER_SOUND": "Sonido de recordatorio de tomar un descanso", "DONE_SOUND": "Sonido de tarea marcada como hecha", "IS_INCREASE_DONE_PITCH": "Aumentar tono por cada tarea hecha", "TITLE": "Sonido", @@ -1631,36 +1941,35 @@ }, "TAKE_A_BREAK": { "ADD_NEW_IMG": "Añadir imagen motivacional", - "HELP": "

Te permite configurar un recordatorio recurrente cuando has trabajado durante una cantidad de tiempo especificada sin hacer un descanso.

Puedes modificar el mensaje mostrado. ${duration} será reemplazado con el tiempo dedicado sin un descanso.

", - "IS_ENABLED": "Habilitar recordatorio de hacer un descanso", - "IS_FOCUS_WINDOW": "Enfocar ventana de la app cuando el recordatorio esté activo (solo escritorio)", - "IS_LOCK_SCREEN": "Bloquear pantalla cuando venza un descanso (solo escritorio)", - "IS_FULL_SCREEN_BLOCKER": "Mostrar mensaje en ventana de pantalla completa (solo escritorio)", "FULL_SCREEN_BLOCKER_DURATION": "Duración para mostrar la ventana de pantalla completa (solo escritorio)", - "MESSAGE": "Mensaje de hacer un descanso", - "MIN_WORKING_TIME": "Activar notificación de hacer un descanso después de X trabajando sin uno", + "HELP": "

Te permite configurar un recordatorio recurrente cuando has trabajado durante una cantidad de tiempo especificada sin hacer un descanso.

Puedes modificar el mensaje mostrado. ${duration} será reemplazado con el tiempo dedicado sin un descanso.

", + "IS_ENABLED": "Habilitar recordatorio de tomar un descanso", + "IS_FOCUS_WINDOW": "Enfocar ventana de la app cuando el recordatorio esté activo (solo escritorio)", + "IS_FULL_SCREEN_BLOCKER": "Mostrar mensaje en ventana de pantalla completa (solo escritorio)", + "IS_LOCK_SCREEN": "Bloquear pantalla cuando venza un descanso (solo escritorio)", + "MESSAGE": "Mensaje de tomar un descanso", + "MIN_WORKING_TIME": "Activar notificación de tomar un descanso después de X trabajando sin uno", "MOTIVATIONAL_IMGS": "Imágenes motivacionales (usar urls web)", "NOTIFICATION_TITLE": "¡Tómate un descanso!", - "SNOOZE_TIME": "Tiempo de posponer cuando se pide hacer un descanso", + "SNOOZE_TIME": "Tiempo de posponer cuando se pide tomar un descanso", "TITLE": "Recordatorio de Descanso" }, - "APP_FEATURES": { - "HELP": "Habilita o deshabilita funciones específicas de la aplicación en la interfaz.", - "TITLE": "Funciones de la aplicación", - "TIME_TRACKING": "Seguimiento de tiempo (Cronómetro)", - "FOCUS_MODE": "Modo concentración", - "SCHEDULE": "Horario", - "PLANNER": "Planificador", - "BOARDS": "Tableros", - "SCHEDULE_DAY_PANEL": "Panel de horario diario", - "ISSUES_PANEL": "Panel de incidencias", - "PROJECT_NOTES": "Notas del proyecto", - "SYNC_BUTTON": "Botón de sincronización", - "DONATE_PAGE": "Página de donaciones", - "USER_PROFILES": "Perfiles de usuario (Experimental)", - "USER_PROFILES_HINT": "Permite crear y alternar entre diferentes perfiles de usuario, cada uno con configuraciones, tareas y datos de sincronización independientes. El botón de gestión de perfiles aparecerá en la esquina superior derecha cuando esté habilitado. Nota: Deshabilitar esta función ocultará la interfaz pero conservará los datos de tu perfil (Función experimental, sin garantías. Asegúrate de tener una copia de seguridad)." - } - }, + "APP_FEATURES": { + "HELP": "Habilita o deshabilita funciones específicas de la aplicación en la interfaz.", + "TITLE": "Funciones de la aplicación", + "TIME_TRACKING": "Seguimiento de tiempo (Cronómetro)", + "FOCUS_MODE": "Modo concentración", + "SCHEDULE": "Horario", + "PLANNER": "Planificador", + "BOARDS": "Tableros", + "SCHEDULE_DAY_PANEL": "Panel de horario diario", + "ISSUES_PANEL": "Panel de incidencias", + "PROJECT_NOTES": "Notas del proyecto", + "SYNC_BUTTON": "Botón de sincronización", + "DONATE_PAGE": "Página de donaciones", + "USER_PROFILES": "Perfiles de usuario (Experimental)", + "USER_PROFILES_HINT": "Permite crear y alternar entre diferentes perfiles de usuario, cada uno con configuraciones, tareas y datos de sincronización independientes. El botón de gestión de perfiles aparecerá en la esquina superior derecha cuando esté habilitado. Nota: Deshabilitar esta función ocultará la interfaz pero conservará los datos de tu perfil (Función experimental, sin garantías. Asegúrate de tener una copia de seguridad)." + }, "TIMELINE": { "CAL_PROVIDERS": "Proveedores de calendario (experimental y opcional)", "CAL_PROVIDERS_ADD": "Añadir fuente ical", @@ -1673,25 +1982,38 @@ "TITLE": "Línea de tiempo", "WORK_START_END_DESCRIPTION": "p. ej. 17:00" }, - "TRACKING_REMINDER": { - "HELP": "Configura un banner para mostrarse en caso de que olvidaras iniciar el seguimiento de tiempo.", - "L_IS_ENABLED": "Habilitado", - "L_IS_SHOW_ON_MOBILE": "Mostrar en app móvil", - "L_MIN_TIME": "Tiempo de espera antes de mostrar Banner", - "TITLE": "Recordatorio de Seguimiento de Tiempo" + "PAST": { + "A_DAY": "hace un día", + "A_MINUTE": "hace un minuto", + "A_MONTH": "hace un mes", + "A_YEAR": "hace un año", + "AN_HOUR": "hace una hora", + "DAYS": "hace {{count}} días", + "FEW_SECONDS": "hace unos segundos", + "HOURS": "hace {{count}} horas", + "MINUTES": "hace {{count}} minutos", + "MONTHS": "hace {{count}} meses", + "YEARS": "hace {{count}} años" } }, "GLOBAL_SNACK": { "COPY_TO_CLIPPBOARD": "Copiado al portapapeles", - "DUPLICATE_PROJECT_ERROR": "No se pudo duplicar el proyecto", - "DUPLICATE_PROJECT_SUCCESS": "Proyecto duplicado", - "ERR_COMPRESSION": "Error de la interfaz de compresión", + "DUPLICATE_PROJECT_ERROR": "El proyecto no pudo ser duplicado", + "DUPLICATE_PROJECT_SUCCESS": "Proyecto duplicado exitosamente", + "ERR_COMPRESSION": "Error para la interfaz de compresión", "FILE_DOWNLOADED": "{{fileName}} descargado", "FILE_DOWNLOADED_BTN": "Abrir carpeta", + "NAVIGATE_TO_TASK_ERR": "No se pudo enfocar en la tarea. ¿La eliminaste?", + "NO_TASKS_TO_COPY": "No hay tareas para copiar", + "NO_TASKS_TO_UNPLAN": "No hay tareas para desplanificar", "PERSISTENCE_DISALLOWED": "Los datos no persistirán permanentemente. ¡Sé consciente de que esto puede llevar a la pérdida de datos!", "PERSISTENCE_ERROR": "Error al solicitar la persistencia de datos: {{err}}", "RUNNING_X": "Ejecutando \"{{str}}\".", - "SHORTCUT_WARN_OPEN_BOOKMARKS_FROM_TAG": "{{keyCombo}} presionado, pero el atajo de abrir marcadores solo está disponible cuando se está en contexto de proyecto." + "SHARE_FAILED": "Error al compartir. Por favor copia manualmente.", + "SHARE_FAILED_FALLBACK": "Error al compartir. Copiado al portapapeles en su lugar.", + "SHARE_UNAVAILABLE_FALLBACK": "Copiado al portapapeles.", + "SHORTCUT_WARN_OPEN_BOOKMARKS_FROM_TAG": "{{keyCombo}} presionado, pero el atajo de abrir marcadores solo está disponible cuando se está en contexto de proyecto.", + "UNPLANNED_TODAY_TASKS": "Desplanificadas todas las tareas de Hoy" }, "GLOBAL": { "COPY_SUFFIX": " (copia)" @@ -1704,146 +2026,240 @@ "DBX_UPLOAD": "Dropbox: Subir archivo...", "GITHUB_LOAD_ISSUE": "GitHub: Cargar datos de incidencia...", "JIRA_LOAD_ISSUE": "Jira: Cargar datos de incidencia...", + "SYNC": "Sincronizando datos...", "UNKNOWN": "Cargando datos remotos", "WEB_DAV_DOWNLOAD": "WebDAV: Descargando datos...", "WEB_DAV_UPLOAD": "WebDAV: Subiendo datos..." }, "MH": { "ADD_NEW_TASK": "Añadir nueva Tarea", + "ALL_PLANNED_LIST": "Planificado / Repetir", + "BOARDS": "Tableros", + "COPY_TASK_LIST_MARKDOWN": "Copiar al Portapapeles", "CREATE_PROJECT": "Crear Proyecto", - "CREATE_TAG": "Crear Etiqueta", - "DELETE_TAG": "Eliminar Etiqueta", "DUPLICATE_PROJECT": "Duplicar", - "GO_TO_TASK_LIST": "Ir a lista de tareas", - "MANAGE_PROJECTS": "Gestionar Proyectos", + "CREATE_TAG": "Crear Etiqueta", + "DELETE_PROJECT": "Eliminar Proyecto", + "DELETE_TAG": "Eliminar Etiqueta", + "DONATE": "Apóyanos", + "ENTER_FOCUS_MODE": "Entrar en Modo Enfoque", + "GO_TO_TASK_LIST": "Ir a la lista de tareas", + "HELP": "Ayuda", + "HM": { + "CALENDARS": "Tutorial: Conectar Calendarios", + "CONTRIBUTE": "Contribuir", + "GET_HELP_ONLINE": "Obtener ayuda online", + "KEYBOARD": "Tutorial: Teclado Avanzado", + "REDDIT_COMMUNITY": "Comunidad Reddit", + "REPORT_A_PROBLEM": "Reportar un problema", + "START_WELCOME": "Iniciar Tour de Bienvenida", + "SYNC": "Tutorial: Configurar Sync" + }, "METRICS": "Métricas", + "NO_PROJECT_INFO": "No hay proyectos disponibles. Puedes crear un nuevo proyecto haciendo clic en el botón \"Crear Proyecto\".", + "NO_TAG_INFO": "Actualmente no hay etiquetas. Puedes añadir etiquetas introduciendo `#tuNombreEtiqueta` al añadir o editar tareas.", "NOTES": "Notas", - "NOTES_PANEL_INFO": "Las notas solo se pueden mostrar desde las vistas de línea de tiempo y lista de tareas regular.", + "NOTES_PANEL_INFO": "Las notas solo se pueden mostrar desde el cronograma y las vistas regulares de lista de tareas.", + "PLANNER": "Planificador", "PROCRASTINATE": "Procrastinar", - "PROJECTS": "Proyectos", "PROJECT_MENU": "Menú de Proyecto", "PROJECT_SETTINGS": "Ajustes del Proyecto", + "PROJECTS": "Proyectos", "QUICK_HISTORY": "Historial Rápido", - "SCHEDULED": "Programado", + "SCHEDULE": "Cronograma", + "SEARCH": "Buscar", "SETTINGS": "Ajustes", + "SHARE_TASK_LIST_MARKDOWN": "Compartir Lista de Tareas", "SHOW_SEARCH_BAR": "Mostrar Barra de Búsqueda", + "SIDE_PANEL_MENU": "Menú del Panel Lateral", "TAGS": "Etiquetas", - "TASKS": "Tareas", "TASK_LIST": "Lista de Tareas", + "TASKS": "Tareas", "TOGGLE_SHOW_BOOKMARKS": "Mostrar/Ocultar Marcadores", - "TOGGLE_SHOW_ISSUE_PANEL": "Mostrar/Ocultar Panel de Problemas", + "TOGGLE_SHOW_ISSUE_PANEL": "Mostrar/Ocultar Panel de Incidencias", "TOGGLE_SHOW_NOTES": "Mostrar/Ocultar Notas del Proyecto", - "TOGGLE_TRACK_TIME": "Iniciar/Parar seguimiento de tiempo", - "TRIGGER_SYNC": "Activar sincronización manualmente", + "TOGGLE_TRACK_TIME": "Iniciar/Detener seguimiento de tiempo", + "TRIGGER_SYNC": "¡Sincronizar!", + "UNPLAN_ALL_TASKS": "Desplanificar todas las tareas", "WORKLOG": "Registro de trabajo" }, "MIGRATE": { - "C_DOWNLOAD_BACKUP": "¿Desea descargar una copia de seguridad de sus datos heredados (se puede utilizar con versiones anteriores de Super Productivity)?", - "DETECTED_LEGACY": "Datos heredados detectados. ¡Lo migraremos por ti!", + "C_DOWNLOAD_BACKUP": "¿Quieres descargar una copia de seguridad de tus datos heredados (se puede usar con versiones anteriores de Super Productivity)?", + "DETECTED_LEGACY": "Datos heredados detectados. ¡Los migraremos por ti!", "E_MIGRATION_FAILED": "¡Falló la migración! con Error:", - "E_RESTART_FAILED": "Error de reinicio automático. ¡Reinicie la aplicación manualmente!", - "SUCCESS": "¡Migración ya está hecha! Reiniciando la aplicación ahora..." + "E_RESTART_FAILED": "El reinicio automático falló. ¡Por favor reinicia la aplicación manualmente!", + "SUCCESS": "¡Migración terminada! Reiniciando aplicación ahora..." }, "PDS": { - "BACK": "¡Espera, olvidé algo!", + "ADD_TASKS_FROM_TODAY": "Añadir tareas de hoy", + "ARCHIVED_TASKS": { + "PLURAL": "{{count}} tareas principales hechas han sido archivadas", + "SINGULAR": "{{count}} tarea principal hecha ha sido archivada" + }, "BREAK_LABEL": "Descansos (nº / tiempo)", - "CELEBRATE": "¡Tómate un momento para celebrarlo!", + "CELEBRATE": "¡Tómate un momento para celebrarlo!", "CLEAR_ALL_CONTINUE": "Limpiar todo lo hecho y continuar", "D_CONFIRM_APP_CLOSE": { "CANCEL": "No, solo limpia las tareas", - "MSG": "Has terminado por hoy. ¡Hora de irse a casa!", - "OK": "¡A la orden! ¡Apagar!" + "MSG": "¿Salir de la aplicación?", + "OK": "Salir" }, "ESTIMATE_TOTAL": "Estimación total:", - "EVALUATION": "Evaluación", + "EVALUATE_DAY": "Evaluar Día", "EXPORT_TASK_LIST": "Exportar Lista de Tareas", + "END_OF_DAYS_RITUALS_PLACEHOLDER": "Puedes usar este espacio para escribir tus propios rituales de fin de día de los que quieras que se te recuerde.", + "FOCUS_SUMMARY": "Sesiones de Enfoque", "NO_TASKS": "No hay tareas para este día", - "PLAN": "Plan", + "PLAN_TOMORROW": "Planificar", + "REVIEW_TASKS": "Revisar", + "ROUND_5M": "Redondear todas las tareas a 5 minutos", "ROUND_15M": "Redondear todas las tareas a 15 minutos", "ROUND_30M": "Redondear todas las tareas a 30 minutos", - "ROUND_5M": "Redondear todas las tareas a 5 minutos", "ROUND_TIME_SPENT": "Redondear Tiempo Dedicado", "ROUND_TIME_SPENT_TITLE": "Redondear tiempo dedicado en todas las tareas. ¡Ten cuidado! ¡No puedes deshacer esto!", "ROUND_TIME_WARNING": "!!! Ten cuidado esto no se puede deshacer !!!", + "ROUND_UP_5M": "Redondear HACIA ARRIBA todas las tareas a 5 minutos", "ROUND_UP_15M": "Redondear HACIA ARRIBA todas las tareas a 15 minutos", "ROUND_UP_30M": "Redondear HACIA ARRIBA todas las tareas a 30 minutos", - "ROUND_UP_5M": "Redondear HACIA ARRIBA todas las tareas a 5 minutos", - "SAVE_AND_GO_HOME": "Guardar e irse a casa", - "SAVE_AND_GO_HOME_TOOLTIP": "Mover todas las tareas hechas al Archivo (registro de trabajo) y opcionalmente sincronizar todos los datos y cerrar la app.", + "SAVE_AND_GO_HOME": "Finalizar el día", + "SAVE_AND_GO_HOME_TOOLTIP": "Mover todas las tareas hechas al archivo (registro de trabajo) y opcionalmente sincronizar todos los datos y cerrar la app.", "START_END": "Inicio – Fin", "SUMMARY_FOR": "Resumen Diario para {{dayStr}}", "TASKS_COMPLETED": "Tareas Completadas", - "TASK_LIST": "Lista de Tareas", "TIME_SPENT_AND_ESTIMATE_LABEL": "Tiempo Dedicado / Estimado", "TIME_SPENT_ESTIMATE_TITLE": "Tiempo Dedicado: Tiempo total dedicado hoy. Tareas archivadas no incluidas. – Tiempo Estimado: Tiempo estimado para tareas trabajadas hoy menos el tiempo ya dedicado a ellas en otros días.", - "TODAY": "Hoy", + "TIME_SPENT_TODAY_BY_TAG": "Tiempo Dedicado hoy por Etiqueta", "WEEK": "Semana" }, "PLUGINS": { + "ACTION_TYPE_NOT_ALLOWED": "El tipo de acción '{{type}}' no está permitido", + "ALREADY_INITIALIZED": "El plugin ya está inicializado", "CANCEL": "Cancelar", + "CAPABILITIES": { + "ACCESS_FILES": "Acceder y modificar archivos en tu sistema", + "RUN_COMMANDS": "Ejecutar comandos del sistema", + "USE_NODE_APIS": "Usar APIs y módulos de Node.js" + }, + "CHOOSE_PLUGIN_FILE": "Elegir Archivo de Plugin", + "CLEAR_PLUGIN_CACHE": "Borrar Caché de Plugin", + "CODE_TOO_LARGE": "El código del plugin es demasiado grande (máx {{maxSize}}MB)", "CONFIGURATION": "Configuración", "CONFIGURE": "Configurar", + "CONFIRM_REMOVE": "¿Estás seguro de que quieres eliminar el plugin \"{{name}}\"?", "DISABLED": "Deshabilitado", + "ELECTRON_API_NOT_AVAILABLE": "La API de Electron no está disponible", "ENABLED": "Habilitado", "ERROR": "Error", + "ERROR_LOADING_PLUGIN": "Error cargando plugin", + "EXPERIMENTAL_WARNING": "El sistema de plugins está en una etapa experimental y debe usarse con extrema precaución.", + "EXPERIMENTAL_WARNING_TITLE": "Característica Experimental - Advertencia de Seguridad", + "FAILED_TO_CLEAR_CACHE": "Error al borrar caché de plugin", + "FAILED_TO_EXECUTE_SCRIPT": "Error al ejecutar script de Node.js", + "FAILED_TO_EXTRACT_ZIP": "Error al extraer archivo ZIP del plugin", + "FAILED_TO_INSTALL": "Error al instalar plugin", + "FAILED_TO_LOAD": "Error al cargar plugin", + "FAILED_TO_LOAD_CONFIG": "Error al cargar configuración del plugin", + "FAILED_TO_REMOVE": "Error al eliminar plugin", + "FAILED_TO_SAVE_CONFIG": "Error al guardar configuración del plugin", + "FILE_TOO_LARGE": "Archivo demasiado grande ({{fileSize}}MB). El tamaño máximo es {{maxSize}}MB", "GO_BACK": "Volver", + "GRANT_PERMISSION": "Conceder Permiso", + "HOOKS": "Hooks", + "ID": "ID:", + "INDEX_HTML_NOT_LOADED": "index.html del plugin no cargado", + "INSTALL_PLUGIN": "Instalar Plugin", + "INSTALL_WARNING": "Antes de instalar un plugin, asegúrate de confiar en su fuente y entender los permisos que solicita.", + "INSTALLING": "Instalando...", + "LOADING_INTERFACE": "Cargando interfaz del plugin...", "LOADING_PLUGIN": "Cargando...", - "OK": "Ok", + "MANIFEST_NOT_FOUND": "Manifiesto del plugin (manifest.json) no encontrado en el archivo ZIP", + "MANIFEST_TOO_LARGE": "El manifiesto del plugin es demasiado grande (máx {{maxSize}}KB)", + "MENU_ENTRY_ICON_STRING": "El icono de entrada de menú debe ser una cadena", + "MENU_ENTRY_LABEL_REQUIRED": "La etiqueta de entrada de menú es obligatoria", + "MENU_ENTRY_ONCLICK_REQUIRED": "El manejador onClick de entrada de menú es obligatorio", + "MIN_VERSION": "Versión Mín.:", + "NO_ADDITIONAL_INFO": "No hay información adicional disponible", + "NO_CONTENT_PROVIDED": "No se proporcionó contenido", + "NO_PLUGIN_CONTEXT_ACTION": "El plugin no tiene permiso para realizar acciones", + "NO_PLUGIN_CONTEXT_HEADER_BUTTON": "El plugin no tiene permiso para añadir botones de cabecera", + "NO_PLUGIN_CONTEXT_LOADING": "El plugin no tiene contexto para carga de datos", + "NO_PLUGIN_CONTEXT_MENU_ENTRY": "El plugin no tiene permiso para añadir entradas de menú", + "NO_PLUGIN_CONTEXT_NODE": "El plugin no tiene permiso para ejecutar código Node.js", + "NO_PLUGIN_CONTEXT_PERSISTENCE": "El plugin no tiene permiso para persistir datos", + "NO_PLUGIN_CONTEXT_SHORTCUT": "El plugin no tiene permiso para registrar atajos", + "NO_PLUGIN_CONTEXT_SIDE_PANEL": "El plugin no tiene permiso para añadir botones de panel lateral", + "NO_PLUGIN_CONTEXT_SYNC": "El plugin no tiene permiso para usar sincronización", + "NO_PLUGIN_ID_PROVIDED_FOR_HTML": "No se proporcionó ID de plugin para contenido HTML", + "NO_PLUGIN_MANIFEST_NODE": "El manifiesto del plugin no incluye módulo Node.js", + "NODE_EXECUTION_REQUIRED": "Este plugin requiere ejecución de Node.js que solo está disponible en la aplicación de escritorio.", + "NODE_ONLY_DESKTOP": "La ejecución de Node.js solo está disponible en la aplicación de escritorio", + "OK": "OK", + "PARENT_TASK_DOES_NOT_EXIST": "La tarea padre no existe", + "PARENT_TASK_NOT_FOUND": "Tarea padre no encontrada en contexto '{{contextId}}'", + "PERMISSIONS": "Permisos", + "PLEASE_SELECT_ZIP_FILE": "Por favor selecciona un archivo ZIP", + "PLUGIN_DIALOG_TITLE": "Mensaje del Plugin", + "PLUGIN_DOES_NOT_SUPPORT_IFRAME": "Este plugin no soporta interfaz iframe", + "PLUGIN_ID_NOT_PROVIDED": "ID de plugin no proporcionado", + "PLUGIN_JS_NOT_FOUND": "Archivo JavaScript del plugin (plugin.js) no encontrado en archivo ZIP", + "PLUGIN_NOT_FOUND": "Plugin no encontrado", + "PLUGIN_SYSTEM_FAILED_INIT": "El sistema de plugins falló al inicializar", + "PROJECT_DOES_NOT_EXIST": "El proyecto no existe", + "PROJECT_NOT_FOUND": "Proyecto '{{contextId}}' no encontrado", + "RECOMMENDATION": "Instala solo plugins de fuentes de confianza y revisa su código si es posible. Siempre haz una copia de seguridad de tus datos antes de instalar nuevos plugins.", + "REMEMBER_CHOICE": "Recordar mi elección para este plugin", "REMOVE": "Eliminar", + "RISK_DATA_ACCESS": "Los plugins pueden leer, modificar y eliminar TODAS tus tareas, proyectos y datos personales", + "RISK_MALICIOUS_CODE": "Los plugins maliciosos podrían robar información sensible o corromper tus datos", + "RISK_PERFORMANCE": "Los plugins mal escritos pueden causar problemas de rendimiento o bloqueos", + "RISK_SYSTEM_ACCESS": "Los plugins de escritorio con permisos de Node.js pueden ejecutar comandos del sistema", + "SECURITY_WARNING": "Los plugins tienen acceso significativo a tus datos y sistema. Instalar plugins no confiables plantea serios riesgos de seguridad:", + "SIDE_PANEL_LABEL_REQUIRED": "La etiqueta del botón del panel lateral es obligatoria", + "SIDE_PANEL_ONCLICK_REQUIRED": "El manejador onClick del botón del panel lateral es obligatorio", + "SYSTEM_ACCESS_REQUEST_DESC": "Este plugin está solicitando permiso para ejecutar código Node.js en tu sistema. Esto le permitirá:", + "SYSTEM_ACCESS_REQUEST_TITLE": "Solicitud de Acceso al Sistema", + "TAGS_DO_NOT_EXIST": "Una o más etiquetas no existen", + "TASK_NOT_FOUND": "Tarea con ID '{{taskId}}' no encontrada", + "TASKS_NOT_IN_PROJECT": "Una o más tareas no están en el proyecto '{{contextId}}'", + "TASKS_NOT_SUBTASKS": "Una o más tareas no son subtareas de '{{contextId}}'", + "TOGGLE_PLUGIN": "Alternar {{pluginName}}", + "TRUST_WARNING": "Solo concede permiso si confías en este plugin", + "TYPE": "Tipo: {{type}}", + "UNABLE_TO_PERSIST_DATA": "No se pueden persistir datos en el almacenamiento del plugin", + "UNKNOWN_ERROR": "Ocurrió un error desconocido", + "UPLOAD_PLUGIN_INSTRUCTION": "Sube un archivo ZIP de plugin para instalarlo:", + "USER_DECLINED_NODE_PERMISSION": "El usuario rechazó el permiso de ejecución de Node.js", "VALIDATION_FAILED": "Validación fallida" }, "PM": { "TITLE": "Métricas del Proyecto" }, - "PP": { - "ARCHIVED_PROJECTS": "Proyectos Archivados", - "ARCHIVE_PROJECT": "Archivar Proyecto", - "CLICK_TO_HIDE": "Clic para ocultar proyecto del menú", - "CLICK_TO_SHOW": "Clic para mostrar proyecto en menú", - "DELETE_PROJECT": "Eliminar Proyecto", - "D_CONFIRM_ARCHIVE": { - "MSG": "¿Estás seguro de que quieres archivar este proyecto? Los Proyectos Archivados no se mostrarán en el menú nunca más y no puedes acceder a ninguno de sus datos sin restaurarlos. Se recomienda que hagas una copia de seguridad de tus datos antes de hacer esto.", - "OK": "Archivar" - }, - "D_CONFIRM_DELETE": { - "MSG": "¿Estás seguro de que quieres eliminar este proyecto? Se recomienda hacer una copia de seguridad de tus datos antes de hacer esto.", - "OK": "Eliminar" - }, - "D_CONFIRM_UNARCHIVE": { - "MSG": "¿Estás seguro de que quieres desarchivar este proyecto?", - "OK": "Desarchivar" - }, - "EDIT_PROJECT": "Editar Proyecto", - "EXPORT_PROJECT": "Exportar Proyecto", - "GITHUB_CONFIGURED": "Integración GitHub Configurada", - "GITLAB_CONFIGURED": "Integración GitLab Configurada", - "IMPORT_FROM_FILE": "Importar desde archivo", - "JIRA_CONFIGURED": "Integración Jira Configurada", - "OPEN_PROJECT_CONFIGURED": "Integración OpenProject Configurada", - "S_INVALID_JSON": "Json inválido para archivo de proyecto", - "TITLE": "Resumen del Proyecto", - "UN_ARCHIVE_PROJECT": "Desarchivar Proyecto" - }, "PS": { "GLOBAL_SETTINGS": "Ajustes Globales", "ISSUE_INTEGRATION": "Integración de Incidencias", + "NO_PLUGINS_INSTALLED": "No hay plugins instalados actualmente", + "PLUGINS": "Plugins", "PRIVACY_POLICY": "Política de Privacidad", "PRODUCTIVITY_HELPER": "Ayudante de Productividad", "PROJECT_SETTINGS": "Ajustes Específicos del Proyecto", "PROVIDE_FEEDBACK": "Proporcionar Feedback", + "RELOAD": "Recargar", "SYNC_EXPORT": "Sincronizar y Exportar", "TAG_SETTINGS": "Ajustes Específicos de Etiqueta", "TOGGLE_DARK_MODE": "Alternar Modo Oscuro" }, "SCHEDULE": { - "NO_SCHEDULED": "Actualmente no hay tareas programadas. Puedes programar una tarea eligiendo \"Programar Tarea\" en el menú contextual de la tarea. Para abrirlo haz clic derecho (pulsación larga en móvil) en una tarea.", + "LAST": "Último:", + "NEXT": "Siguiente", "NO_REPEATABLE_TASKS": "Actualmente no hay tareas repetidas. Puedes programar una tarea eligiendo \"Repetir Tarea\" en el panel lateral de la tarea. Para abrirlo haz clic en el icono más a la derecha que aparece al pasar el ratón por una tarea (o simplemente toca la tarea en móvil).", - "REPEATED_TASKS": "Tareas repetidas", + "NO_SCHEDULED": "Actualmente no hay tareas programadas. Puedes programar una tarea eligiendo \"Programar Tarea\" en el menú contextual de la tarea. Para abrirlo haz clic derecho (pulsación larga en móvil) en una tarea.", + "NO_SCHEDULED_TITLE": "Tareas Programadas para el Día", + "REPEATED_TASKS": "Tareas Repetidas", "SCHEDULED_TASKS": "Tareas programadas", + "SCHEDULED_TASKS_WITH_TIME": "Tareas Programadas con Hora de Inicio", "START_TASK": "Iniciar tarea y eliminar recordatorio" }, "THEMES": { - "SELECT_THEME": "Seleccionar Tema", "amber": "ámbar", "blue": "azul", "blue-grey": "azul-gris", @@ -1857,6 +2273,7 @@ "lime": "lima", "pink": "rosa", "purple": "morado", + "SELECT_THEME": "Seleccionar Tema", "teal": "verde azulado", "yellow": "amarillo" }, @@ -1865,47 +2282,53 @@ "CANCEL": "Cancelar", "CLOSE": "Cerrar", "CREATE": "Crear", - "CREATE_NEW_PROFILE": "Crear nuevo perfil", + "CREATE_NEW_PROFILE": "Crear Nuevo Perfil", "CREATED": "Creado:", - "DELETE_PROFILE": "Eliminar perfil", - "DIALOG_TITLE": "Administrar perfiles de usuario", - "EXISTING_PROFILES": "Perfiles existentes", - "EXPORT_PROFILE": "Exportar perfil", - "MANAGE_PROFILES": "Administrar perfiles...", - "OPEN_PROFILES_FOLDER": "Abrir carpeta de perfiles", - "PROFILE_NAME": "Nombre del perfil", - "PROFILE_NAME_PLACEHOLDER": "p. ej. Trabajo, Personal", + "DELETE_PROFILE": "Eliminar Perfil", + "DIALOG_TITLE": "Administrar Perfiles de Usuario", + "EXISTING_PROFILES": "Perfiles Existentes", + "EXPORT_PROFILE": "Exportar Perfil", + "MANAGE_PROFILES": "Administrar Perfiles...", + "OPEN_PROFILES_FOLDER": "Abrir Carpeta de Perfiles", + "PROFILE_NAME": "Nombre del Perfil", + "PROFILE_NAME_PLACEHOLDER": "p. ej., Trabajo, Personal", "RENAME": "Renombrar", "SAVE": "Guardar" }, "V": { "E_1TO10": "Por favor introduce un valor entre 1 y 10", "E_DATETIME": "¡El valor introducido no es una fecha y hora!", + "E_DURATION": "Por favor introduce una duración válida (p. ej. 1h)", "E_MAX": "No debería ser mayor que {{val}}", "E_MAX_LENGTH": "Debería tener máx {{val}} caracteres de largo", - "E_MIN": "Debería ser menor que {{val}}", + "E_MIN": "No debería ser menor que {{val}}", "E_MIN_LENGTH": "Debería tener al menos {{val}} caracteres de largo", "E_PATTERN": "Entrada inválida", "E_REQUIRED": "Este campo es obligatorio" }, "WW": { "ADD_MORE": "Añadir más", - "ADD_SCHEDULED_FOR_TODAY": "Añadir tareas planificadas para hoy ({{nr}})", "ADD_SCHEDULED_FOR_TOMORROW": "Añadir tareas planificadas para mañana ({{nr}})", "ADD_SOME_TASKS": "¡Añade algunas tareas para planificar tu día!", - "COMPLETED_TASKS": "Tareas Completadas", + "DONE_TASKS": "Tareas Hechas", + "DONE_TASKS_IN_ARCHIVE": "Actualmente no hay tareas hechas aquí, pero hay algunas ya archivadas.", "ESTIMATE_REMAINING": "Estimación restante:", "FINISH_DAY": "Finalizar Día", - "FINISH_DAY_TOOLTIP": "Evalúa tu día, mueve todas las tareas hechas al Archivo (opcionalmente) y/o planifica tu próximo día.", "FINISH_DAY_FOR_PROJECT": "Finalizar Día para este Proyecto", "FINISH_DAY_FOR_TAG": "Finalizar Día para esta Etiqueta", - "HELP_PROCRASTINATION": "¡Ayuda! ¡Estoy procrastinando!", - "NO_COMPLETED_TASKS": "Actualmente no hay tareas completadas", - "NO_PLANNED_TASKS": "Sin tareas planificadas", + "FINISH_DAY_TOOLTIP": "Evalúa tu día, mueve todas las tareas hechas al archivo (opcionalmente) y/o planifica tu próximo día.", + "HELP_PROCRASTINATION": "¡Ayuda, estoy procrastinando!", + "LATER_TODAY": "Más Tarde Hoy", + "MOVE_DONE_TO_ARCHIVE": "Mover hechas al archivo", + "NO_DONE_TASKS": "Actualmente no hay tareas hechas", + "NO_PLANNED_TASK_ALL_DONE": "todas las tareas hechas", + "NO_PLANNED_TASKS": "sin tareas planificadas", "READY_TO_WORK": "¡Listo para trabajar!", "RESET_BREAK_TIMER": "Reiniciar sin temporizador de descanso", "TIME_ESTIMATED": "Tiempo Estimado:", + "TODAY_REMAINING": "Restante hoy:", "WITHOUT_BREAK": "Sin descanso:", - "WORKING_TODAY": "Trabajando hoy:" + "WORKING_TODAY": "Trabajando hoy:", + "WORKING_TODAY_ARCHIVED": "Tiempo trabajado hoy en tareas archivadas" } } diff --git a/src/assets/i18n/tr.json b/src/assets/i18n/tr.json index 15ce89bce..7c4fd74aa 100644 --- a/src/assets/i18n/tr.json +++ b/src/assets/i18n/tr.json @@ -24,14 +24,14 @@ "UPDATE_MAIN_MODEL_NO_UPDATE": "Hiçbir model güncellemesi seçilmedi. Model yükseltmesini gerçekleştirmek istemiyorsanız, son sürüme geçmek zorunda olduğunuzu lütfen unutmayın.", "UPDATE_WEB_APP": "Yeni sürüm mevcut. Yeni Sürüm Yüklensin Mi?" }, + "BL": { + "NO_TASKS": "Birikmiş işler günlüğünüzde şu anda görev yok" + }, "BN": { "SHOW_ISSUE_PANEL": "Sorun Panelini Göster", "SHOW_NOTES": "Proje Notlarını Göster", "SHOW_TASK_VIEW_CUSTOMIZER_PANEL": "Filtre/Grup/Sırala Panelini Göster" }, - "BL": { - "NO_TASKS": "Birikmiş işler günlüğünüzde şu anda görev yok" - }, "CONFIRM": { "AUTO_FIX": "Verileriniz zarar görmüş görünüyor (\"{{validityError}}\"). Otomatik olarak düzeltmeyi denemek ister misiniz? Bu, kısmi veri kaybına neden olabilir.", "RELOAD_AFTER_IDB_ERROR": "Veritabanına erişilemiyor :( Olası nedenler, arka planda uygulama güncellemesi veya düşük disk alanıdır.Eğer uygulamayı linux da snap aracılığıyla yüklediyseniz, onlar bu sorunu kendi taraflarında çözene kadar haberdar olma yenilemesini 'snap set core experimental.refresh-app-awareness=true' etkinleştirmeyi isteyebilirsiniz. Uygulamayı yeniden yüklemek için Tamam'a basın (bazı platformlarda uygulamanın manuel olarak yeniden başlatılması gerekebilir).", @@ -47,11 +47,16 @@ "TOMORROW": "Yarın" }, "DIALOG_UNSPLASH_PICKER": { - "TITLE": "Unsplash'tan Arka Plan Resmi Seçin", + "BY": "tarafından", + "NO_RESULTS": "Görüntü bulunamadı. Lütfen başka arama terimi deneyin.", "SEARCH_LABEL": "Resim Ara", "SEARCH_PLACEHOLDER": "örn. doğa, dağ, soyut", - "NO_RESULTS": "Görüntü bulunamadı. Lütfen başka arama terimi deneyin.", - "BY": "tarafından" + "TITLE": "Unsplash'tan Arka Plan Resmi Seçin" + }, + "DONATE_PAGE": { + "BUTTON_TEXT": "Github Sponsorları ile Bağış Yapın", + "INTRO_1": "Super Productivity tamamen topluluk tarafından finanse edilmektedir. Takip, reklam veya veri toplama yoktur — görevleriniz cihazınızda kalır.", + "INTRO_2": "Bu yaklaşımı değerli buluyorsanız ve projenin sağlıklı ve gelişen kalmasını istiyorsanız, gönüllü bir bağış büyük takdirle karşılanır." }, "F": { "ATTACHMENT": { @@ -156,7 +161,7 @@ "CALENDARS": { "BANNER": { "ADD_AS_TASK": "Görev Olarak Ekle", - "FOCUS_TASK": "Odaklanma Görevi", + "SHOW_TASK": "Görevleri Göster", "TXT": "{{title}} {{start}}'da başlıyor!", "TXT_MULTIPLE": "{{title}} {{start}}'da başlıyor!
(ve {{nrOfOfOtherBanners}} diğer etkinlik sona eriyor)", "TXT_PAST": "{{title}} {{start}}'da başladı!", @@ -209,49 +214,69 @@ } }, "FOCUS_MODE": { + "ADD_TIME_MINUTE": "1 dakika ekle", "B": { - "SESSION_RUNNING": "Odaklanma oturumu çalışıyor", - "POMODORO_SESSION_RUNNING": "Pomodoro seansı #{{cycleNr}} çalışıyor", "BREAK_RUNNING": "Mola çalışıyor", + "END_BREAK": "Molayı Bitir", + "END_SESSION": "Oturumu Bitir", + "PAUSE": "Duraklat", "POMODORO_BREAK_RUNNING": "Mola #{{cycleNr}} çalışıyor", + "POMODORO_SESSION_RUNNING": "Pomodoro seansı #{{cycleNr}} çalışıyor", + "RESUME": "Devam Et", + "SESSION_RUNNING": "Odaklanma oturumu çalışıyor", + "START": "Başlat", "TO_FOCUS_OVERLAY": "Oturuma odaklan" }, "BACK_TO_PLANNING": "Planlamaya Geri Dön", + "BREAK_RELAX_MSG": "Rahatlamak için bir dakika ayırın", + "CLICK_TO_EDIT_DURATION": "Süreyi düzenlemek için tıklayın", + "COMPLETE_FOCUS_SESSION": "Odaklanma oturumunu tamamla", + "COMPLETE_SESSION": "Oturumu tamamla", "CONGRATS": "Bu oturumu tamamladığınız için tebrikler!", + "CONTINUE_SESSION": "Oturumu devam ettir", + "CONTINUE_TO_NEXT_SESSION": "Bir sonraki oturuma devam et", "COUNTDOWN": "Geri sayım", + "COUNTDOWN_HINT": "Saat sıfıra ulaşana kadar odaklan", + "CURRENT_SESSION_TIME_TOOLTIP": "Şu anki oturum süresi", "FINISH_TASK_AND_SELECT_NEXT": "Görevi bitirin ve sonrakini seçin", "FLOWTIME": "Akış Zamanı", + "FLOWTIME_HINT": "Sıkı zamanlayıcılar olmadan esnek hız", + "FOCUS_TIME_TOOLTIP": "Odaklanma süresi", "FOR_TASK": "görev için", "GET_READY": "Odaklanma oturumunuza hazır olun!", "GO_TO_PROCRASTINATION": "Ertelediğinizde yardım alın", "GOGOGO": "Hadi hadi hadi!!!", + "LONG_BREAK": "Uzun Mola", + "LONG_BREAK_TITLE": "Uzun Mola - Döngü {{cycle}}", "NEXT": "Sonraki", "ON": "on", "OPEN_ISSUE_IN_BROWSER": "Sorunu Tarayıcıda aç", + "PAUSE_SESSION": "Seansı Duraklat", + "PAUSE_TRACKING": "Takibi Duraklat", + "PAUSE_TRACKING_FOR_CURRENT_TASK": "Geçerli görev için takibi duraklat", "POMODORO": "Pomodoro", + "POMODORO_HINT": "Planlanmış molalarla yapılandırılmış sprintler", + "POMODORO_SESSION_COMPLETED": "Pomodoro Oturumu Tamamlandı!", + "POMODORO_SETTINGS": "Pomodoro Ayarları", "PREP_GET_MENTALLY_READY": "Odaklanmaya ve üretken olmaya zihinsel olarak hazır olun", "PREP_SIT_UPRIGHT": "Dik durun", "PREP_STRETCH": "Hafifçe esneme hareketleri yapın", + "REMOVE_TIME_MINUTE": "1 dakika çıkar", + "RESUME_SESSION": "Oturumu devam ettir", "SELECT_ANOTHER_TASK": "Başka bir Görev seç", + "SELECT_MODE": "Odaklanma modunu seçin", "SELECT_TASK": "Odaklanmak için Görevi seçin", + "SELECT_TASK_TO_FOCUS": "Odaklanmak için Görevi seçin", "SESSION_COMPLETED": "Odak Oturumu Tamamlandı!", - "POMODORO_SESSION_COMPLETED": "Pomodoro Oturumu Tamamlandı!", "SET_FOCUS_SESSION_DURATION": "Odaklanma Oturumu Süresini Ayarlayın", + "SHORT_BREAK": "Kısa Mola", + "SHORT_BREAK_TITLE": "Kısa Mola - Döngü {{cycle}}", "SHOW_HIDE_NOTES_AND_ATTACHMENTS": "Görev notlarını ve eklerini göster/gizle", + "SKIP_BREAK": "Molayı Atla", "START_FOCUS_SESSION": "Odaklanma Oturumunu Başlat", "START_NEXT_FOCUS_SESSION": "Sonraki Odak Oturumunu başlat", - "WORKED_FOR": "Şunun için çalıştın", - "BREAK_RELAX_MSG": "Rahatlamak için bir dakika ayırın", - "SKIP_BREAK": "Molayı Atla", - "CONTINUE_TO_NEXT_SESSION": "Bir sonraki oturuma devam et", - "FOCUS_TIME_TOOLTIP": "Odaklanma süresi", - "CURRENT_SESSION_TIME_TOOLTIP": "Şu anki oturum süresi", - "COMPLETE_SESSION": "Oturumu tamamla", - "CONTINUE_SESSION": "Oturumu devam ettir", - "LONG_BREAK": "Uzun Mola", - "SHORT_BREAK": "Kısa Mola", - "LONG_BREAK_TITLE": "Uzun Mola - Döngü {{cycle}}", - "SHORT_BREAK_TITLE": "Kısa Mola - Döngü {{cycle}}" + "SWITCH_TASK": "Görevi Değiştir", + "WORKED_FOR": "Şunun için çalıştın" }, "GITEA": { "DIALOG_INITIAL": { @@ -429,6 +454,11 @@ "POLLING_CHANGES": "{{issueProviderName}}: {{issuesStr}}için Anket Değişiklikleri" } }, + "ISSUE_PANEL": { + "CALENDAR_AGENDA": { + "OFFLINE_BANNER_MSG": "Önbelleğe alınmış takvim sonuçları gösteriliyor. Veriler güncel olmayabilir." + } + }, "JIRA": { "BANNER": { "BLOCK_ACCESS_MSG": "Jira: Api'nin kapatılmasını önlemek için erişim Super Productivity tarafından engellendi. Muhtemelen jira ayarlarınızı kontrol etmelisiniz!", @@ -572,6 +602,7 @@ "CHECK": "Yaptım!" }, "CMP": { + "ACTIVITY_HEATMAP": "Aktivite Isı Haritası", "AVG_BREAKS_PER_DAY": "Ort. günlük molalar", "AVG_TASKS_PER_DAY_WORKED": "Ort. çalışılan günlük görevler", "AVG_TIME_SPENT_ON_BREAKS": "Ort. molalara harcanan zaman", @@ -579,32 +610,77 @@ "AVG_TIME_SPENT_PER_TASK": "Ort. görev başına harcanan zaman", "COUNTING_SUBTASKS": "(alt görevler sayılıyor)", "DAYS_WORKED": "Çalışılan günler", + "FOCUS_SESSION_TRENDS": "Zaman içinde odaklanılan oturumlar", "GLOBAL_METRICS": "Küresel İstatistikler", "MOOD_PRODUCTIVITY_OVER_TIME": "Zaman içindeki ruh hali ve verimlilik", "NO_ADDITIONAL_DATA_YET": "Henüz ek veri toplanmadı. Bunu yapmak için günlük değerlendirme \"Değerlendirme\" panelindeki formu kullanın.", + "PRODUCTIVITY_BREAKDOWN_OVER_TIME": "Zaman içinde Verimlilik & Sürdürülebilirlik", "SIMPLE_CLICK_COUNTERS_OVER_TIME": "Zaman içindeki Sayaçlar'a tıklayın", "SIMPLE_COUNTERS": "Basit Sayaçlar", "SIMPLE_STOPWATCH_COUNTERS_OVER_TIME": "Zaman içindeki kronometre Sayaçları", "TASKS_DONE_CREATED": "Görevler (yapıldı/oluşturuldu)", "TIME_ESTIMATED": "Tahmini Süre", + "TIME_FRAME_1_MONTH": "1 ay", + "TIME_FRAME_2_WEEKS": "2 haftalar", + "TIME_FRAME_LABEL": "Zaman aralığı", + "TIME_FRAME_MAX": "Maks", "TIME_SPENT": "Harcanan zaman" }, "EVAL_FORM": { "ADD_NOTE_FOR_TOMORROW": "Yarın için not ekle", - "DISABLE_REPEAT_EVERY_DAY": "Her gün tekrarlamayı devre dışı bırak", - "ENABLE_REPEAT_EVERY_DAY": "Her gün tekrarla", - "HELP_H1": "Neden umursayayım?", - "HELP_LINK_TXT": "İstatistikler bölümüne git", - "HELP_P1": "Küçük bir öz değerlendirme zamanı! Buradaki cevaplarınız kaydedilir ve ölçümler bölümünde nasıl çalıştığınızla ilgili size biraz istatistik sunar. Ayrıca, yarın için öneriler ertesi gün görev listenizin üstünde görünecektir.", - "HELP_P2": "Bunun amacı, kesin ölçümleri hesaplamak veya yaptığınız her şeyde makine gibi verimli olmak değil, işinizle ilgili duygularınızı iyileştirmektir. Günlük rutininizdeki sıkıntılı/dertli noktaları değerlendirmenin yanı sıra size yardımcı olacak faktörleri bulmak da yararlı olabilir. Bu konuda biraz sistematik olmanın, bunları daha iyi kavramanıza ve yapabileceklerinizi geliştirmenize yardımcı olacağını umuyoruz.", - "IMPROVEMENTS": "Verimliliğinizi ne arttırdı?", - "IMPROVEMENTS_TOMORROW": "Yarını geliştirmek için ne yapabilirsin?", - "MOOD": "Nasıl hissediyorsun?", - "MOOD_HINT": "1: Korkunç - 10: Muhteşem", - "NOTES": "Yarın için notlar", - "OBSTRUCTIONS": "Verimliliğinizi ne engelledi?", - "PRODUCTIVITY": "Ne kadar verimli çalıştınız?", - "PRODUCTIVITY_HINT": "1: Başlamadım bile - 10: Son derece verimli" + "DAILY_STATE": "Günlük Durum", + "DAILY_STATE_TOOLTIP": "Her iki puana göre kuadrant sistemi (≥50 yüksek): Derin Akış (yüksek/yüksek), Aşırı Çalışma (yüksek/düşük), Kurtarma (düşük/yüksek), Sürüklenme (düşük/düşük)", + "ENERGY_LEVEL": "Enerjin nasıl?", + "ENERGY_LEVEL_HINT": "😫 Tükenmiş – 😐 Tamam – 😊 İyi", + "FOCUS_WORK_TIME": "Odaklı çalışma süresi", + "IMPACT_OF_WORK": "Bugünkü çalışmanızın etkisini nasıl değerlendiriyorsunuz?", + "IMPACT_OF_WORK_HINT": "1: Anlamlı ilerleme yok – 4: Önemli etki", + "PRODUCTIVITY_SCORE": "Üretkenlik Puanı", + "PRODUCTIVITY_SCORE_TOOLTIP": "65% Etki (1-4 ölçeği) • 30% 4 saat hedefe odaklanma • 5% toplam çalışma (10 saatte sınırlı)", + "SCORE_BREAKDOWN_TITLE_PRODUCTIVITY": "7 Günlük Üretkenlik Dağılımı", + "SCORE_BREAKDOWN_TITLE_SUSTAINABILITY": "7 Günlük Sürdürülebilirlik Dağılımı", + "STATE_DEEP_FLOW_HEADLINE": "Derin Akış: Mükemmel odaklanma ve sürdürülebilir tempo.", + "STATE_DEEP_FLOW_HINT": "Tatlı noktadasınız — bu kombinasyonu mümkün kılan şeyi düşünün.", + "STATE_DRIFT_HEADLINE": "Sürüklenme: Düşük odaklanma ve düşük enerji.", + "STATE_DRIFT_HINT": "Sorun değil. Sizi neyin dağıttığını düşünün ve nazikçe ayarlayın — her sıfırlama öğrenme fırsatıdır.", + "STATE_IMPACT_MISMATCH_HEADLINE": "Düşük Etki: Bolca odaklanma, az kazanç.", + "STATE_IMPACT_MISMATCH_HINT": "Sağlam bir odaklanma yatırımı yaptınız — şimdi farkı hissetmek için daha yüksek etki çalışmalarına yönlendirin.", + "STATE_OVERDRIVE_HEADLINE": "Aşırı Çalışma: Üretken, ancak bir bedelle.", + "STATE_OVERDRIVE_HINT": "Çok şey yaptınız — şimdi tükenme yaklaşıyor diye şarj olduğunuzdan emin olun.", + "STATE_RECOVERY_HEADLINE": "Kurtarma: Dinlenme ve şarj olma.", + "STATE_RECOVERY_HINT": "Zihninizin iyileşmesi için alan veriyorsunuz. Yarın tekrar derinlere dalmak için enerjiniz olacak.", + "STATE_STEADY_HEADLINE": "Sabit Ritm: Tutarlı, sürdürülebilir ilerleme.", + "STATE_STEADY_HINT": "Güzel denge. Aşırı zorlamadan momentumu koruyorsunuz — bu tempoyu çalıştıran şeyi beslemeye devam edin.", + "SUSTAINABILITY_SCORE": "Sürdürülebilirlik Puanı", + "SUSTAINABILITY_SCORE_TOOLTIP": "45% Enerji seviyesi • 40% Makul çalışma saatleri (10 saatte 0) • 15% Odak dengesi (4 saatte optimal)", + "TOTAL_WORK_TIME": "Bugünkü toplam çalışma süresi" + }, + "FOCUS_SESSION_DIALOG": { + "ADD_BTN": "Ekle", + "ADD_SESSION": "Oturum Ekle", + "CHART_LABEL": "Odak Süresi (dk)", + "NEW_SESSION_DURATION": "Süre", + "NO_SESSIONS": "Bu gün için odak oturumu yok", + "SESSIONS_LIST": "Oturumlar", + "TITLE": "Odak Oturumları", + "TOTAL_SESSIONS": "Toplam Oturum", + "TOTAL_TIME": "Toplam Süre" + }, + "REFLECTION": { + "HISTORY_BTN": "Geçmiş düşünceleri görüntüle", + "HISTORY_EMPTY": "Henüz kaydedilmiş düşünce yok. Seriye başlamak için bugünkü notu yakala.", + "HISTORY_TITLE": "Düşünce geçmişi", + "PLACEHOLDER_1": "Bugün ne iyi çalıştı? Yarın ne değişmeli?", + "PLACEHOLDER_2": "Yarın için küçük bir iyileştirme?", + "PLACEHOLDER_3": "Odak nerede kaydı - ve onu nasıl koruyabilirsin?", + "PLACEHOLDER_4": "Hangi görev sana enerji verdi? Hangisi tüketti?", + "PLACEHOLDER_5": "Tekrar edilecek bir şey. Değiştirilecek bir şey.", + "REMIND_LABEL": "Bunu yarın hatırlat", + "REMINDER_CREATED": "Düşünce hatırlatıcısı yarın için planlandı", + "REMINDER_ERROR": "Hatırlatıcı planlanamadı", + "REMINDER_NEEDS_TEXT": "Hatırlatıcı istemeden önce bir düşünce yaz", + "REMINDER_TASK_TITLE": "Düşünce notunu yeniden ziyaret et", + "TITLE": "Düşünce Notu" }, "S": { "SAVE_METRIC": "İstatistik başarıyla kaydedildi" @@ -776,6 +852,12 @@ } }, "PROJECT": { + "D_CONFIRM_DUPLICATE_BIG_PROJECT": { + "TITLE": "Projeyi kopyala?", + "MSG": "Bu proje oldukça büyük ve kopyalanması biraz zaman alabilir. Devam etmek istiyor musunuz?", + "OK": "Yine de kopyala", + "CANCEL": "İptal" + }, "D_CREATE": { "CREATE": "Proje Yarat", "EDIT": "Projeyi Düzenle", @@ -826,42 +908,24 @@ } }, "PROJECT_FOLDER": { + "CONFIRM_DELETE": "\"{{title}}\" adlı klasörü silmek istediğinizden emin misiniz? Bu klasördeki tüm projeler kök düzeye taşınacaktır.", "DIALOG": { "CREATE_TITLE": "Klasör oluştur", "EDIT_TITLE": "Klasörü düzenle", "NAME_LABEL": "Klasör adı", "NAME_PLACEHOLDER": "Klasör adını girin", "NAME_REQUIRED": "Klasör adı gereklidir", - "PARENT_LABEL": "Ana klasör", - "NO_PARENT": "Ana klasör yok (kök düzey)" + "NO_PARENT": "Ana klasör yok (kök düzey)", + "PARENT_LABEL": "Ana klasör" }, "SELECT": { "LABEL": "Klasör", - "PLACEHOLDER": "Klasör seçin", - "NO_PARENT": "Klasör yok (kök düzey)" + "NO_PARENT": "Klasör yok (kök düzey)", + "PLACEHOLDER": "Klasör seçin" }, - "CONFIRM_DELETE": "\"{{title}}\" adlı klasörü silmek istediğinizden emin misiniz? Bu klasördeki tüm projeler kök düzeye taşınacaktır.", "TOOLTIP_CREATE": "Proje klasörü oluştur", "TOOLTIP_VISIBILITY": "Projeleri göster/gizle" }, - "TAG_FOLDER": { - "DIALOG": { - "CREATE_TITLE": "Klasör oluştur", - "EDIT_TITLE": "Klasörü düzenle", - "NAME_LABEL": "Klasör adı", - "NAME_PLACEHOLDER": "Klasör adını girin", - "NAME_REQUIRED": "Klasör adı gereklidir", - "PARENT_LABEL": "Ana klasör", - "NO_PARENT": "Ana klasör yok (kök düzey)" - }, - "SELECT": { - "LABEL": "Klasör", - "PLACEHOLDER": "Klasör seçin", - "NO_PARENT": "Klasör yok (kök düzey)" - }, - "CONFIRM_DELETE": "\"{{title}}\" adlı klasörü silmek istediğinizden emin misiniz? Bu klasördeki tüm etiketler kök düzeye taşınacaktır.", - "TOOLTIP_CREATE": "Etiket klasörü oluştur" - }, "QUICK_HISTORY": { "NO_DATA": "Bu yıl için veri yok", "PAGE_TITLE": "Hızlı Geçmiş", @@ -896,6 +960,10 @@ "ERR_UNKNOWN": "Redmine: Bilinmeyen hata {{statusCode}} {{errorMsg}}." } }, + "REFLECTION_NOTE": { + "ACTION_DISMISS": "Görmezden gel", + "MSG": "Düşünce Notu: ({{date}}): {{content}}" + }, "REMINDER": { "COUNTDOWN_BANNER": { "HIDE": "Gizle", @@ -913,12 +981,14 @@ "TITLE": "Zaman çizelgesi" }, "END": "İş Bitişi", + "INSERT_BEFORE": "Önce", "LUNCH_BREAK": "Öğle Arası", "MONTH": "Ay", "NO_TASKS": "Şu anda görev yok. Lütfen üst çubuktaki + Düğmesi aracılığıyla bazı görevler ekleyin.", "NOW": "Şimdi", "PLAN_END_DAY": "Günün sonunda planla", "PLAN_START_DAY": "Günün başında planla", + "SHIFT_KEY_INFO": "Shift tuşunu basılı tutarak gün planlama modunu değiştirin", "START": "İş Başlangıcı", "TASK_PROJECTION_INFO": "Zamanlanmış tekrarlanabilir bir görevin gelecek projeksiyonu", "WEEK": "Hafta" @@ -987,7 +1057,11 @@ "TITLE": "Giriş: {{provider}}" }, "D_CONFLICT": { + "ADDITIONAL_INFO": "Ek Bilgi", + "CHANGES": "Değişiklikler", + "CHANGES_SINCE_LAST_SYNC": "Son senkronizasyondan bu yana değişiklikler", "COMPARISON_RESULT": "Karşılaştırma Sonucu", + "DATE": "Tarih", "LAMPORT_CLOCK": "Revizyon", "LAST_CHANGE": "Son Değişiklik:", "LAST_SYNC": "Son Senkronizasyon:", @@ -996,25 +1070,21 @@ "LOCAL": "Lokal", "LOCAL_REMOTE": "Lokal -> Uzak", "NEVER": "Asla", + "OVERWRITE_WARNING": "UYARI: {{targetName}} verileri yaklaşık olarak {{targetChanges}} değişiklik içerirken, {{sourceName}} verileri yalnızca {{sourceChanges}} değişikliğe sahiptir. {{targetName}} verisinin üzerine {{sourceName}} verisini yazmak istediğinizden emin misiniz? Bu eylem geri alınamaz.", "REMOTE": "Uzak", + "RESULT": "Sonuç", "TEXT": "

Hem yerel hem de uzak veriler değiştirilmiş gibi görünüyor. Uzaktan güncelleyin.

", + "TIME": "Zaman", "TIMESTAMP": "Zaman Damgası", "TITLE": "Senkronizasyon: Veri Çakışması", "USE_LOCAL": "Local'ı kullan", "USE_REMOTE": "Uzak'ı kullan", - "ADDITIONAL_INFO": "Ek Bilgi", + "VECTOR_CLOCK": "Vektör Saati", + "VECTOR_CLOCK_HEADING": "Vektör Saati", "VECTOR_COMPARISON_CONCURRENT": "Concurrent (True Conflict)", "VECTOR_COMPARISON_EQUAL": "Eşit", "VECTOR_COMPARISON_LOCAL_GREATER": "Lokal > Uzak", - "VECTOR_COMPARISON_LOCAL_LESS": "Lokal < Uzak", - "DATE": "Tarih", - "TIME": "Zaman", - "VECTOR_CLOCK": "Vektör Saati", - "RESULT": "Sonuç", - "CHANGES": "Değişiklikler", - "CHANGES_SINCE_LAST_SYNC": "Son senkronizasyondan bu yana değişiklikler", - "VECTOR_CLOCK_HEADING": "Vektör Saati", - "OVERWRITE_WARNING": "UYARI: {{targetName}} verileri yaklaşık olarak {{targetChanges}} değişiklik içerirken, {{sourceName}} verileri yalnızca {{sourceChanges}} değişikliğe sahiptir. {{targetName}} verisinin üzerine {{sourceName}} verisini yazmak istediğinizden emin misiniz? Bu eylem geri alınamaz." + "VECTOR_COMPARISON_LOCAL_LESS": "Lokal < Uzak" }, "D_DECRYPT_ERROR": { "BTN_OVER_WRITE_REMOTE": "Uzaktan kumandayı değiştirme ve üzerine yazma", @@ -1085,6 +1155,7 @@ "BTN_FORCE_OVERWRITE": "Üzerine Yazmaya Zorla", "ERROR_CORS": "WebDAV Senkronizasyon Hatası: Ağ isteği başarısız oldu.\n\nBu bir CORS sorunu olabilir. Lütfen emin olun:\n• WebDAV sunucunuz Çapraz Kaynak isteklerine izin verir\n• Sunucu URL'si doğru ve erişilebilirdir\n• Çalışan bir internet bağlantınız var", "ERROR_DATA_IS_CURRENTLY_WRITTEN": "Uzak Veri şu anda yazılıyor", + "ERROR_PERMISSION": "Dosya erişimi reddedildi. Flatpak/Snap kullanıyorsanız, Flatseal aracılığıyla dosya sistemi izni verin veya ~/.var/app/ içinde bir yol kullanın", "ERROR_FALLBACK_TO_BACKUP": "Veriler içe aktarılırken bir sorun oluştu. Yerel yedeklemeye geri dönülüyor.", "ERROR_INVALID_DATA": "Senkronizasyon sırasında hata oluştu. Geçersiz veri", "ERROR_NO_REV": "Uzak dosya için geçerli bir rev yok", @@ -1099,38 +1170,38 @@ "UPLOAD_ERROR": "Bilinmeyen Yükleme Hatası (Ayarlar doğru mu?): {{err}}" }, "SAFETY_BACKUP": { - "TITLE": "Senkronizasyon Güvenlik Yedekleri", - "DESCRIPTION": "Uzaktan veri indirme işlemleri sırasında senkronizasyon öncesinde otomatik olarak yedekler oluşturulur. Yedekler 4 akıllı yuvada düzenlenir: 2 en yeni, 1 ilk bugünden ve 1 ilk önce bugünden bir gün önce", - "BTN_CREATE_MANUAL": "Manuel Yedek Oluştur", - "BTN_REFRESH": "Yenile", - "BTN_CLEAR_ALL": "Tümünü Temizle", - "BTN_RESTORE": "Geri Yükle", - "BTN_DELETE": "Sil", - "LOADING": "Yükleniyor...", - "NO_BACKUPS": "Henüz güvenlik yedeği yok. Yedekler senkronizasyon işlemleri öncesinde otomatik olarak oluşturulur.", - "TOOLTIP_CREATE_MANUAL": "Tüm verilerinizin manuel yedeğini oluşturun", - "TOOLTIP_REFRESH": "Yedek listesini yenile", - "TOOLTIP_CLEAR_ALL": "Tüm güvenlik yedeklerini sil", - "TOOLTIP_RESTORE": "Bu yedeği geri yükle (tüm mevcut verileri değiştirecek)", - "TOOLTIP_DELETE": "Bu yedeği sil", - "SLOT_RECENT": "Son yedek", - "SLOT_TODAY": "Bugünün ilk yedeği", - "SLOT_BEFORE_TODAY": "Dünden önceki ilk yedek", - "REASON_MANUAL": "Manuel yedek", - "REASON_BEFORE_UPDATE": "Senkronizasyon öncesi otomatik yedek", - "LAST_CHANGE_PREFIX": "Son değişiklik:", - "RESTORE_CONFIRM_TITLE": "Yedeği Geri Yükle", - "RESTORE_CONFIRM_MSG": "Yedeği {{timestamp}} tarihinden geri yüklemek istediğinizden emin misiniz?\n\nBu işlem TÜM mevcut verilerinizi TAMAMEN DEĞİŞTİRECEK!\n\nSebep: {{reason}}\n\nDevam etmek için Tamam'a, iptal etmek için İptal'e tıklayın.", "BACKUP_NOT_FOUND": "{{backupId}} kimliğine sahip yedek bulunamadı", - "RESTORE_FAILED": "Yedek geri yüklenemedi: {{error}}", - "INVALID_ID_ERROR": "Geçersiz yedek kimliği oluşturuldu", - "CREATED_SUCCESS": "Manuel yedek başarıyla oluşturuldu", - "RESTORED_SUCCESS": "Yedek başarıyla geri yüklendi", - "DELETED_SUCCESS": "Yedek başarıyla silindi", + "BTN_CLEAR_ALL": "Tümünü Temizle", + "BTN_CREATE_MANUAL": "Manuel Yedek Oluştur", + "BTN_DELETE": "Sil", + "BTN_REFRESH": "Yenile", + "BTN_RESTORE": "Geri Yükle", + "CLEAR_FAILED": "Yedekler temizlenemedi", "CLEARED_SUCCESS": "Tüm yedekler başarıyla temizlendi", "CREATE_FAILED": "Yedek oluşturulamadı", + "CREATED_SUCCESS": "Manuel yedek başarıyla oluşturuldu", "DELETE_FAILED": "Yedek silinemedi", - "CLEAR_FAILED": "Yedekler temizlenemedi" + "DELETED_SUCCESS": "Yedek başarıyla silindi", + "DESCRIPTION": "Uzaktan veri indirme işlemleri sırasında senkronizasyon öncesinde otomatik olarak yedekler oluşturulur. Yedekler 4 akıllı yuvada düzenlenir: 2 en yeni, 1 ilk bugünden ve 1 ilk önce bugünden bir gün önce", + "INVALID_ID_ERROR": "Geçersiz yedek kimliği oluşturuldu", + "LAST_CHANGE_PREFIX": "Son değişiklik:", + "LOADING": "Yükleniyor...", + "NO_BACKUPS": "Henüz güvenlik yedeği yok. Yedekler senkronizasyon işlemleri öncesinde otomatik olarak oluşturulur.", + "REASON_BEFORE_UPDATE": "Senkronizasyon öncesi otomatik yedek", + "REASON_MANUAL": "Manuel yedek", + "RESTORE_CONFIRM_MSG": "Yedeği {{timestamp}} tarihinden geri yüklemek istediğinizden emin misiniz?\n\nBu işlem TÜM mevcut verilerinizi TAMAMEN DEĞİŞTİRECEK!\n\nSebep: {{reason}}\n\nDevam etmek için Tamam'a, iptal etmek için İptal'e tıklayın.", + "RESTORE_CONFIRM_TITLE": "Yedeği Geri Yükle", + "RESTORE_FAILED": "Yedek geri yüklenemedi: {{error}}", + "RESTORED_SUCCESS": "Yedek başarıyla geri yüklendi", + "SLOT_BEFORE_TODAY": "Dünden önceki ilk yedek", + "SLOT_RECENT": "Son yedek", + "SLOT_TODAY": "Bugünün ilk yedeği", + "TITLE": "Senkronizasyon Güvenlik Yedekleri", + "TOOLTIP_CLEAR_ALL": "Tüm güvenlik yedeklerini sil", + "TOOLTIP_CREATE_MANUAL": "Tüm verilerinizin manuel yedeğini oluşturun", + "TOOLTIP_DELETE": "Bu yedeği sil", + "TOOLTIP_REFRESH": "Yedek listesini yenile", + "TOOLTIP_RESTORE": "Bu yedeği geri yükle (tüm mevcut verileri değiştirecek)" } }, "TAG": { @@ -1159,6 +1230,24 @@ "ADD_NEW_TAG": "Yeni Etiket Ekle" } }, + "TAG_FOLDER": { + "CONFIRM_DELETE": "\"{{title}}\" adlı klasörü silmek istediğinizden emin misiniz? Bu klasördeki tüm etiketler kök düzeye taşınacaktır.", + "DIALOG": { + "CREATE_TITLE": "Klasör oluştur", + "EDIT_TITLE": "Klasörü düzenle", + "NAME_LABEL": "Klasör adı", + "NAME_PLACEHOLDER": "Klasör adını girin", + "NAME_REQUIRED": "Klasör adı gereklidir", + "NO_PARENT": "Ana klasör yok (kök düzey)", + "PARENT_LABEL": "Ana klasör" + }, + "SELECT": { + "LABEL": "Klasör", + "NO_PARENT": "Klasör yok (kök düzey)", + "PLACEHOLDER": "Klasör seçin" + }, + "TOOLTIP_CREATE": "Etiket klasörü oluştur" + }, "TASK": { "ADD_TASK_BAR": { "ADD_EXISTING_TASK": "Mevcut \"{{taskTitle}}\" görevini ekle", @@ -1167,32 +1256,32 @@ "ADD_TASK_TO_BOTTOM_OF_TODAY": "Listenin en altına görev ekle", "ADD_TASK_TO_TOP_OF_BACKLOG": "Biriktirme listesinin en üstüne görev ekle", "ADD_TASK_TO_TOP_OF_TODAY": "Listenin başına görev ekle", + "CREATE_NEW_TAGS": "Yeni etiketler oluştur", "CREATE_TASK": "Yeni görev oluştur", - "EXAMPLE": "Örnek: \"Bir görev başlığı @friday 4pm +projeAdı #bir etiket #başka bir etiket 10m/3h\"", - "START": "Başlamak için bir kez daha enter tuşuna basın", - "TOGGLE_ADD_TO_BACKLOG_TODAY": "Görevi biriktirme listesine/bugünün listesine eklemeyi aç/kapat'", - "TOGGLE_ADD_TOP_OR_BOTTOM": "Görev eklemeyi listenin en üstüne ve en altına değiştir", - "PLACEHOLDER_SEARCH": "Mevcut görev veya sorunları ekle...", - "PLACEHOLDER_CREATE": "Bir görev başlığı #etiket @16:00", - "TOOLTIP_ADD_TASK": "Görev ekle", - "TOOLTIP_ADD_TO_TOP": "En üste ekle (Ctrl+1)", - "TOOLTIP_ADD_TO_BOTTOM": "En alta ekle (Ctrl+1)", - "TOOLTIP_ADD_TO_TODAY": "Bugüne ekle", - "TOOLTIP_ADD_TO_BACKLOG": "Biriktirme listesine ekle", - "TOOLTIP_ENABLE_SEARCH": "Sorun aramayı etkinleştir (Ctrl+2)", - "TOOLTIP_DISABLE_SEARCH": "Sorun aramayı devre dışı bırak (Ctrl+2)", - "SEARCH_INFO_TEXT": "Arşivden ve diğer projelerden sorunları ve görevleri arayın ve ekleyin", "DUE_BUTTON": "Son Tarih", - "TAGS_BUTTON": "Etiketler", "ESTIMATE_BUTTON": "Tahmin", - "TOOLTIP_CLEAR_DATE": "Tarihi temizle", - "TOOLTIP_CLEAR_TAGS": "Etiketleri temizle", - "TOOLTIP_CLEAR_ESTIMATE": "Tahmini temizle", + "EXAMPLE": "Örnek: \"Bir görev başlığı @friday 4pm +projeAdı #bir etiket #başka bir etiket 10m/3h\"", "NO_DATE": "Tarih Yok", "NO_TIME": "Zaman Yok", + "PLACEHOLDER_CREATE": "Bir görev başlığı #etiket @16:00", + "PLACEHOLDER_SEARCH": "Mevcut görev veya sorunları ekle...", + "SEARCH_INFO_TEXT": "Arşivden ve diğer projelerden sorunları ve görevleri arayın ve ekleyin", + "START": "Başlamak için bir kez daha enter tuşuna basın", + "TAGS_BUTTON": "Etiketler", "TODAY": "Bugün", + "TOGGLE_ADD_TO_BACKLOG_TODAY": "Görevi biriktirme listesine/bugünün listesine eklemeyi aç/kapat'", + "TOGGLE_ADD_TOP_OR_BOTTOM": "Görev eklemeyi listenin en üstüne ve en altına değiştir", "TOMORROW": "Yarın", - "CREATE_NEW_TAGS": "Yeni etiketler oluştur" + "TOOLTIP_ADD_TASK": "Görev ekle", + "TOOLTIP_ADD_TO_BACKLOG": "Biriktirme listesine ekle", + "TOOLTIP_ADD_TO_BOTTOM": "En alta ekle (Ctrl+1)", + "TOOLTIP_ADD_TO_TODAY": "Bugüne ekle", + "TOOLTIP_ADD_TO_TOP": "En üste ekle (Ctrl+1)", + "TOOLTIP_CLEAR_DATE": "Tarihi temizle", + "TOOLTIP_CLEAR_ESTIMATE": "Tahmini temizle", + "TOOLTIP_CLEAR_TAGS": "Etiketleri temizle", + "TOOLTIP_DISABLE_SEARCH": "Sorun aramayı devre dışı bırak (Ctrl+2)", + "TOOLTIP_ENABLE_SEARCH": "Sorun aramayı etkinleştir (Ctrl+2)" }, "ADDITIONAL_INFO": { "ADD_ATTACHMENT": "Eklenti ekle", @@ -1263,6 +1352,8 @@ "ADD_TO_TODAY": "Bugüne ekle", "COMPLETE": "Tamamla", "COMPLETE_ALL": "Tümünü tamamla", + "DISMISS_ALL_REMINDERS_KEEP_TODAY": "Tüm Hatırlatıcıları Kapat (Bugün de Kalsın)", + "DISMISS_REMINDER_KEEP_TODAY": "Hatırlatıcıyı Kapat (Bugün de Kalsın)", "DONE": "Bitti", "DUE_TASK": "Yapılacak görev", "DUE_TASKS": "Yapılacak görevler", @@ -1277,9 +1368,7 @@ "START": "Başla", "SWITCH_CONTEXT_START": "Kaynak Değiştir ve Başlat", "UNSCHEDULE": "Programı Kaldır", - "UNSCHEDULE_ALL": "Tümünü planlamayı kaldır", - "DISMISS_REMINDER_KEEP_TODAY": "Hatırlatıcıyı Kapat (Bugün de Kalsın)", - "DISMISS_ALL_REMINDERS_KEEP_TODAY": "Tüm Hatırlatıcıları Kapat (Bugün de Kalsın)" + "UNSCHEDULE_ALL": "Tümünü planlamayı kaldır" }, "D_SCHEDULE_TASK": { "MOVE_TO_BACKLOG": "Zamanlanana kadar görevi biriktirme günlüğüne kaydet", @@ -1299,6 +1388,11 @@ "SCHEDULE": "Planla", "UNSCHEDULE": "Planlamayı iptal et" }, + "D_SELECT_DATE_AND_TIME": { + "DATE": "Tarih", + "TIME": "Saat", + "TITLE": "Tarih ve Saat Seç" + }, "D_TIME": { "ADD_FOR_OTHER_DAY": "Diğer gün için harcanan zamanı ekle", "DELETE_FOR": "Günün girişini sil", @@ -1326,6 +1420,7 @@ "FOUND_MOVE_FROM_BACKLOG": " {{title}} görevini bugünkü görev listesine taşıdı", "FOUND_MOVE_FROM_OTHER_LIST": " {{contextTitle}} 'den geçerli listeye {{title}} görevi eklendi", "FOUND_RESTORE_FROM_ARCHIVE": "Arşivdeki sorunla ilgili {{title} görevi geri yüklendi", + "GO_TO_TASK": "Göreve Git", "LAST_TAG_DELETION_WARNING": "Proje dışı bir görevin son etiketini kaldırmaya çalışıyorsunuz. Buna izin verilmiyor!", "MOVED_TO_ARCHIVE": " {{nr}} görevler arşive taşındı", "MOVED_TO_PROJECT": "\"{{taskTitle}}\" görevini \"{{projectTitle}}\" projesine taşıdı", @@ -1375,15 +1470,15 @@ "MSG": "Tekrar yapılandırmasının kaldırılması, bu görevin önceki tüm örneklerini yalnızca normal görevlere dönüştürecektir. Devam etmek istediğinizden emin misiniz?", "OK": "Tamamen kaldır" }, - "D_DELETE_INSTANCE": { - "MSG": "Tekrarlanan görevin {{date}} tarihindeki örneğini kaldırmak istiyor musunuz? Bu yalnızca görevin bu tarihte oluşturulmasını engelleyecektir.", - "OK": "Örneği Kaldır" - }, "D_CONFIRM_UPDATE_INSTANCES": { "CANCEL": "Yalnızca gelecekteki görevler", "MSG": "Bu tekrarlanabilir görev için oluşturulmuş {{tasksNr}} örnek var. Hepsini yeni varsayılanlarla mı yoksa yalnızca gelecekteki görevlerle mi güncellemek istiyorsunuz?", "OK": "Tüm örnekleri güncelle" }, + "D_DELETE_INSTANCE": { + "MSG": "Tekrarlanan görevin {{date}} tarihindeki örneğini kaldırmak istiyor musunuz? Bu yalnızca görevin bu tarihte oluşturulmasını engelleyecektir.", + "OK": "Örneği Kaldır" + }, "D_EDIT": { "ADD": "Tekrar Görev Yapısı Ekle", "ADVANCED_CFG": "Gelişmiş Yapılandırma", @@ -1400,7 +1495,11 @@ "C_WEEK": "Hafta", "C_YEAR": "Yıl", "DEFAULT_ESTIMATE": "Varsayılan Tahmini", + "DISABLE_AUTO_UPDATE_SUBTASKS": "Alt görevlerin otomatik güncellenmesini devre dışı bırak", + "DISABLE_AUTO_UPDATE_SUBTASKS_DESCRIPTION": "En yeni örnek değiştiğinde devralınan alt görevleri otomatik olarak güncelleme", "FRIDAY": "Cuma", + "INHERIT_SUBTASKS": "Alt görevleri devral", + "INHERIT_SUBTASKS_DESCRIPTION": "Etkinleştirildiğinde, en son görev örneğindeki alt görevler tekrarlayan görevle birlikte yeniden oluşturulur", "IS_ADD_TO_BOTTOM": "Görevi listenin altına taşı", "MONDAY": "Pazartesi", "NOTES": "Varsayılan notlar", @@ -1415,9 +1514,16 @@ "QUICK_SETTING": "Yapılandırmayı Tekrarla", "REMIND_AT": "Şu saatte hatırlat:", "REMIND_AT_PLACEHOLDER": "Ne zaman hatırlatılacağını seçin", + "REMOVE_FOR_DATE": "{{date}} için kaldır", + "REMOVE_INSTANCE": "Bugünkü örneği kaldır", "REPEAT_CYCLE": "Döngüyü tekrarla", "REPEAT_EVERY": "Yinelenme sıklığı", + "REPEAT_FROM_COMPLETION_DATE": "Tamamlandığında tekrarla", + "REPEAT_FROM_COMPLETION_DATE_DESCRIPTION": "Bir sonraki görev, başlangıç tarihinden değil, tamamlanma tarihinizden oluşturulur. (örneğin, 'Her 7 günde bir' = tamamlandıktan 7 gün sonra)", "SATURDAY": "Cumartesi", + "SCHEDULE_TYPE_FIXED": "Sabit Planlama (örn. her Pazartesi, ayın 1'i)", + "SCHEDULE_TYPE_FLEXIBLE": "Tamamlandıktan sonra (örn. bitirdikten 7 gün sonra)", + "SCHEDULE_TYPE_LABEL": "Planlama Türü", "START_DATE": "Başlangıç tarihi", "START_TIME": "Planlanan başlangıç zamanı", "START_TIME_DESCRIPTION": "Örneğin. 15:00. Tüm gün süren bir görev için boş bırakın", @@ -1425,13 +1531,7 @@ "THURSDAY": "Perşembe", "TITLE": "Görevin Başlığı", "TUESDAY": "Salı", - "WEDNESDAY": "Çarşamba", - "INHERIT_SUBTASKS": "Alt görevleri devral", - "INHERIT_SUBTASKS_DESCRIPTION": "Etkinleştirildiğinde, en son görev örneğindeki alt görevler tekrarlayan görevle birlikte yeniden oluşturulur", - "REMOVE_INSTANCE": "Bugünkü örneği kaldır", - "REMOVE_FOR_DATE": "{{date}} için kaldır", - "DISABLE_AUTO_UPDATE_SUBTASKS": "Alt görevlerin otomatik güncellenmesini devre dışı bırak", - "DISABLE_AUTO_UPDATE_SUBTASKS_DESCRIPTION": "En yeni örnek değiştiğinde devralınan alt görevleri otomatik olarak güncelleme" + "WEDNESDAY": "Çarşamba" } }, "TASK_VIEW": { @@ -1458,11 +1558,12 @@ "SCHEDULED_THIS_WEEK": "Bu hafta", "SCHEDULED_TODAY": "Bugün", "SCHEDULED_TOMORROW": "Yarın", - "SORT_BY": "Sırala", "SORT_CREATION_DATE": "Oluşturma Tarihi", "SORT_DEFAULT": "Varsayılan", "SORT_NAME": "Ad", + "SORT_PERMANENT": "Sırala (kalıcı)", "SORT_SCHEDULED_DATE": "Planlanan Tarih", + "SORT_TEMPORARY": "Sırala (geçici)", "TIME_1HOUR": "> 1 Saat", "TIME_2HOUR": "> 2 Saat", "TIME_10MIN": "> 10 Dakika", @@ -1496,7 +1597,9 @@ "SKIP": "Atla", "SPLIT_TIME": "Zamanı çoklu görevlere ve molalara bölün", "TASK": "Görev", - "TRACK_TO": "Takip et:" + "TRACK_TO": "Takip et:", + "WARN_SIMPLE_COUNTER": "Etkinleştirilen basit sayaç düğmelerinde zaman sayılacaktır.", + "WARN_SIMPLE_COUNTER_BREAK": "Etkinleştirilen basit sayaç düğmelerinde zaman yine de sayılacaktır." }, "D_TRACKING_REMINDER": { "CREATE_AND_TRACK": "Oluşturun ve takip edin", @@ -1559,6 +1662,7 @@ }, "WEEK": { "EXPORT": "Haftalık veriyi dışa aktar", + "FOCUS_SUMMARY": "Odaklanma Oturumu (sayı / saat)", "NO_DATA": "Henüz bu haftanın görevleri yok.", "TITLE": "Başlık" } @@ -1652,7 +1756,11 @@ "FOCUS_MODE": { "HELP": "Odak Modu, mevcut görevinize odaklanmanıza yardımcı olmak için dikkatinizi dağıtmayan bir ekran açar.", "L_ALWAYS_OPEN_FOCUS_MODE": "İzleme sırasında her zaman odak modunu açın", + "L_SYNC_SESSION_WITH_TRACKING": "Odak oturumunu zaman takibi ile senkronize et", + "L_IS_PLAY_TICK": "Odak oturumları sırasında tıklama sesi çal", + "L_PAUSE_TRACKING_DURING_BREAK": "Molalar sırasında görev takibini duraklat", "L_SKIP_PREPARATION_SCREEN": "Hazırlık ekranını atlayın (esnetme vb.)", + "L_START_IN_BACKGROUND": "Odak oturumlarını yalnızca kapak ile başlat (kaplama yok)", "TITLE": "Odak modu" }, "IDLE": { @@ -1668,6 +1776,7 @@ }, "KEYBOARD": { "ADD_NEW_NOTE": "Yeni not ekle", + "ADD_NEW_PROJECT": "Yeni Proje ekle", "ADD_NEW_TASK": "Yeni Görev ekle", "APP_WIDE_SHORTCUTS": "Genel Kısayollar (uygulama genelinde)", "COLLAPSE_SUB_TASKS": "Alt Görevleri Daralt", @@ -1690,6 +1799,7 @@ "MOVE_TO_BACKLOG": "Görevi biriktirme listesine taşı", "MOVE_TO_REGULARS_TASKS": "Görevi Bugünün Görev Listesine Taşı", "OPEN_PROJECT_NOTES": "Proje Notlarını Göster / Gizle", + "PLUGIN_SHORTCUTS": "Eklenti Kısayolları", "SAVE_NOTE": "Notu kaydet", "SELECT_NEXT_TASK": "Sonraki görevi seç", "SELECT_PREVIOUS_TASK": "Önceki Görevi Seç", @@ -1719,8 +1829,7 @@ "TRIGGER_SYNC": "Senkronizasyonu tetikle (yapılandırılmışsa)", "ZOOM_DEFAULT": "Varsayılan yakınlaştırma (Yalnızca masaüstü)", "ZOOM_IN": "Yakınlaştır (Yalnızca masaüstü)", - "ZOOM_OUT": "Uzaklaştır (Yalnızca masaüstü)", - "PLUGIN_SHORTCUTS": "Eklenti Kısayolları" + "ZOOM_OUT": "Uzaklaştır (Yalnızca masaüstü)" }, "LANG": { "AR": "عربى", @@ -1740,6 +1849,7 @@ "NL": "Nederlands", "PL": "Polish", "PT": "Português", + "PT_BR": "Português (Brazil)", "RU": "Russian", "SK": "Slovak", "TITLE": "Dil", @@ -1750,21 +1860,27 @@ "TIME_LOCALE": "Zaman formatı yerel ayarı", "TIME_LOCALE_DESCRIPTION": "Zaman formatı için bir yerel ayar seçin. Farklı yerel ayarlar farklı gelenekler kullanır (12sa vs 24sa formatı)", "TIME_LOCALE_AUTO": "Sistem varsayılanı", - "TIME_LOCALE_EN_US": "İngiliz (ABD) - 12-saat AM/PM", - "TIME_LOCALE_EN_GB": "İngiliz (İngiltere) - 24-saat", - "TIME_LOCALE_TR_TR": "Türk - 24-saat", - "TIME_LOCALE_DE_DE": "Alman - 24-saat", - "TIME_LOCALE_FR_FR": "Fransız - 24-saat", - "TIME_LOCALE_ES_ES": "İspanyol - 24-saat", - "TIME_LOCALE_IT_IT": "İtalyan - 24-saat", - "TIME_LOCALE_PT_BR": "Portekiz (Brezilya) - 24-saat", - "TIME_LOCALE_RU_RU": "Rus - 24-saat", - "TIME_LOCALE_ZH_CN": "Çin (Basit) - 24-saat", - "TIME_LOCALE_JA_JP": "Japon - 24-saat", - "TIME_LOCALE_KO_KR": "Kore - 12-saat AM/PM" + "TIME_LOCALE_EN_US": "English (US): 12-saat AM/PM, MM/DD/YYYY", + "TIME_LOCALE_EN_GB": "English (UK): 24-saat, DD/MM/YYYY", + "TIME_LOCALE_TR_TR": "Türk: 24-saat, DD.MM.YYYY", + "TIME_LOCALE_DE_DE": "German: 24-saat, DD.MM.YYYY", + "TIME_LOCALE_FR_FR": "French: 24-saat, DD/MM/YYYY", + "TIME_LOCALE_ES_ES": "Spanish: 24-saat, DD/MM/YYYY", + "TIME_LOCALE_IT_IT": "Italian: 24-saat, DD/MM/YYYY", + "TIME_LOCALE_PT_BR": "Portuguese (Brazil): 24-saat, DD/MM/YYYY", + "TIME_LOCALE_RU_RU": "Russian: 24-saat, DD.MM.YYYY", + "TIME_LOCALE_ZH_CN": "Chinese (Simplified): 24-saat, YYYY/MM/DD", + "TIME_LOCALE_JA_JP": "Japanese: 24-saat, YYYY/MM/DD", + "TIME_LOCALE_KO_KR": "Korean: 12-saat AM/PM, YYYY. MM. DD" }, "MISC": { + "DARK_MODE": "Karanlık Mod", + "DARK_MODE_ARIA_LABEL": "Karanlık mod seçimi", + "DARK_MODE_DARK": "Karanlık", + "DARK_MODE_LIGHT": "Aydınlık", + "DARK_MODE_SYSTEM": "Sistem", "DEFAULT_PROJECT": "Proje belirtilmezse görevler için kullanılacak varsayılan proje", + "DEFAULT_START_PAGE": "Varsayılan başlangıç sayfası", "FIRST_DAY_OF_WEEK": "Haftanın ilk günü", "HELP": "

Masaüstü Bildirimleri'ni göremiyor musunuz? Pencereler için Sistem> Bildirimler ve eylemleri kontrol etmek ve gerekli bildirimlerin etkin olup olmadığını kontrol etmek isteyebilirsiniz.

", "IS_AUTO_ADD_WORKED_ON_TO_TODAY": "Çalışılan görevlere bugün etiketini otomatik olarak ekle", @@ -1776,25 +1892,20 @@ "IS_DISABLE_CELEBRATION": "Günlük özet kutlamasını devre dışı bırak", "IS_HIDE_NAV": "Ana başlık yönlendirilene kadar gezinmeyi gizle (yalnızca masaüstü)", "IS_MINIMIZE_TO_TRAY": "Tepsiye küçült (yalnızca masaüstü)", + "IS_OVERLAY_INDICATOR_ENABLED": "Panel gösterge penceresini etkinleştir (masaüstü linux/gnome)", "IS_SHOW_TIP_LONGER": "Uygulamada üretkenlik ipucunu biraz daha uzun süre başlatın", "IS_TRAY_SHOW_CURRENT_COUNTDOWN": "Mevcut geri sayımı tepsi / durum menüsünde göster (sadece masaüstü mac için)", "IS_TRAY_SHOW_CURRENT_TASK": "Mevcut görevi tepsi / Durum menüsünde göster (yalnızca masaüstü)", - "IS_OVERLAY_INDICATOR_ENABLED": "Panel gösterge penceresini etkinleştir (masaüstü linux/gnome)", "IS_TURN_OFF_MARKDOWN": "Notlar için etiketleme ayrıştırmayı kapatın", - "USER_PROFILES": "Kullanıcı profillerini etkinleştir (Beta)", - "USER_PROFILES_HINT": "Her biri ayrı ayarlar, görevler ve senkronizasyon yapılandırmalarına sahip farklı kullanıcı profilleri oluşturmanıza ve aralarında geçiş yapmanıza olanak tanır. Etkinleştirildiğinde profil yönetimi düğmesi sağ üst köşede görünecektir. Not: Bu özelliği devre dışı bırakmak kullanıcı arayüzünü gizler ancak profil verilerinizi korur (Beta özelliği, garanti yok. Yedeğiniz olduğundan emin olun).", + "IS_USE_CUSTOM_WINDOW_TITLE_BAR": "Özel başlık çubuğunu kullan (Windows/Linux için)", + "IS_USE_CUSTOM_WINDOW_TITLE_BAR_HINT": "Etkin olması için yeniden başlatma gerektirir", "START_OF_NEXT_DAY": "Ertesi günün başlama saati", "START_OF_NEXT_DAY_HINT": "Ertesi günün başladığı saati saymak istediğiniz andan itibaren (saat cinsinden). Varsayılan gece yarısıdır, yani 0'dır.", "TASK_NOTES_TPL": "Görev açıklama şablonu", - "TITLE": "Diğer ayarlar", - "DARK_MODE": "Karanlık Mod", - "DARK_MODE_SYSTEM": "Sistem", - "DARK_MODE_DARK": "Karanlık", - "DARK_MODE_LIGHT": "Aydınlık", - "DARK_MODE_ARIA_LABEL": "Karanlık mod seçimi", "THEME": "Tema", "THEME_EXPERIMENTAL": "Tema (Deneysel)", - "THEME_SELECT_LABEL": "Tema seç" + "THEME_SELECT_LABEL": "Tema seç", + "TITLE": "Diğer ayarlar" }, "POMODORO": { "BREAK_DURATION": "Kısa molaların süresi", @@ -1804,8 +1915,10 @@ }, "REMINDER": { "COUNTDOWN_DURATION": "Gerçek hatırlatıcıdan önce X başlığını göster", + "DEFAULT_TASK_REMIND_OPTION": "Görev oluştururken varsayılan hatırlatma seçeneği", "IS_COUNTDOWN_BANNER_ENABLED": "Hatırlatıcıların son teslim tarihinden önce geri sayım afişini göster", - "TITLE": "Hatırlatıcılar" + "TITLE": "Hatırlatıcılar", + "DISABLE_REMINDERS": "Tüm hatırlatıcıları devre dışı bırak" }, "SCHEDULE": { "HELP": "Zaman çizelgesi özelliği, planlanan görevlerinizin zaman içinde nasıl oynandığına dair size hızlı bir genel bakış sağlamalıdır. Bunu sol taraftaki menüde \"Zaman çizelgesi\"altında bulabilirsiniz.", @@ -1816,10 +1929,10 @@ "L_WORK_END": "İş Günü Sonu", "L_WORK_START": "İş Günü Başlangıcı", "LUNCH_BREAK_START_END_DESCRIPTION": "örn. 13:00", + "MONTH": "Ay", "TITLE": "Zaman çizelgesi", - "WORK_START_END_DESCRIPTION": "örn. 17:00", "WEEK": "Hafta", - "MONTH": "Ay" + "WORK_START_END_DESCRIPTION": "örn. 17:00" }, "SHORT_SYNTAX": { "HELP": "

Görev oluştururken kısa sözdizimi seçeneklerini buradan kontrol edebilirsiniz

", @@ -1864,6 +1977,22 @@ "L_TRACKING_INTERVAL": "Zaman izleme aralığı (DENEYSEL)", "L_TRACKING_REMINDER_MIN_TIME": "Banner göstermeden önce bekleme süresi", "TITLE": "Zaman İzleyici" + }, + "APP_FEATURES": { + "HELP": "Kullanıcı arayüzü genelinde belirli uygulama özelliklerini etkinleştirin veya devre dışı bırakın.", + "TITLE": "Uygulama Özellikleri", + "TIME_TRACKING": "Kronometre Zaman İzleme", + "FOCUS_MODE": "Odak Modu", + "SCHEDULE": "Takvim", + "PLANNER": "Planlayıcı", + "BOARDS": "Panolar", + "SCHEDULE_DAY_PANEL": "Takvim Gün Paneli", + "ISSUES_PANEL": "Sorunlar Paneli", + "PROJECT_NOTES": "Proje Notları", + "SYNC_BUTTON": "Senkronizasyon Düğmesi", + "DONATE_PAGE": "Bağış Sayfası", + "USER_PROFILES": "Kullanıcı profilleri (Deneysel)", + "USER_PROFILES_HINT": "Farklı kullanıcı profilleri oluşturmanıza ve bunlar arasında geçiş yapmanıza olanak tanır, her biri ayrı ayarlar, görevler ve senkronizasyon yapılandırmalarına sahip. Etkinleştirildiğinde profil yönetimi düğmesi sağ üst köşede görünecektir. Not: Bu özelliği devre dışı bırakmak UI'yi gizleyecek ancak profil verilerinizi koruyacaktır (Deneysel özellik, garanti verilmez. Yedek aldığınızdan emin olun)." } }, "GLOBAL_RELATIVE_TIME": { @@ -1896,14 +2025,25 @@ }, "GLOBAL_SNACK": { "COPY_TO_CLIPPBOARD": "Panoya kopyalandı", + "DUPLICATE_PROJECT_ERROR": "Proje çoğaltılamadı", + "DUPLICATE_PROJECT_SUCCESS": "Proje başarıyla çoğaltıldı", "ERR_COMPRESSION": "Sıkıştırma arayüzü için hata", "FILE_DOWNLOADED": "{{fileName}} indirildi", "FILE_DOWNLOADED_BTN": "Açılan dosya", "NAVIGATE_TO_TASK_ERR": "Göreve odaklanılamadı. Sildiniz mi?", + "NO_TASKS_TO_COPY": "Kopyalanacak görev yok", + "NO_TASKS_TO_UNPLAN": "Planlaması kaldırılacak görev yok", "PERSISTENCE_DISALLOWED": "Veri kalıcı olarak sürdürülmeyecek. Bunun veri kaybına neden olabileceğini unutmayın!", "PERSISTENCE_ERROR": "Verilerin kalıcı hale getirilmesi istenirken hata oluştu: {{err}}", "RUNNING_X": "\"{{str}}\" çalıştırılıyor.", - "SHORTCUT_WARN_OPEN_BOOKMARKS_FROM_TAG": "{{keyCombo}} basıldı, ancak yer imlerini aç kısayolu yalnızca proje bağlamındayken kullanılabilir." + "SHARE_FAILED": "Paylaşım başarısız. Lütfen manuel olarak kopyalayın.", + "SHARE_FAILED_FALLBACK": "Paylaşım başarısız. Bunun yerine panoya kopyalandı.", + "SHARE_UNAVAILABLE_FALLBACK": "Panoya kopyalandı.", + "SHORTCUT_WARN_OPEN_BOOKMARKS_FROM_TAG": "{{keyCombo}} basıldı, ancak yer imlerini aç kısayolu yalnızca proje bağlamındayken kullanılabilir.", + "UNPLANNED_TODAY_TASKS": "Bugünden tüm görevlerin planlaması kaldırıldı" + }, + "GLOBAL": { + "COPY_SUFFIX": " (kopyala)" }, "GPB": { "ASSETS": "Varlıklar yükleniyor ...", @@ -1922,10 +2062,13 @@ "ADD_NEW_TASK": "Yeni Görev ekle", "ALL_PLANNED_LIST": "Tekrar/Planlanmış", "BOARDS": "Panolar", + "COPY_TASK_LIST_MARKDOWN": "Panoya Kopyala", "CREATE_PROJECT": "Proje Yarat", + "DUPLICATE_PROJECT": "Çoğalt", "CREATE_TAG": "Etiket Oluştur", "DELETE_PROJECT": "Projeyi Sil", "DELETE_TAG": "Etiketi Sil", + "DONATE": "Bize Destek Ol", "ENTER_FOCUS_MODE": "Odak Moduna Geç", "GO_TO_TASK_LIST": "Görev listesine git", "HELP": "Yardım", @@ -1953,7 +2096,9 @@ "SCHEDULE": "Takvim", "SEARCH": "Arama", "SETTINGS": "Ayarlar", + "SHARE_TASK_LIST_MARKDOWN": "Görev Listesini Paylaş", "SHOW_SEARCH_BAR": "Arama çubuğunu göster", + "SIDE_PANEL_MENU": "Yan Panel Menüsü", "TAGS": "Etiketler", "TASK_LIST": "Görev listesi", "TASKS": "Görevler", @@ -1962,8 +2107,8 @@ "TOGGLE_SHOW_NOTES": "Proje Notlarını Göster / Gizle", "TOGGLE_TRACK_TIME": "Başlat / Durdur takip süresi", "TRIGGER_SYNC": "Senkronizasyonu manuel olarak tetikle", - "WORKLOG": "Görev günlüğü", - "SIDE_PANEL_MENU": "Yan Panel Menüsü" + "UNPLAN_ALL_TASKS": "Tüm görevlerin plandan kaldır", + "WORKLOG": "Görev günlüğü" }, "MIGRATE": { "C_DOWNLOAD_BACKUP": "Eski verilerinizin bir yedeğini indirmek istiyor musunuz (Süper Üretkenlik'in eski sürümleriyle kullanılabilir)?", @@ -1974,6 +2119,10 @@ }, "PDS": { "ADD_TASKS_FROM_TODAY": "Bugünden itibaren görev ekleme", + "ARCHIVED_TASKS": { + "PLURAL": "{{count}} tamamlanmış ana görevler arşivlendi", + "SINGULAR": "{{count}} tamamlanmış ana görev arşivlendi" + }, "BREAK_LABEL": "Molalar (saat / saat)", "CELEBRATE": "Kutlamak için bir dakikanızı ayırın!", "CLEAR_ALL_CONTINUE": "Hepsini temizle ve devam et", @@ -1985,6 +2134,8 @@ "ESTIMATE_TOTAL": "Toplam tahmin:", "EVALUATE_DAY": "Değerlendir", "EXPORT_TASK_LIST": "Görev Listesini Dışa Aktar", + "END_OF_DAYS_RITUALS_PLACEHOLDER": "Günün son ritüellerinizi yazmak için bu alanı kullanabilirsiniz, hatırlatılmasını istediğiniz.", + "FOCUS_SUMMARY": "Odak Oturumları", "NO_TASKS": "Bugün için görev yok", "PLAN_TOMORROW": "Plan", "REVIEW_TASKS": "İnceleme", @@ -2007,6 +2158,106 @@ "TIME_SPENT_TODAY_BY_TAG": "Etikete Göre Bugün Harcanan Zaman", "WEEK": "Hafta" }, + "PLUGINS": { + "ACTION_TYPE_NOT_ALLOWED": "'{{type}}' işlem türüne izin verilmiyor", + "ALREADY_INITIALIZED": "Eklenti zaten başlatıldı", + "CANCEL": "İptal", + "CAPABILITIES": { + "ACCESS_FILES": "Sisteminizdeki dosyalara erişme ve değiştirme", + "RUN_COMMANDS": "Sistem komutlarını çalıştırma", + "USE_NODE_APIS": "Node.js API'lerini ve modüllerini kullanma" + }, + "CHOOSE_PLUGIN_FILE": "Eklenti Dosyası Seç", + "CLEAR_PLUGIN_CACHE": "Eklenti Önbelleğini Temizle", + "CODE_TOO_LARGE": "Eklenti kodu çok büyük (maksimum {{maxSize}}MB)", + "CONFIGURATION": "Konfigürasyon", + "CONFIGURE": "Yapılandır", + "CONFIRM_REMOVE": "\"{{name}}\" eklentisini kaldırmak istediğinize emin misiniz?", + "DISABLED": "Devre Dışı", + "ELECTRON_API_NOT_AVAILABLE": "Electron API'si kullanılamıyor", + "ENABLED": "Etkin", + "ERROR": "Hata", + "ERROR_LOADING_PLUGIN": "Eklenti yüklenirken hata oluştu", + "EXPERIMENTAL_WARNING": "Eklenti sistemi deneysel aşamadadır ve son derece dikkatli kullanılmalıdır.", + "EXPERIMENTAL_WARNING_TITLE": "Deneysel Özellik - Güvenlik Uyarısı", + "FAILED_TO_CLEAR_CACHE": "Eklenti önbelleği temizlenemedi", + "FAILED_TO_EXECUTE_SCRIPT": "Node.js betiği çalıştırılamadı", + "FAILED_TO_EXTRACT_ZIP": "Eklenti ZIP dosyası çıkarılamadı", + "FAILED_TO_INSTALL": "Eklenti yüklenemedi", + "FAILED_TO_LOAD": "Eklenti yüklenemedi", + "FAILED_TO_LOAD_CONFIG": "Eklenti yapılandırması yüklenemedi", + "FAILED_TO_REMOVE": "Eklenti kaldırılamadı", + "FAILED_TO_SAVE_CONFIG": "Eklenti yapılandırması kaydedilemedi", + "FILE_TOO_LARGE": "Dosya çok büyük ({{fileSize}}MB). Maksimum boyut {{maxSize}}MB", + "GO_BACK": "Geri Dön", + "GRANT_PERMISSION": "İzin Ver", + "HOOKS": "Kancalar", + "ID": "Kimlik:", + "INDEX_HTML_NOT_LOADED": "Eklenti index.html yüklenemedi", + "INSTALL_PLUGIN": "Eklenti Yükle", + "INSTALL_WARNING": "Bir eklenti yüklemeden önce, kaynağına güvendiğinizden ve talep ettiği izinleri anladığınızdan emin olun.", + "INSTALLING": "Yükleniyor...", + "LOADING_INTERFACE": "Eklenti arayüzü yükleniyor...", + "LOADING_PLUGIN": "Eklenti yükleniyor...", + "MANIFEST_NOT_FOUND": "ZIP dosyasında eklenti manifesti (manifest.json) bulunamadı", + "MANIFEST_TOO_LARGE": "Eklenti manifesti çok büyük (maksimum {{maxSize}}KB)", + "MENU_ENTRY_ICON_STRING": "Menü simgesi bir metin (string) olmalıdır", + "MENU_ENTRY_LABEL_REQUIRED": "Menü girdisi etiketi gereklidir", + "MENU_ENTRY_ONCLICK_REQUIRED": "Menü girdisi onClick işleyicisi gereklidir", + "MIN_VERSION": "Min. Sürüm:", + "NO_ADDITIONAL_INFO": "Ek bilgi yok", + "NO_CONTENT_PROVIDED": "İçerik sağlanmadı", + "NO_PLUGIN_CONTEXT_ACTION": "Eklentinin işlem yapma izni yok", + "NO_PLUGIN_CONTEXT_HEADER_BUTTON": "Eklentinin üst başlığa buton ekleme izni yok", + "NO_PLUGIN_CONTEXT_LOADING": "Eklenti verileri yüklemek için bağlam iznine sahip değil", + "NO_PLUGIN_CONTEXT_MENU_ENTRY": "Eklentinin menü girdisi ekleme izni yok", + "NO_PLUGIN_CONTEXT_NODE": "Eklentinin Node.js kodu çalıştırma izni yok", + "NO_PLUGIN_CONTEXT_PERSISTENCE": "Eklentinin veri kalıcılığı izni yok", + "NO_PLUGIN_CONTEXT_SHORTCUT": "Eklentinin kısayol kaydetme izni yok", + "NO_PLUGIN_CONTEXT_SIDE_PANEL": "Eklentinin yan panel butonu ekleme izni yok", + "NO_PLUGIN_CONTEXT_SYNC": "Eklentinin senkronizasyon izni yok", + "NO_PLUGIN_ID_PROVIDED_FOR_HTML": "HTML içeriği için eklenti kimliği sağlanmadı", + "NO_PLUGIN_MANIFEST_NODE": "Eklenti manifest dosyası Node.js modülünü içermiyor", + "NODE_EXECUTION_REQUIRED": "Bu eklenti, yalnızca masaüstü uygulamasında mevcut olan Node.js çalıştırmasını gerektirir.", + "NODE_ONLY_DESKTOP": "Node.js çalıştırması yalnızca masaüstü uygulamasında kullanılabilir", + "OK": "Tamam", + "PARENT_TASK_DOES_NOT_EXIST": "Üst görev mevcut değil", + "PARENT_TASK_NOT_FOUND": "'{{contextId}}' bağlamında üst görev bulunamadı", + "PERMISSIONS": "İzinler", + "PLEASE_SELECT_ZIP_FILE": "Lütfen bir ZIP dosyası seçin", + "PLUGIN_DIALOG_TITLE": "Eklenti Mesajı", + "PLUGIN_DOES_NOT_SUPPORT_IFRAME": "Bu eklenti iframe arayüzünü desteklemiyor", + "PLUGIN_ID_NOT_PROVIDED": "Eklenti kimliği sağlanmadı", + "PLUGIN_JS_NOT_FOUND": "Eklenti JavaScript dosyası (plugin.js) ZIP içinde bulunamadı", + "PLUGIN_NOT_FOUND": "Eklenti bulunamadı", + "PLUGIN_SYSTEM_FAILED_INIT": "Eklenti sistemi başlatılamadı", + "PROJECT_DOES_NOT_EXIST": "Proje mevcut değil", + "PROJECT_NOT_FOUND": "'{{contextId}}' projesi bulunamadı", + "RECOMMENDATION": "Yalnızca güvenilir kaynaklardan eklenti yükleyin ve mümkünse kodlarını inceleyin. Yeni eklentileri yüklemeden önce her zaman verilerinizi yedekleyin.", + "REMEMBER_CHOICE": "Bu eklenti için seçimimi hatırla", + "REMOVE": "Kaldır", + "RISK_DATA_ACCESS": "Eklentiler TÜM görevlerinizi, projelerinizi ve kişisel verilerinizi okuyabilir, değiştirebilir ve silebilir", + "RISK_MALICIOUS_CODE": "Kötü amaçlı eklentiler hassas bilgileri çalabilir veya verilerinizi bozabilir", + "RISK_PERFORMANCE": "Kötü yazılmış eklentiler performans sorunlarına veya çökmelere neden olabilir", + "RISK_SYSTEM_ACCESS": "Node.js izinli masaüstü eklentileri sistem komutları çalıştırabilir", + "SECURITY_WARNING": "Eklentiler verilerinize ve sisteminize önemli erişimlere sahiptir. Güvenilmeyen eklentilerin yüklenmesi ciddi güvenlik riskleri taşır:", + "SIDE_PANEL_LABEL_REQUIRED": "Yan panel buton etiketi gereklidir", + "SIDE_PANEL_ONCLICK_REQUIRED": "Yan panel buton onClick işleyicisi gereklidir", + "SYSTEM_ACCESS_REQUEST_DESC": "Bu eklenti, sisteminizde Node.js kodu çalıştırma izni istiyor. Bu izin, aşağıdakilere olanak tanıyacaktır:", + "SYSTEM_ACCESS_REQUEST_TITLE": "Sistem Erişim Talebi", + "TAGS_DO_NOT_EXIST": "Bir veya daha fazla etiket mevcut değil", + "TASK_NOT_FOUND": "{{taskId}} kimliğine sahip görev bulunamadı", + "TASKS_NOT_IN_PROJECT": "Bir veya daha fazla görev '{{contextId}}' projesinde değil", + "TASKS_NOT_SUBTASKS": "Bir veya daha fazla görev '{{contextId}}' alt görevi değil", + "TOGGLE_PLUGIN": "{{pluginName}} Aç/Kapat", + "TRUST_WARNING": "Yalnızca bu eklentiye güveniyorsanız izin verin", + "TYPE": "Tür: {{type}}", + "UNABLE_TO_PERSIST_DATA": "Eklenti deposuna veri kaydedilemedi", + "UNKNOWN_ERROR": "Bilinmeyen bir hata oluştu", + "UPLOAD_PLUGIN_INSTRUCTION": "Yüklemek için bir eklenti ZIP dosyası yükleyin:", + "USER_DECLINED_NODE_PERMISSION": "Kullanıcı Node.js çalıştırma iznini reddetti", + "VALIDATION_FAILED": "Doğrulama başarısız oldu" + }, "PM": { "TITLE": "Proje İstatistikleri" }, @@ -2024,106 +2275,6 @@ "TAG_SETTINGS": "Etiket Ayarları", "TOGGLE_DARK_MODE": "Koyu Modu Değiştir" }, - "PLUGINS": { - "INSTALL_PLUGIN": "Eklenti Yükle", - "UPLOAD_PLUGIN_INSTRUCTION": "Yüklemek için bir eklenti ZIP dosyası yükleyin:", - "CHOOSE_PLUGIN_FILE": "Eklenti Dosyası Seç", - "INSTALLING": "Yükleniyor...", - "CLEAR_PLUGIN_CACHE": "Eklenti Önbelleğini Temizle", - "REMOVE": "Kaldır", - "HOOKS": "Kancalar", - "PERMISSIONS": "İzinler", - "ID": "Kimlik:", - "MIN_VERSION": "Min. Sürüm:", - "NODE_EXECUTION_REQUIRED": "Bu eklenti, yalnızca masaüstü uygulamasında mevcut olan Node.js çalıştırmasını gerektirir.", - "TOGGLE_PLUGIN": "{{pluginName}} Aç/Kapat", - "EXPERIMENTAL_WARNING_TITLE": "Deneysel Özellik - Güvenlik Uyarısı", - "EXPERIMENTAL_WARNING": "Eklenti sistemi deneysel aşamadadır ve son derece dikkatli kullanılmalıdır.", - "SECURITY_WARNING": "Eklentiler verilerinize ve sisteminize önemli erişimlere sahiptir. Güvenilmeyen eklentilerin yüklenmesi ciddi güvenlik riskleri taşır:", - "RISK_DATA_ACCESS": "Eklentiler TÜM görevlerinizi, projelerinizi ve kişisel verilerinizi okuyabilir, değiştirebilir ve silebilir", - "RISK_MALICIOUS_CODE": "Kötü amaçlı eklentiler hassas bilgileri çalabilir veya verilerinizi bozabilir", - "RISK_SYSTEM_ACCESS": "Node.js izinli masaüstü eklentileri sistem komutları çalıştırabilir", - "RISK_PERFORMANCE": "Kötü yazılmış eklentiler performans sorunlarına veya çökmelere neden olabilir", - "RECOMMENDATION": "Yalnızca güvenilir kaynaklardan eklenti yükleyin ve mümkünse kodlarını inceleyin. Yeni eklentileri yüklemeden önce her zaman verilerinizi yedekleyin.", - "CONFIGURATION": "Konfigürasyon", - "CONFIGURE": "Yapılandır", - "INSTALL_WARNING": "Bir eklenti yüklemeden önce, kaynağına güvendiğinizden ve talep ettiği izinleri anladığınızdan emin olun.", - "SYSTEM_ACCESS_REQUEST_TITLE": "Sistem Erişim Talebi", - "SYSTEM_ACCESS_REQUEST_DESC": "Bu eklenti, sisteminizde Node.js kodu çalıştırma izni istiyor. Bu izin, aşağıdakilere olanak tanıyacaktır:", - "GRANT_PERMISSION": "İzin Ver", - "CANCEL": "İptal", - "REMEMBER_CHOICE": "Bu eklenti için seçimimi hatırla", - "TRUST_WARNING": "Yalnızca bu eklentiye güveniyorsanız izin verin", - "CAPABILITIES": { - "ACCESS_FILES": "Sisteminizdeki dosyalara erişme ve değiştirme", - "RUN_COMMANDS": "Sistem komutlarını çalıştırma", - "USE_NODE_APIS": "Node.js API'lerini ve modüllerini kullanma" - }, - "ERROR": "Hata", - "LOADING_PLUGIN": "Eklenti yükleniyor...", - "ENABLED": "Etkin", - "DISABLED": "Devre Dışı", - "PLEASE_SELECT_ZIP_FILE": "Lütfen bir ZIP dosyası seçin", - "FILE_TOO_LARGE": "Dosya çok büyük ({{fileSize}}MB). Maksimum boyut {{maxSize}}MB", - "FAILED_TO_INSTALL": "Eklenti yüklenemedi", - "FAILED_TO_CLEAR_CACHE": "Eklenti önbelleği temizlenemedi", - "CONFIRM_REMOVE": "\"{{name}}\" eklentisini kaldırmak istediğinize emin misiniz?", - "FAILED_TO_REMOVE": "Eklenti kaldırılamadı", - "TYPE": "Tür: {{type}}", - "NO_ADDITIONAL_INFO": "Ek bilgi yok", - "NO_PLUGIN_ID_PROVIDED_FOR_HTML": "HTML içeriği için eklenti kimliği sağlanmadı", - "PROJECT_NOT_FOUND": "'{{contextId}}' projesi bulunamadı", - "TASKS_NOT_IN_PROJECT": "Bir veya daha fazla görev '{{contextId}}' projesinde değil", - "PARENT_TASK_NOT_FOUND": "'{{contextId}}' bağlamında üst görev bulunamadı", - "TASK_NOT_FOUND": "{{taskId}} kimliğine sahip görev bulunamadı", - "TASKS_NOT_SUBTASKS": "Bir veya daha fazla görev '{{contextId}}' alt görevi değil", - "NO_PLUGIN_CONTEXT_PERSISTENCE": "Eklentinin veri kalıcılığı izni yok", - "UNABLE_TO_PERSIST_DATA": "Eklenti deposuna veri kaydedilemedi", - "NO_PLUGIN_CONTEXT_LOADING": "Eklenti verileri yüklemek için bağlam iznine sahip değil", - "NO_PLUGIN_CONTEXT_SYNC": "Eklentinin senkronizasyon izni yok", - "NO_PLUGIN_CONTEXT_HEADER_BUTTON": "Eklentinin üst başlığa buton ekleme izni yok", - "NO_PLUGIN_CONTEXT_MENU_ENTRY": "Eklentinin menü girdisi ekleme izni yok", - "MENU_ENTRY_LABEL_REQUIRED": "Menü girdisi etiketi gereklidir", - "MENU_ENTRY_ONCLICK_REQUIRED": "Menü girdisi onClick işleyicisi gereklidir", - "MENU_ENTRY_ICON_STRING": "Menü simgesi bir metin (string) olmalıdır", - "NO_PLUGIN_CONTEXT_SIDE_PANEL": "Eklentinin yan panel butonu ekleme izni yok", - "SIDE_PANEL_LABEL_REQUIRED": "Yan panel buton etiketi gereklidir", - "SIDE_PANEL_ONCLICK_REQUIRED": "Yan panel buton onClick işleyicisi gereklidir", - "NO_PLUGIN_CONTEXT_SHORTCUT": "Eklentinin kısayol kaydetme izni yok", - "PROJECT_DOES_NOT_EXIST": "Proje mevcut değil", - "TAGS_DO_NOT_EXIST": "Bir veya daha fazla etiket mevcut değil", - "PARENT_TASK_DOES_NOT_EXIST": "Üst görev mevcut değil", - "VALIDATION_FAILED": "Doğrulama başarısız oldu", - "NO_PLUGIN_CONTEXT_ACTION": "Eklentinin işlem yapma izni yok", - "ACTION_TYPE_NOT_ALLOWED": "'{{type}}' işlem türüne izin verilmiyor", - "NODE_ONLY_DESKTOP": "Node.js çalıştırması yalnızca masaüstü uygulamasında kullanılabilir", - "NO_PLUGIN_CONTEXT_NODE": "Eklentinin Node.js kodu çalıştırma izni yok", - "NO_PLUGIN_MANIFEST_NODE": "Eklenti manifest dosyası Node.js modülünü içermiyor", - "ELECTRON_API_NOT_AVAILABLE": "Electron API'si kullanılamıyor", - "FAILED_TO_EXECUTE_SCRIPT": "Node.js betiği çalıştırılamadı", - "ALREADY_INITIALIZED": "Eklenti zaten başlatıldı", - "USER_DECLINED_NODE_PERMISSION": "Kullanıcı Node.js çalıştırma iznini reddetti", - "FAILED_TO_EXTRACT_ZIP": "Eklenti ZIP dosyası çıkarılamadı", - "MANIFEST_NOT_FOUND": "ZIP dosyasında eklenti manifesti (manifest.json) bulunamadı", - "MANIFEST_TOO_LARGE": "Eklenti manifesti çok büyük (maksimum {{maxSize}}KB)", - "PLUGIN_JS_NOT_FOUND": "Eklenti JavaScript dosyası (plugin.js) ZIP içinde bulunamadı", - "CODE_TOO_LARGE": "Eklenti kodu çok büyük (maksimum {{maxSize}}MB)", - "UNKNOWN_ERROR": "Bilinmeyen bir hata oluştu", - "PLUGIN_DIALOG_TITLE": "Eklenti Mesajı", - "NO_CONTENT_PROVIDED": "İçerik sağlanmadı", - "OK": "Tamam", - "LOADING_INTERFACE": "Eklenti arayüzü yükleniyor...", - "ERROR_LOADING_PLUGIN": "Eklenti yüklenirken hata oluştu", - "GO_BACK": "Geri Dön", - "FAILED_TO_LOAD": "Eklenti yüklenemedi", - "FAILED_TO_LOAD_CONFIG": "Eklenti yapılandırması yüklenemedi", - "FAILED_TO_SAVE_CONFIG": "Eklenti yapılandırması kaydedilemedi", - "PLUGIN_ID_NOT_PROVIDED": "Eklenti kimliği sağlanmadı", - "PLUGIN_SYSTEM_FAILED_INIT": "Eklenti sistemi başlatılamadı", - "PLUGIN_NOT_FOUND": "Eklenti bulunamadı", - "PLUGIN_DOES_NOT_SUPPORT_IFRAME": "Bu eklenti iframe arayüzünü desteklemiyor", - "INDEX_HTML_NOT_LOADED": "Eklenti index.html yüklenemedi" - }, "SCHEDULE": { "LAST": "Son:", "NEXT": "Sonraki:", @@ -2153,6 +2304,24 @@ "teal": "turkuaz", "yellow": "sarı" }, + "USER_PROFILES": { + "ACTIVE": "Aktif", + "CANCEL": "İptal", + "CLOSE": "Kapat", + "CREATE": "Oluştur", + "CREATE_NEW_PROFILE": "Yeni profil oluştur", + "CREATED": "Oluşturuldu:", + "DELETE_PROFILE": "Profili sil", + "DIALOG_TITLE": "Kullanıcı profillerini yönet", + "EXISTING_PROFILES": "Mevcut profiller", + "EXPORT_PROFILE": "Profili dışa aktar", + "MANAGE_PROFILES": "Profilleri yönet...", + "OPEN_PROFILES_FOLDER": "Profiller klasörünü aç", + "PROFILE_NAME": "Profil adı", + "PROFILE_NAME_PLACEHOLDER": "örn. İş, Kişisel", + "RENAME": "Yeniden adlandır", + "SAVE": "Kaydet" + }, "V": { "E_1TO10": "Lütfen 1 ile 10 arasında bir değer girin", "E_DATETIME": "Girilen değer bir tarih değil!", @@ -2164,24 +2333,6 @@ "E_PATTERN": "Geçersiz Giriş", "E_REQUIRED": "Bu alan gereklidir" }, - "USER_PROFILES": { - "DIALOG_TITLE": "Kullanıcı profillerini yönet", - "CREATE_NEW_PROFILE": "Yeni profil oluştur", - "PROFILE_NAME": "Profil adı", - "PROFILE_NAME_PLACEHOLDER": "örn. İş, Kişisel", - "CREATE": "Oluştur", - "EXISTING_PROFILES": "Mevcut profiller", - "ACTIVE": "Aktif", - "CREATED": "Oluşturuldu:", - "SAVE": "Kaydet", - "CANCEL": "İptal", - "RENAME": "Yeniden adlandır", - "EXPORT_PROFILE": "Profili dışa aktar", - "DELETE_PROFILE": "Profili sil", - "OPEN_PROFILES_FOLDER": "Profiller klasörünü aç", - "CLOSE": "Kapat", - "MANAGE_PROFILES": "Profilleri yönet..." - }, "WW": { "ADD_MORE": "Daha ekle", "ADD_SCHEDULED_FOR_TOMORROW": "Yarın için planlanan görevleri ekleyin ({{nr}})", @@ -2189,12 +2340,12 @@ "DONE_TASKS": "Yapılan Görevler", "DONE_TASKS_IN_ARCHIVE": "Burada şu anda tamamlanmış görev yok, ancak bazıları zaten arşivlendi.", "ESTIMATE_REMAINING": "Tahmini kalan süre:", - "LATER_TODAY": "Bugün Daha Sonra", "FINISH_DAY": "Günü bitir", "FINISH_DAY_FOR_PROJECT": "Günü bu proje için bitir", "FINISH_DAY_FOR_TAG": "Günü bu etiket için bitir", "FINISH_DAY_TOOLTIP": "Gününüzü değerlendirin, yapılan tüm görevleri arşive taşıyın (isteğe bağlı olarak) ve/veya bir sonraki gününüzü planlayın.", "HELP_PROCRASTINATION": "Erteliyorum yardım et!", + "LATER_TODAY": "Bugün Daha Sonra", "MOVE_DONE_TO_ARCHIVE": "Arşive taşıma işlemi tamamlandı", "NO_DONE_TASKS": "Şu anda yapılmış bir görev yok", "NO_PLANNED_TASK_ALL_DONE": "tüm görevler tamamlandı", diff --git a/src/environments/versions.ts b/src/environments/versions.ts index 32168c53e..c1645520a 100644 --- a/src/environments/versions.ts +++ b/src/environments/versions.ts @@ -1,6 +1,6 @@ // this file is automatically generated by git.version.ts script export const versions = { - version: '16.7.2', + version: '16.7.3', revision: 'NO_REV', branch: 'NO_BRANCH', };