Compare commits

...

11 Commits

Author SHA1 Message Date
Eli Bosley
291fb24e16 Refactor mutation resolver to include NotificationMutations and remove unused import in notifications model 2025-11-23 10:49:57 -05:00
Eli Bosley
53851e4c9d Refactor notification jobs to shared background service 2025-11-22 09:34:54 -05:00
Pujit Mehrotra
31af99e52f chore: for releases, use tag as source of truth for API_VERSION (#1804) 2025-11-21 10:16:00 -05:00
Eli Bosley
933cefa020 New Crowdin updates (#1803)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Localization**
* Updated translations across 24 languages including Arabic, Bengali,
German, Spanish, French, Japanese, Korean, Portuguese, and Russian for
OS update eligibility messages, driver update status notifications, and
license/trial key expiration messaging to improve international user
experience.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-11-21 10:09:30 -05:00
github-actions[bot]
375dcd0598 chore(main): release 4.27.2 (#1802)
🤖 I have created a release *beep* *boop*
---


## [4.27.2](https://github.com/unraid/api/compare/v4.27.1...v4.27.2)
(2025-11-21)


### Bug Fixes

* issue with header flashing + issue with trial date
([64875ed](64875edbba))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-11-20 21:16:14 -05:00
Eli Bosley
64875edbba fix: issue with header flashing + issue with trial date
Removed an empty line in the web testing rules.
2025-11-20 21:08:07 -05:00
github-actions[bot]
330e81a484 chore(main): release 4.27.1 (#1797)
🤖 I have created a release *beep* *boop*
---


## [4.27.1](https://github.com/unraid/api/compare/v4.27.0...v4.27.1)
(2025-11-21)


### Bug Fixes

* missing translations for expiring trials
([#1800](https://github.com/unraid/api/issues/1800))
([36c1049](36c104915e))
* resolve header flash when background color is set
([#1796](https://github.com/unraid/api/issues/1796))
([dc9a036](dc9a036c73))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-11-20 19:44:39 -05:00
Eli Bosley
b8f0fdf8d2 New Crowdin updates (#1801) 2025-11-20 19:39:45 -05:00
Eli Bosley
36c104915e fix: missing translations for expiring trials (#1800)
- Removed translation function calls from the UI components for reboot
type text, replacing them with direct references to the computed
properties.
- Enhanced ineligible update messages by integrating localization for
various conditions, ensuring clearer user feedback regarding update
eligibility.
- Added new localization strings for ineligible update scenarios in the
English locale file.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added new localization keys for OS update eligibility, reboot labels,
changelog link, and expanded uptime/trial expiry messages.

* **Bug Fixes**
* Restored translated strings and added locale-aware release date
formatting for update/ineligible messaging and badges.

* **Theme & UI**
* Streamlined theme initialization and server-driven theme application;
removed legacy CSS-variable persistence and adjusted dark/banner
behavior.

* **Tests**
* Added i18n and date/locale formatting tests and improved
local-storage-like test mocks.

* **Chores**
* Removed an auto-registered global component and strengthened
script/theme initialization and CSS-variable validation.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-11-20 19:30:39 -05:00
Eli Bosley
dc9a036c73 fix: resolve header flash when background color is set (#1796)
## Summary
- rely on the existing Pinia persisted state instead of manual
localStorage hydration
- reapply CSS variables after persisted hydration so custom header
colors show immediately

## Testing
- Not run (not requested)


------
[Codex
Task](https://chatgpt.com/codex/tasks/task_e_691e5a1d052c8323973847eb5833fbb9)
2025-11-19 19:43:45 -05:00
github-actions[bot]
c71b0487ad chore(main): release 4.27.0 (#1795)
🤖 I have created a release *beep* *boop*
---


## [4.27.0](https://github.com/unraid/api/compare/v4.26.2...v4.27.0)
(2025-11-19)


### Features

* remove Unraid API log download functionality
([#1793](https://github.com/unraid/api/issues/1793))
([e4a9b82](e4a9b8291b))


### Bug Fixes

* auto-uninstallation of connect api plugin
([#1791](https://github.com/unraid/api/issues/1791))
([e734043](e7340431a5))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-11-19 14:35:57 -05:00
64 changed files with 1749 additions and 708 deletions

View File

@@ -241,4 +241,3 @@ const pinia = createTestingPinia({
- Set initial state for focused testing
- Test computed properties by accessing them directly
- Verify state changes by updating the store

View File

@@ -78,7 +78,21 @@ jobs:
GIT_SHA=$(git rev-parse --short HEAD)
IS_TAGGED=$(git describe --tags --abbrev=0 --exact-match || echo '')
PACKAGE_LOCK_VERSION=$(jq -r '.version' package.json)
API_VERSION=$([[ -n "$IS_TAGGED" ]] && echo "$PACKAGE_LOCK_VERSION" || echo "${PACKAGE_LOCK_VERSION}+${GIT_SHA}")
# For release builds, trust the release tag version to avoid stale checkouts
if [ "${{ inputs.RELEASE_CREATED }}" = "true" ] && [ -n "${{ inputs.RELEASE_TAG }}" ]; then
TAG_VERSION="${{ inputs.RELEASE_TAG }}"
TAG_VERSION="${TAG_VERSION#v}" # trim leading v if present
if [ "$TAG_VERSION" != "$PACKAGE_LOCK_VERSION" ]; then
echo "::warning::Release tag version ($TAG_VERSION) does not match package.json version ($PACKAGE_LOCK_VERSION). Using tag version for TXZ naming."
fi
API_VERSION="$TAG_VERSION"
else
API_VERSION=$([[ -n "$IS_TAGGED" ]] && echo "$PACKAGE_LOCK_VERSION" || echo "${PACKAGE_LOCK_VERSION}+${GIT_SHA}")
fi
echo "API_VERSION=${API_VERSION}" >> $GITHUB_OUTPUT
- name: Install dependencies

View File

@@ -1 +1 @@
{".":"4.26.2"}
{".":"4.27.2"}

View File

@@ -6,11 +6,6 @@
/* Default/White Theme */
.Theme--white {
--header-text-primary: #ffffff;
--header-text-secondary: #999999;
--header-background-color: #1c1b1b;
--header-gradient-start: rgba(28, 27, 27, 0);
--header-gradient-end: rgba(28, 27, 27, 0.7);
--color-border: #383735;
--color-alpha: #ff8c2f;
--color-beta: #1c1b1b;
@@ -21,11 +16,6 @@
/* Black Theme */
.Theme--black,
.Theme--black.dark {
--header-text-primary: #1c1b1b;
--header-text-secondary: #999999;
--header-background-color: #f2f2f2;
--header-gradient-start: rgba(242, 242, 242, 0);
--header-gradient-end: rgba(242, 242, 242, 0.7);
--color-border: #e0e0e0;
--color-alpha: #ff8c2f;
--color-beta: #f2f2f2;
@@ -35,11 +25,6 @@
/* Gray Theme */
.Theme--gray {
--header-text-primary: #ffffff;
--header-text-secondary: #999999;
--header-background-color: #1c1b1b;
--header-gradient-start: rgba(28, 27, 27, 0);
--header-gradient-end: rgba(28, 27, 27, 0.7);
--color-border: #383735;
--color-alpha: #ff8c2f;
--color-beta: #383735;
@@ -49,11 +34,6 @@
/* Azure Theme */
.Theme--azure {
--header-text-primary: #1c1b1b;
--header-text-secondary: #999999;
--header-background-color: #f2f2f2;
--header-gradient-start: rgba(242, 242, 242, 0);
--header-gradient-end: rgba(242, 242, 242, 0.7);
--color-border: #5a8bb8;
--color-alpha: #ff8c2f;
--color-beta: #e7f2f8;
@@ -65,33 +45,3 @@
.dark {
--color-border: #383735;
}
/*
* Dynamic color variables for user overrides from GraphQL
* These are set via JavaScript and override the theme defaults
* Using :root with class for higher specificity to override theme classes
*/
:root.has-custom-header-text {
--header-text-primary: var(--custom-header-text-primary);
--color-header-text-primary: var(--custom-header-text-primary);
}
:root.has-custom-header-meta {
--header-text-secondary: var(--custom-header-text-secondary);
--color-header-text-secondary: var(--custom-header-text-secondary);
}
:root.has-custom-header-bg,
.has-custom-header-bg.Theme--black,
.has-custom-header-bg.Theme--black.dark,
.has-custom-header-bg.Theme--white,
.has-custom-header-bg.Theme--white.dark,
.has-custom-header-bg.Theme--gray,
.has-custom-header-bg.Theme--azure {
--header-background-color: var(--custom-header-background-color);
--color-header-background: var(--custom-header-background-color);
--header-gradient-start: var(--custom-header-gradient-start);
--header-gradient-end: var(--custom-header-gradient-end);
--color-header-gradient-start: var(--custom-header-gradient-start);
--color-header-gradient-end: var(--custom-header-gradient-end);
}

View File

@@ -1,5 +1,32 @@
# Changelog
## [4.27.2](https://github.com/unraid/api/compare/v4.27.1...v4.27.2) (2025-11-21)
### Bug Fixes
* issue with header flashing + issue with trial date ([64875ed](https://github.com/unraid/api/commit/64875edbba786a0d1ba0113c9e9a3d38594eafcc))
## [4.27.1](https://github.com/unraid/api/compare/v4.27.0...v4.27.1) (2025-11-21)
### Bug Fixes
* missing translations for expiring trials ([#1800](https://github.com/unraid/api/issues/1800)) ([36c1049](https://github.com/unraid/api/commit/36c104915ece203a3cac9e1a13e0c325e536a839))
* resolve header flash when background color is set ([#1796](https://github.com/unraid/api/issues/1796)) ([dc9a036](https://github.com/unraid/api/commit/dc9a036c73d8ba110029364e0d044dc24c7d0dfa))
## [4.27.0](https://github.com/unraid/api/compare/v4.26.2...v4.27.0) (2025-11-19)
### Features
* remove Unraid API log download functionality ([#1793](https://github.com/unraid/api/issues/1793)) ([e4a9b82](https://github.com/unraid/api/commit/e4a9b8291b049752a9ff59b17ff50cf464fe0535))
### Bug Fixes
* auto-uninstallation of connect api plugin ([#1791](https://github.com/unraid/api/issues/1791)) ([e734043](https://github.com/unraid/api/commit/e7340431a58821ec1b4f5d1b452fba6613b01fa5))
## [4.26.2](https://github.com/unraid/api/compare/v4.26.1...v4.26.2) (2025-11-19)

View File

@@ -1,6 +1,6 @@
{
"name": "@unraid/api",
"version": "4.26.2",
"version": "4.27.2",
"main": "src/cli/index.ts",
"type": "module",
"corepack": {

View File

@@ -40,6 +40,11 @@ export class RCloneMutations {
deleteRCloneRemote!: boolean;
}
@ObjectType({
description: 'Notification related mutations',
})
export class NotificationMutations {}
@ObjectType()
export class RootMutations {
@Field(() => ArrayMutations, { description: 'Array related mutations' })
@@ -59,4 +64,7 @@ export class RootMutations {
@Field(() => RCloneMutations, { description: 'RClone related mutations' })
rclone: RCloneMutations = new RCloneMutations();
@Field(() => NotificationMutations, { description: 'Notification related mutations' })
notifications: NotificationMutations = new NotificationMutations();
}

View File

@@ -4,6 +4,7 @@ import {
ApiKeyMutations,
ArrayMutations,
DockerMutations,
NotificationMutations,
ParityCheckMutations,
RCloneMutations,
RootMutations,
@@ -41,4 +42,9 @@ export class RootMutationsResolver {
rclone(): RCloneMutations {
return new RCloneMutations();
}
@Mutation(() => NotificationMutations, { name: 'notifications' })
notifications(): NotificationMutations {
return new NotificationMutations();
}
}

View File

@@ -1,4 +1,4 @@
import { Field, InputType, Int, ObjectType, registerEnumType } from '@nestjs/graphql';
import { Field, ID, InputType, Int, ObjectType, registerEnumType } from '@nestjs/graphql';
import { Node } from '@unraid/shared/graphql.model.js';
import { IsEnum, IsInt, IsNotEmpty, IsOptional, IsString, Min } from 'class-validator';
@@ -14,6 +14,18 @@ export enum NotificationImportance {
WARNING = 'WARNING',
}
export enum NotificationJobState {
QUEUED = 'QUEUED',
RUNNING = 'RUNNING',
SUCCEEDED = 'SUCCEEDED',
FAILED = 'FAILED',
}
export enum NotificationJobOperation {
ARCHIVE_ALL = 'ARCHIVE_ALL',
DELETE = 'DELETE',
}
// Register enums with GraphQL
registerEnumType(NotificationType, {
name: 'NotificationType',
@@ -23,6 +35,14 @@ registerEnumType(NotificationImportance, {
name: 'NotificationImportance',
});
registerEnumType(NotificationJobState, {
name: 'NotificationJobState',
});
registerEnumType(NotificationJobOperation, {
name: 'NotificationJobOperation',
});
@InputType('NotificationFilter')
export class NotificationFilter {
@Field(() => NotificationImportance, { nullable: true })
@@ -164,4 +184,28 @@ export class Notifications extends Node {
@Field(() => [Notification])
@IsNotEmpty()
list!: Notification[];
@Field(() => NotificationJob, { nullable: true })
job?: NotificationJob;
}
@ObjectType()
export class NotificationJob {
@Field(() => ID)
id!: string;
@Field(() => NotificationJobOperation)
operation!: NotificationJobOperation;
@Field(() => NotificationJobState)
state!: NotificationJobState;
@Field(() => Int)
processed!: number;
@Field(() => Int)
total!: number;
@Field({ nullable: true })
error?: string | null;
}

View File

@@ -0,0 +1,61 @@
import { Args, ResolveField, Resolver } from '@nestjs/graphql';
import { AuthAction, Resource } from '@unraid/shared/graphql.model.js';
import { PrefixedID } from '@unraid/shared/prefixed-id-scalar.js';
import { UsePermissions } from '@unraid/shared/use-permissions.directive.js';
import { AppError } from '@app/core/errors/app-error.js';
import {
NotificationImportance,
NotificationJob,
NotificationJobState,
NotificationMutations,
NotificationOverview,
NotificationType,
} from '@app/unraid-api/graph/resolvers/notifications/notifications.model.js';
import { NotificationsService } from '@app/unraid-api/graph/resolvers/notifications/notifications.service.js';
@Resolver(() => NotificationMutations)
export class NotificationMutationsResolver {
constructor(private readonly notificationsService: NotificationsService) {}
@ResolveField(() => NotificationOverview)
@UsePermissions({
action: AuthAction.DELETE_ANY,
resource: Resource.NOTIFICATIONS,
})
public async delete(
@Args('id', { type: () => PrefixedID }) id: string,
@Args('type', { type: () => NotificationType }) type: NotificationType
): Promise<NotificationOverview> {
const { overview } = await this.notificationsService.deleteNotification({ id, type });
return overview;
}
@ResolveField(() => NotificationJob)
@UsePermissions({
action: AuthAction.DELETE_ANY,
resource: Resource.NOTIFICATIONS,
})
public async startArchiveAll(
@Args('importance', { type: () => NotificationImportance, nullable: true })
importance?: NotificationImportance
): Promise<NotificationJob> {
return this.notificationsService.startArchiveAllJob(importance);
}
@ResolveField(() => NotificationJob)
@UsePermissions({
action: AuthAction.DELETE_ANY,
resource: Resource.NOTIFICATIONS,
})
public async startDeleteAll(
@Args('type', { type: () => NotificationType, nullable: true }) type?: NotificationType
): Promise<NotificationJob> {
const job = await this.notificationsService.startDeleteAllJob(type);
if (job.state === NotificationJobState.FAILED) {
throw new AppError(job.error ?? 'Failed to delete notifications', 500);
}
return job;
}
}

View File

@@ -11,6 +11,7 @@ import {
NotificationData,
NotificationFilter,
NotificationImportance,
NotificationJob,
NotificationOverview,
Notifications,
NotificationType,
@@ -41,6 +42,11 @@ export class NotificationsResolver {
return this.notificationsService.getOverview();
}
@ResolveField(() => NotificationJob, { nullable: true })
public job(@Args('id') jobId: string): NotificationJob {
return this.notificationsService.getJob(jobId);
}
@ResolveField(() => [Notification])
public async list(
@Args('filter', { type: () => NotificationFilter })

View File

@@ -24,10 +24,14 @@ import {
NotificationData,
NotificationFilter,
NotificationImportance,
NotificationJob,
NotificationJobOperation,
NotificationJobState,
NotificationOverview,
NotificationType,
} from '@app/unraid-api/graph/resolvers/notifications/notifications.model.js';
import { validateObject } from '@app/unraid-api/graph/resolvers/validation.utils.js';
import { BackgroundJobsService } from '@app/unraid-api/graph/services/background-jobs.service.js';
import { SortFn } from '@app/unraid-api/types/util.js';
import { batchProcess, formatDatetime, isFulfilled, isRejected, unraidTimestamp } from '@app/utils.js';
@@ -55,7 +59,36 @@ export class NotificationsService {
},
};
constructor() {
private createJob(operation: NotificationJobOperation, total: number): NotificationJob {
const state = total === 0 ? NotificationJobState.SUCCEEDED : NotificationJobState.QUEUED;
return this.backgroundJobs.createJob<NotificationJobOperation, NotificationJobState>({
operation,
total,
initialState: state,
prefix: operation.toLowerCase(),
});
}
private updateJob(job: NotificationJob) {
this.backgroundJobs.updateJob(job);
}
private async runJob(job: NotificationJob, work: () => Promise<void>) {
await this.backgroundJobs.runJob<NotificationJobState>({
job,
work,
runningState: NotificationJobState.RUNNING,
successState: NotificationJobState.SUCCEEDED,
failureState: NotificationJobState.FAILED,
logContext: 'notification-job',
});
}
public getJob(jobId: string): NotificationJob {
return this.backgroundJobs.getJob<NotificationJobOperation, NotificationJobState>(jobId);
}
constructor(private readonly backgroundJobs: BackgroundJobsService) {
this.path = getters.dynamix().notify!.path;
void this.getNotificationsWatcher(this.path);
}
@@ -323,6 +356,30 @@ export class NotificationsService {
return this.getOverview();
}
public async startDeleteAllJob(type?: NotificationType): Promise<NotificationJob> {
const targets = type ? [type] : [NotificationType.ARCHIVE, NotificationType.UNREAD];
const queue: Array<{ id: string; type: NotificationType }> = [];
for (const targetType of targets) {
const ids = await this.listFilesInFolder(this.paths()[targetType]);
ids.forEach((id) => queue.push({ id, type: targetType }));
}
const job = this.createJob(NotificationJobOperation.DELETE, queue.length);
setImmediate(() =>
this.runJob(job, async () => {
for (const entry of queue) {
await this.deleteNotification({ id: entry.id, type: entry.type });
job.processed += 1;
this.updateJob(job);
}
})
);
return job;
}
/**
* Deletes all notifications from disk while preserving the directory structure.
* Resets overview stats to zero.
@@ -485,6 +542,35 @@ export class NotificationsService {
return { ...stats, overview: overviewSnapshot };
}
public async startArchiveAllJob(importance?: NotificationImportance): Promise<NotificationJob> {
const { UNREAD } = this.paths();
const unreads = await this.listFilesInFolder(UNREAD);
const [notifications] = await this.loadNotificationsFromPaths(
unreads,
importance ? { importance } : {}
);
const job = this.createJob(NotificationJobOperation.ARCHIVE_ALL, notifications.length);
setImmediate(() =>
this.runJob(job, async () => {
const overviewSnapshot = this.getOverview();
const archive = this.moveNotification({
from: NotificationType.UNREAD,
to: NotificationType.ARCHIVE,
snapshot: overviewSnapshot,
});
for (const notification of notifications) {
await archive(notification);
job.processed += 1;
this.updateJob(job);
}
})
);
return job;
}
public async unarchiveAll(importance?: NotificationImportance) {
const { ARCHIVE } = this.paths();

View File

@@ -15,6 +15,7 @@ import { InfoModule } from '@app/unraid-api/graph/resolvers/info/info.module.js'
import { LogsModule } from '@app/unraid-api/graph/resolvers/logs/logs.module.js';
import { MetricsModule } from '@app/unraid-api/graph/resolvers/metrics/metrics.module.js';
import { RootMutationsResolver } from '@app/unraid-api/graph/resolvers/mutation/mutation.resolver.js';
import { NotificationMutationsResolver } from '@app/unraid-api/graph/resolvers/notifications/notifications.mutations.resolver.js';
import { NotificationsResolver } from '@app/unraid-api/graph/resolvers/notifications/notifications.resolver.js';
import { NotificationsService } from '@app/unraid-api/graph/resolvers/notifications/notifications.service.js';
import { OnlineResolver } from '@app/unraid-api/graph/resolvers/online/online.resolver.js';
@@ -29,6 +30,7 @@ import { VarsResolver } from '@app/unraid-api/graph/resolvers/vars/vars.resolver
import { VmMutationsResolver } from '@app/unraid-api/graph/resolvers/vms/vms.mutations.resolver.js';
import { VmsResolver } from '@app/unraid-api/graph/resolvers/vms/vms.resolver.js';
import { VmsService } from '@app/unraid-api/graph/resolvers/vms/vms.service.js';
import { BackgroundJobsService } from '@app/unraid-api/graph/services/background-jobs.service.js';
import { ServicesModule } from '@app/unraid-api/graph/services/services.module.js';
import { ServicesResolver } from '@app/unraid-api/graph/services/services.resolver.js';
import { SharesResolver } from '@app/unraid-api/graph/shares/shares.resolver.js';
@@ -57,8 +59,10 @@ import { MeResolver } from '@app/unraid-api/graph/user/user.resolver.js';
ConfigResolver,
FlashResolver,
MeResolver,
NotificationMutationsResolver,
NotificationsResolver,
NotificationsService,
BackgroundJobsService,
OnlineResolver,
OwnerResolver,
RegistrationResolver,

View File

@@ -0,0 +1,94 @@
import { Injectable, Logger } from '@nestjs/common';
import { AppError } from '@app/core/errors/app-error.js';
export interface BackgroundJob<Operation = string, State = string> {
id: string;
operation: Operation;
state: State;
processed: number;
total: number;
error?: string | null;
meta?: Record<string, unknown>;
}
interface CreateJobOptions<Operation, State> {
operation: Operation;
total: number;
initialState: State;
prefix?: string;
meta?: Record<string, unknown>;
}
interface RunJobOptions<State> {
job: BackgroundJob<unknown, State>;
work: () => Promise<void>;
runningState: State;
successState: State;
failureState: State;
logContext?: string;
}
@Injectable()
export class BackgroundJobsService {
private readonly logger = new Logger(BackgroundJobsService.name);
private readonly jobs = new Map<string, BackgroundJob>();
public createJob<Operation, State>(options: CreateJobOptions<Operation, State>) {
const { operation, total, initialState, prefix, meta } = options;
const idBase = typeof operation === 'string' ? operation.toLowerCase() : 'job';
const id = `${prefix ?? idBase}-${Date.now().toString(36)}`;
const job: BackgroundJob<Operation, State> = {
id,
operation,
state: initialState,
processed: 0,
total,
error: null,
meta,
};
this.jobs.set(job.id, job);
return job;
}
public updateJob<Operation, State>(job: BackgroundJob<Operation, State>) {
this.jobs.set(job.id, { ...job });
return this.jobs.get(job.id) as BackgroundJob<Operation, State>;
}
public getJob<Operation, State>(jobId: string) {
const job = this.jobs.get(jobId) as BackgroundJob<Operation, State> | undefined;
if (!job) {
throw new AppError(`Background job ${jobId} not found`, 404);
}
return job;
}
public async runJob<State>(options: RunJobOptions<State>) {
const { job, work, runningState, successState, failureState, logContext } = options;
if (job.state === successState) {
return job;
}
job.state = runningState;
this.updateJob(job);
try {
await work();
job.state = successState;
} catch (error) {
job.state = failureState;
job.error = error instanceof Error ? error.message : 'Unknown error while processing job';
this.logger.error(
`[${logContext ?? 'background-job'}] failed ${job.id}: ${job.error}`,
error as Error
);
} finally {
this.updateJob(job);
}
return job;
}
}

View File

@@ -1,7 +1,7 @@
{
"name": "unraid-monorepo",
"private": true,
"version": "4.26.2",
"version": "4.27.2",
"scripts": {
"build": "pnpm -r build",
"build:watch": "pnpm -r --parallel --filter '!@unraid/ui' build:watch",

View File

@@ -1,6 +1,6 @@
{
"name": "@unraid/connect-plugin",
"version": "4.26.2",
"version": "4.27.2",
"private": true,
"dependencies": {
"commander": "14.0.0",

View File

@@ -1,5 +1,11 @@
<?php
$docroot = $docroot ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp';
if (!class_exists('ThemeHelper')) {
$themeHelperPath = $docroot . '/plugins/dynamix/include/ThemeHelper.php';
if (is_readable($themeHelperPath)) {
require_once $themeHelperPath;
}
}
class WebComponentsExtractor
{
@@ -148,22 +154,169 @@ class WebComponentsExtractor
return $files;
}
private function normalizeHex(?string $color): ?string
{
if (!is_string($color) || trim($color) === '') {
return null;
}
$color = trim($color);
if ($color[0] !== '#') {
$color = '#' . ltrim($color, '#');
}
$hex = substr($color, 1);
if (strlen($hex) === 3) {
$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
}
if (!ctype_xdigit($hex) || strlen($hex) !== 6) {
return null;
}
return '#' . strtolower($hex);
}
private function hexToRgba(string $hex, float $alpha): string
{
$hex = ltrim($hex, '#');
if (strlen($hex) === 3) {
$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
}
$r = hexdec(substr($hex, 0, 2));
$g = hexdec(substr($hex, 2, 2));
$b = hexdec(substr($hex, 4, 2));
return sprintf('rgba(%d, %d, %d, %.3f)', $r, $g, $b, max(0, min(1, $alpha)));
}
/**
* Attempt to build CSS variables from PHP display data (server-rendered settings).
*
* @return array{vars: array<string,string>, classes: string[], diagnostics: array}|null
*/
private function getDisplayThemeVars(): ?array
{
if (!isset($GLOBALS['display']) || !is_array($GLOBALS['display'])) {
return null;
}
$display = $GLOBALS['display'];
$vars = [];
$textPrimary = $this->normalizeHex($display['header'] ?? null);
if ($textPrimary) {
$vars['--header-text-primary'] = $textPrimary;
}
$textSecondary = $this->normalizeHex($display['headermetacolor'] ?? null);
if ($textSecondary) {
$vars['--header-text-secondary'] = $textSecondary;
}
$theme = strtolower(trim($display['theme'] ?? ''));
if ($theme === 'white') {
if (!$textPrimary) {
$vars['--header-text-primary'] = 'var(--inverse-text-color, #ffffff)';
}
if (!$textSecondary) {
$vars['--header-text-secondary'] = 'var(--alt-text-color, #999999)';
}
}
$bgColor = $this->normalizeHex($display['background'] ?? null);
if ($bgColor) {
$vars['--header-background-color'] = $bgColor;
$vars['--header-gradient-start'] = $this->hexToRgba($bgColor, 0);
$vars['--header-gradient-end'] = $this->hexToRgba($bgColor, 0.7);
}
$shouldShowBannerGradient = ($display['showBannerGradient'] ?? '') === 'yes';
if ($shouldShowBannerGradient) {
$start = $vars['--header-gradient-start'] ?? 'rgba(0, 0, 0, 0)';
$end = $vars['--header-gradient-end'] ?? 'rgba(0, 0, 0, 0.7)';
$vars['--banner-gradient'] = sprintf(
'linear-gradient(90deg, %s 0, %s 90%%)',
$start,
$end
);
}
if (empty($vars)) {
return null;
}
return [
'vars' => $vars,
'diagnostics' => [
'theme' => $display['theme'] ?? null,
],
];
}
private function renderThemeVars(array $cssVars, string $source, array $diagnostics = []): string
{
$cssRules = [];
foreach ($cssVars as $key => $value) {
if (!is_string($key) || !is_string($value) || $value === '') {
continue;
}
if (!preg_match('/^--[A-Za-z0-9_-]+$/', $key)) {
continue;
}
$safeKey = htmlspecialchars($key, ENT_QUOTES | ENT_HTML5, 'UTF-8');
$safeValue = str_replace('</style>', '<\/style>', $value);
$cssRules[] = sprintf(
' %s: %s;',
$safeKey,
$safeValue
);
}
if (empty($cssRules)) {
return '';
}
return '<style id="unraid-theme-css-vars">
:root {
' . implode("\n", $cssRules) . '
}
</style>';
}
private function getThemeInitScript(): string
{
$displayTheme = $this->getDisplayThemeVars();
if ($displayTheme) {
return $this->renderThemeVars(
$displayTheme['vars'],
'display',
$displayTheme['diagnostics'] ?? []
);
}
return '';
}
private static bool $scriptsOutput = false;
public function getScriptTagHtml(): string
{
// Use a static flag to ensure scripts are only output once per request
static $scriptsOutput = false;
if ($scriptsOutput) {
if (self::$scriptsOutput) {
return '<!-- Resources already loaded -->';
}
try {
$scriptsOutput = true;
return $this->processManifestFiles();
self::$scriptsOutput = true;
$themeScript = $this->getThemeInitScript();
$manifestScripts = $this->processManifestFiles();
return $themeScript . "\n" . $manifestScripts;
} catch (\Exception $e) {
error_log("Error in WebComponentsExtractor::getScriptTagHtml: " . $e->getMessage());
$scriptsOutput = false; // Reset on error
self::$scriptsOutput = false; // Reset on error
return "";
}
}
public static function resetScriptsOutput(): void
{
self::$scriptsOutput = false;
}
}

View File

@@ -120,10 +120,14 @@ class ExtractorTest {
file_put_contents($this->testDir . '/extractor.php', $extractorContent);
}
private function getExtractorOutput() {
private function getExtractorOutput($resetStatic = false) {
$_SERVER['DOCUMENT_ROOT'] = '/usr/local/emhttp';
require_once $this->testDir . '/extractor.php';
if ($resetStatic && class_exists('WebComponentsExtractor')) {
WebComponentsExtractor::resetScriptsOutput();
}
$extractor = WebComponentsExtractor::getInstance();
return $extractor->getScriptTagHtml();
}
@@ -299,6 +303,11 @@ class ExtractorTest {
preg_match('/<link[^>]+id="unraid-[^"]*-css-[^"]+"[^>]+data-unraid="1"/', $output) > 0
);
// Test: CSS Variable Validation
echo "\nTest: CSS Variable Validation\n";
echo "------------------------------\n";
$this->testCssVariableValidation();
// Test: Duplicate Prevention
echo "\nTest: Duplicate Prevention\n";
echo "---------------------------\n";
@@ -317,6 +326,114 @@ class ExtractorTest {
);
}
private function testCssVariableValidation() {
$_SERVER['DOCUMENT_ROOT'] = '/usr/local/emhttp';
require_once $this->testDir . '/extractor.php';
$extractor = WebComponentsExtractor::getInstance();
$reflection = new ReflectionClass('WebComponentsExtractor');
$method = $reflection->getMethod('renderThemeVars');
$method->setAccessible(true);
// Test valid CSS variable names
$validVars = [
'--header-text-primary' => '#ffffff',
'--header-text-secondary' => '#cccccc',
'--header-background-color' => '#000000',
'--test-var' => 'value',
'--test_var' => 'value',
'--test123' => 'value',
'--A-Z_a-z0-9' => 'value',
];
$output = $method->invoke($extractor, $validVars, 'test');
$this->test(
"Accepts valid CSS variable names starting with --",
strpos($output, '--header-text-primary') !== false &&
strpos($output, '--test-var') !== false &&
strpos($output, '--test_var') !== false &&
strpos($output, '--test123') !== false
);
// Test invalid CSS variable names (should be rejected)
$invalidVars = [
'not-a-var' => 'value',
'-not-a-var' => 'value',
'--var with spaces' => 'value',
'--var<script>' => 'value',
'--var"quote' => 'value',
'--var\'quote' => 'value',
'--var;injection' => 'value',
'--var:colon' => 'value',
'--var.value' => 'value',
'--var/value' => 'value',
'--var\\backslash' => 'value',
'' => 'value',
'--' => 'value',
];
$output = $method->invoke($extractor, $invalidVars, 'test');
$this->test(
"Rejects CSS variable names without -- prefix",
strpos($output, 'not-a-var') === false &&
strpos($output, '-not-a-var') === false
);
$this->test(
"Rejects CSS variable names with spaces",
strpos($output, 'var with spaces') === false
);
$this->test(
"Rejects CSS variable names with script tags",
strpos($output, '<script>') === false &&
strpos($output, 'var<script>') === false
);
$this->test(
"Rejects CSS variable names with quotes",
strpos($output, 'var"quote') === false &&
strpos($output, "var'quote") === false
);
$this->test(
"Rejects CSS variable names with semicolons",
strpos($output, 'var;injection') === false
);
$this->test(
"Rejects CSS variable names with dots",
strpos($output, 'var.value') === false
);
$this->test(
"Rejects empty or minimal invalid keys",
strpos($output, ': --;') === false
);
// Test mixed valid and invalid (only valid should appear)
$mixedVars = [
'--valid-var' => 'value1',
'invalid-var' => 'value2',
'--another-valid' => 'value3',
'--invalid<script>' => 'value4',
];
$output = $method->invoke($extractor, $mixedVars, 'test');
$this->test(
"Accepts valid variables and rejects invalid ones in mixed input",
strpos($output, '--valid-var') !== false &&
strpos($output, '--another-valid') !== false &&
strpos($output, 'invalid-var') === false &&
strpos($output, '<script>') === false
);
// Test non-string keys (should be rejected)
$nonStringKeys = [
'--valid' => 'value',
123 => 'value',
true => 'value',
null => 'value',
];
$output = $method->invoke($extractor, $nonStringKeys, 'test');
$this->test(
"Rejects non-string keys",
strpos($output, '--valid') !== false &&
strpos($output, '123') === false
);
}
private function test($name, $condition) {
if ($condition) {
echo " " . self::GREEN . "" . self::NC . " " . $name . "\n";
@@ -352,6 +469,19 @@ class ExtractorTest {
return preg_replace('/[^a-zA-Z0-9-]/', '-', $input);
}
private function resetExtractor() {
// Reset singleton instance
if (class_exists('WebComponentsExtractor')) {
$reflection = new ReflectionClass('WebComponentsExtractor');
$instance = $reflection->getProperty('instance');
$instance->setAccessible(true);
$instance->setValue(null, null);
// Reset static flag
WebComponentsExtractor::resetScriptsOutput();
}
}
private function reportResults() {
echo "\n";
echo "========================================\n";

View File

@@ -1,6 +1,6 @@
{
"name": "@unraid/ui",
"version": "4.26.2",
"version": "4.27.2",
"private": true,
"license": "GPL-2.0-or-later",
"type": "module",

View File

@@ -35,6 +35,13 @@ vi.mock('@vueuse/core', () => ({
isSupported: mockIsSupported,
};
},
useLocalStorage: <T>(key: string, initialValue: T) => {
const storage = new Map<string, T>();
if (!storage.has(key)) {
storage.set(key, initialValue);
}
return ref(storage.get(key) ?? initialValue);
},
}));
vi.mock('@unraid/ui', () => ({

View File

@@ -0,0 +1,90 @@
import { describe, expect, it } from 'vitest';
import { createTestI18n } from '../utils/i18n';
describe('Trial Translation Keys', () => {
it('should load all trial-related translation keys', () => {
const i18n = createTestI18n();
const { t } = i18n.global;
const trialKeys = [
'registration.trialExpiration',
'server.actions.extendTrial',
'server.actions.startTrial',
'server.state.trial.humanReadable',
'server.state.trial.messageEligibleInsideRenewal',
'server.state.trial.messageEligibleOutsideRenewal',
'server.state.trial.messageIneligibleInsideRenewal',
'server.state.trial.messageIneligibleOutsideRenewal',
'server.state.trialExpired.heading',
'server.state.trialExpired.humanReadable',
'server.state.trialExpired.messageEligible',
'server.state.trialExpired.messageIneligible',
'userProfile.trial.trialKeyCreated',
'userProfile.trial.trialKeyCreationFailed',
'userProfile.trial.startingYourFreeDayTrial',
'userProfile.trial.extendingYourFreeTrialByDays',
'userProfile.trial.errorCreatiingATrialKeyPlease',
'userProfile.trial.pleaseKeepThisWindowOpen',
'userProfile.trial.pleaseWaitWhileThePageReloads',
'userProfile.uptimeExpire.trialKeyExpired',
'userProfile.uptimeExpire.trialKeyExpiredAt',
'userProfile.uptimeExpire.trialKeyExpiresAt',
'userProfile.uptimeExpire.trialKeyExpiresIn',
'userProfile.callbackFeedback.calculatingTrialExpiration',
'userProfile.callbackFeedback.installingExtendedTrial',
'userProfile.callbackFeedback.yourFreeTrialKeyProvidesAll',
'userProfile.callbackFeedback.yourTrialKeyHasBeenExtended',
'userProfile.dropdownTrigger.trialExpiredSeeOptionsBelow',
];
for (const key of trialKeys) {
const translation = t(key);
expect(translation).toBeTruthy();
expect(translation).not.toBe(key);
expect(typeof translation).toBe('string');
}
});
it('should translate trial expiration keys with parameters', () => {
const i18n = createTestI18n();
const { t } = i18n.global;
const testDate = '2024-01-15 10:30:00';
const testDuration = '5 days';
expect(t('userProfile.uptimeExpire.trialKeyExpired', [testDuration])).toContain(testDuration);
expect(t('userProfile.uptimeExpire.trialKeyExpiredAt', [testDate])).toContain(testDate);
expect(t('userProfile.uptimeExpire.trialKeyExpiresAt', [testDate])).toContain(testDate);
expect(t('userProfile.uptimeExpire.trialKeyExpiresIn', [testDuration])).toContain(testDuration);
});
it('should have all required trial state messages', () => {
const i18n = createTestI18n();
const { t } = i18n.global;
const stateMessages = [
'server.state.trial.messageEligibleInsideRenewal',
'server.state.trial.messageEligibleOutsideRenewal',
'server.state.trial.messageIneligibleInsideRenewal',
'server.state.trial.messageIneligibleOutsideRenewal',
'server.state.trialExpired.messageEligible',
'server.state.trialExpired.messageIneligible',
];
for (const key of stateMessages) {
const message = t(key);
expect(message).toBeTruthy();
expect(message.length).toBeGreaterThan(0);
expect(message).toMatch(/<p>/);
}
});
it('should have trial action translations', () => {
const i18n = createTestI18n();
const { t } = i18n.global;
expect(t('server.actions.extendTrial')).toBe('Extend Trial');
expect(t('server.actions.startTrial')).toBe('Start Free 30 Day Trial');
});
});

View File

@@ -6,13 +6,10 @@ import { createApp, nextTick, ref } from 'vue';
import { setActivePinia } from 'pinia';
import { defaultColors } from '~/themes/default';
import hexToRgba from 'hex-to-rgba';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import type { Theme } from '~/themes/types';
import { globalPinia } from '~/store/globalPinia';
import { THEME_STORAGE_KEY, useThemeStore } from '~/store/theme';
import { useThemeStore } from '~/store/theme';
vi.mock('@vue/apollo-composable', () => ({
useQuery: () => ({
@@ -23,15 +20,10 @@ vi.mock('@vue/apollo-composable', () => ({
}),
}));
vi.mock('hex-to-rgba', () => ({
default: vi.fn((hex, opacity) => `rgba(mock-${hex}-${opacity})`),
}));
describe('Theme Store', () => {
const originalAddClassFn = document.body.classList.add;
const originalRemoveClassFn = document.body.classList.remove;
const originalStyleCssText = document.body.style.cssText;
const originalDocumentElementSetProperty = document.documentElement.style.setProperty;
const originalDocumentElementAddClass = document.documentElement.classList.add;
const originalDocumentElementRemoveClass = document.documentElement.classList.remove;
@@ -49,7 +41,6 @@ describe('Theme Store', () => {
document.body.classList.add = vi.fn();
document.body.classList.remove = vi.fn();
document.body.style.cssText = '';
document.documentElement.style.setProperty = vi.fn();
document.documentElement.classList.add = vi.fn();
document.documentElement.classList.remove = vi.fn();
@@ -70,7 +61,6 @@ describe('Theme Store', () => {
document.body.classList.add = originalAddClassFn;
document.body.classList.remove = originalRemoveClassFn;
document.body.style.cssText = originalStyleCssText;
document.documentElement.style.setProperty = originalDocumentElementSetProperty;
document.documentElement.classList.add = originalDocumentElementAddClass;
document.documentElement.classList.remove = originalDocumentElementRemoveClass;
vi.restoreAllMocks();
@@ -88,8 +78,6 @@ describe('Theme Store', () => {
it('should initialize with default theme', () => {
const store = createStore();
expect(typeof store.$persist).toBe('function');
expect(store.theme).toEqual({
name: 'white',
banner: false,
@@ -191,93 +179,62 @@ describe('Theme Store', () => {
await nextTick();
// activeColorVariables now contains the theme defaults from defaultColors
// Custom values are applied as CSS variables on the documentElement
// The white theme's --color-beta is a reference to var(--header-text-primary)
expect(store.activeColorVariables['--color-beta']).toBe('var(--header-text-primary)');
expect(document.documentElement.style.setProperty).toHaveBeenCalledWith(
'--custom-header-text-primary',
'#333333'
);
expect(document.documentElement.style.setProperty).toHaveBeenCalledWith(
'--custom-header-text-secondary',
'#666666'
);
expect(document.documentElement.style.setProperty).toHaveBeenCalledWith(
'--custom-header-background-color',
'#ffffff'
);
});
it('should handle banner gradient correctly', async () => {
it('should apply dark mode classes when theme changes', async () => {
const store = createStore();
const mockHexToRgba = vi.mocked(hexToRgba);
mockHexToRgba.mockClear();
store.setTheme({
...store.theme,
banner: true,
bannerGradient: true,
bgColor: '#112233',
name: 'black',
});
await nextTick();
expect(mockHexToRgba).toHaveBeenCalledWith('#112233', 0);
expect(mockHexToRgba).toHaveBeenCalledWith('#112233', 0.7);
// Banner gradient values are now set as custom CSS variables on documentElement
expect(document.documentElement.style.setProperty).toHaveBeenCalledWith(
'--custom-header-gradient-start',
'rgba(mock-#112233-0)'
);
expect(document.documentElement.style.setProperty).toHaveBeenCalledWith(
'--custom-header-gradient-end',
'rgba(mock-#112233-0.7)'
);
expect(document.documentElement.style.setProperty).toHaveBeenCalledWith(
'--banner-gradient',
'linear-gradient(90deg, rgba(mock-#112233-0) 0, rgba(mock-#112233-0.7) 90%)'
);
expect(document.documentElement.classList.add).toHaveBeenCalledWith('dark');
expect(document.body.classList.add).toHaveBeenCalledWith('dark');
});
it('should hydrate theme from cache when available', () => {
const cachedTheme = {
it('should apply dark mode classes to all .unapi elements', async () => {
const store = createStore();
const unapiElement1 = document.createElement('div');
unapiElement1.classList.add('unapi');
document.body.appendChild(unapiElement1);
const unapiElement2 = document.createElement('div');
unapiElement2.classList.add('unapi');
document.body.appendChild(unapiElement2);
const addSpy1 = vi.spyOn(unapiElement1.classList, 'add');
const addSpy2 = vi.spyOn(unapiElement2.classList, 'add');
const removeSpy1 = vi.spyOn(unapiElement1.classList, 'remove');
const removeSpy2 = vi.spyOn(unapiElement2.classList, 'remove');
store.setTheme({
...store.theme,
name: 'black',
banner: true,
bannerGradient: false,
bgColor: '#222222',
descriptionShow: true,
metaColor: '#aaaaaa',
textColor: '#ffffff',
} satisfies Theme;
});
window.localStorage.setItem(THEME_STORAGE_KEY, JSON.stringify({ theme: cachedTheme }));
const store = createStore();
expect(store.theme).toEqual(cachedTheme);
});
it('should persist server theme responses to cache', async () => {
const store = createStore();
const serverTheme = {
name: 'gray',
banner: false,
bannerGradient: false,
bgColor: '#111111',
descriptionShow: false,
metaColor: '#999999',
textColor: '#eeeeee',
} satisfies Theme;
store.setTheme(serverTheme, { source: 'server' });
await nextTick();
expect(window.localStorage.getItem(THEME_STORAGE_KEY)).toEqual(
JSON.stringify({ theme: serverTheme })
);
expect(addSpy1).toHaveBeenCalledWith('dark');
expect(addSpy2).toHaveBeenCalledWith('dark');
store.setTheme({
...store.theme,
name: 'white',
});
await nextTick();
expect(removeSpy1).toHaveBeenCalledWith('dark');
expect(removeSpy2).toHaveBeenCalledWith('dark');
document.body.removeChild(unapiElement1);
document.body.removeChild(unapiElement2);
});
});
});

View File

@@ -10,6 +10,7 @@ import type { ExternalUpdateOsAction } from '@unraid/shared-callbacks';
import type { Release } from '~/store/updateOsActions';
import { useUpdateOsActionsStore } from '~/store/updateOsActions';
import { testTranslate } from '../utils/i18n';
vi.mock('~/helpers/urls', () => ({
WEBGUI_TOOLS_UPDATE: 'https://webgui/tools/update',
@@ -48,20 +49,34 @@ vi.mock('~/store/account', () => ({
}),
}));
const mockServerStore = {
guid: 'test-guid',
keyfile: 'test-keyfile',
osVersion: '6.12.4',
osVersionBranch: 'stable',
regUpdatesExpired: false,
regTy: 'Plus',
locale: 'en_US' as string | undefined,
rebootType: '',
updateOsResponse: null as { date: string } | null,
};
vi.mock('~/store/server', () => ({
useServerStore: () => ({
guid: 'test-guid',
keyfile: 'test-keyfile',
osVersion: '6.12.4',
osVersionBranch: 'stable',
regUpdatesExpired: false,
rebootType: '',
}),
useServerStore: () => mockServerStore,
}));
const mockUpdateOsStore = {
available: '6.12.5',
availableWithRenewal: false,
};
vi.mock('~/store/updateOs', () => ({
useUpdateOsStore: () => ({
available: '6.12.5',
useUpdateOsStore: () => mockUpdateOsStore,
}));
vi.mock('vue-i18n', () => ({
useI18n: () => ({
t: testTranslate,
}),
}));
@@ -70,6 +85,19 @@ describe('UpdateOsActions Store', () => {
beforeEach(() => {
setActivePinia(createPinia());
// Reset mocks to default values
mockServerStore.guid = 'test-guid';
mockServerStore.keyfile = 'test-keyfile';
mockServerStore.osVersion = '6.12.4';
mockServerStore.osVersionBranch = 'stable';
mockServerStore.regUpdatesExpired = false;
mockServerStore.regTy = 'Plus';
mockServerStore.locale = 'en_US';
mockServerStore.rebootType = '';
mockServerStore.updateOsResponse = null;
mockUpdateOsStore.available = '6.12.5';
mockUpdateOsStore.availableWithRenewal = false;
store = useUpdateOsActionsStore();
vi.clearAllMocks();
@@ -417,4 +445,106 @@ describe('UpdateOsActions Store', () => {
expect(store.status).toBe('updating');
});
});
describe('formattedReleaseDate', () => {
it('should return empty string when no release date is available', () => {
mockUpdateOsStore.availableWithRenewal = false;
mockServerStore.updateOsResponse = null;
store = useUpdateOsActionsStore();
expect(store.formattedReleaseDate).toBe('');
});
it('should format date correctly with locale from server store', () => {
mockUpdateOsStore.availableWithRenewal = true;
mockServerStore.updateOsResponse = { date: '2023-10-15' };
mockServerStore.locale = 'en_US';
store = useUpdateOsActionsStore();
const formatted = store.formattedReleaseDate;
expect(formatted).toBeTruthy();
expect(formatted).toContain('2023');
expect(formatted).toContain('October');
expect(formatted).toContain('15');
});
it('should normalize locale underscores to hyphens', () => {
mockUpdateOsStore.availableWithRenewal = true;
mockServerStore.updateOsResponse = { date: '2023-10-15' };
mockServerStore.locale = 'fr_FR';
store = useUpdateOsActionsStore();
const formatted = store.formattedReleaseDate;
expect(formatted).toBeTruthy();
expect(typeof formatted).toBe('string');
expect(formatted.length).toBeGreaterThan(0);
});
it('should fall back to navigator.language when locale is missing', () => {
const originalLanguage = navigator.language;
Object.defineProperty(navigator, 'language', {
value: 'de-DE',
configurable: true,
});
mockUpdateOsStore.availableWithRenewal = true;
mockServerStore.updateOsResponse = { date: '2023-10-15' };
mockServerStore.locale = undefined;
store = useUpdateOsActionsStore();
const formatted = store.formattedReleaseDate;
expect(formatted).toBeTruthy();
expect(typeof formatted).toBe('string');
Object.defineProperty(navigator, 'language', {
value: originalLanguage,
configurable: true,
});
});
it('should fall back to en-US when locale and navigator.language are missing', () => {
const originalLanguage = navigator.language;
Object.defineProperty(navigator, 'language', {
value: undefined,
configurable: true,
});
mockUpdateOsStore.availableWithRenewal = true;
mockServerStore.updateOsResponse = { date: '2023-10-15' };
mockServerStore.locale = undefined;
store = useUpdateOsActionsStore();
const formatted = store.formattedReleaseDate;
expect(formatted).toBeTruthy();
expect(formatted).toContain('2023');
expect(formatted).toContain('October');
expect(formatted).toContain('15');
Object.defineProperty(navigator, 'language', {
value: originalLanguage,
configurable: true,
});
});
it('should parse date correctly to avoid off-by-one errors', () => {
mockUpdateOsStore.availableWithRenewal = true;
mockServerStore.updateOsResponse = { date: '2023-01-01' };
mockServerStore.locale = 'en-US';
store = useUpdateOsActionsStore();
const formatted = store.formattedReleaseDate;
expect(formatted).toContain('January');
expect(formatted).toContain('1');
});
});
describe('ineligibleText', () => {
it('should return empty string when eligible', () => {
mockServerStore.guid = 'test-guid';
mockServerStore.keyfile = 'test-keyfile';
mockServerStore.osVersion = '6.12.4';
mockServerStore.regUpdatesExpired = false;
store = useUpdateOsActionsStore();
expect(store.ineligibleText).toBe('');
});
});
});

View File

@@ -35,30 +35,30 @@ type LocaleMessages = typeof enUS;
const localeMessages: Record<string, LocaleMessages> = {
en_US: enUS,
ar: ar as LocaleMessages,
bn: bn as LocaleMessages,
ca: ca as LocaleMessages,
cs: cs as LocaleMessages,
da: da as LocaleMessages,
de: de as LocaleMessages,
es: es as LocaleMessages,
fr: fr as LocaleMessages,
hi: hi as LocaleMessages,
hr: hr as LocaleMessages,
hu: hu as LocaleMessages,
it: it as LocaleMessages,
ja: ja as LocaleMessages,
ko: ko as LocaleMessages,
lv: lv as LocaleMessages,
nl: nl as LocaleMessages,
no: no as LocaleMessages,
pl: pl as LocaleMessages,
pt: pt as LocaleMessages,
ro: ro as LocaleMessages,
ru: ru as LocaleMessages,
sv: sv as LocaleMessages,
uk: uk as LocaleMessages,
zh: zh as LocaleMessages,
ar: ar as unknown as LocaleMessages,
bn: bn as unknown as LocaleMessages,
ca: ca as unknown as LocaleMessages,
cs: cs as unknown as LocaleMessages,
da: da as unknown as LocaleMessages,
de: de as unknown as LocaleMessages,
es: es as unknown as LocaleMessages,
fr: fr as unknown as LocaleMessages,
hi: hi as unknown as LocaleMessages,
hr: hr as unknown as LocaleMessages,
hu: hu as unknown as LocaleMessages,
it: it as unknown as LocaleMessages,
ja: ja as unknown as LocaleMessages,
ko: ko as unknown as LocaleMessages,
lv: lv as unknown as LocaleMessages,
nl: nl as unknown as LocaleMessages,
no: no as unknown as LocaleMessages,
pl: pl as unknown as LocaleMessages,
pt: pt as unknown as LocaleMessages,
ro: ro as unknown as LocaleMessages,
ru: ru as unknown as LocaleMessages,
sv: sv as unknown as LocaleMessages,
uk: uk as unknown as LocaleMessages,
zh: zh as unknown as LocaleMessages,
};
type AnyObject = Record<string, unknown>;

1
web/components.d.ts vendored
View File

@@ -47,7 +47,6 @@ declare module 'vue' {
'DevThemeSwitcher.standalone': typeof import('./src/components/DevThemeSwitcher.standalone.vue')['default']
Downgrade: typeof import('./src/components/UpdateOs/Downgrade.vue')['default']
'DowngradeOs.standalone': typeof import('./src/components/DowngradeOs.standalone.vue')['default']
'DownloadApiLogs.standalone': typeof import('./src/components/DownloadApiLogs.standalone.vue')['default']
DropdownConnectStatus: typeof import('./src/components/UserProfile/DropdownConnectStatus.vue')['default']
DropdownContent: typeof import('./src/components/UserProfile/DropdownContent.vue')['default']
DropdownError: typeof import('./src/components/UserProfile/DropdownError.vue')['default']

View File

@@ -1,6 +1,6 @@
{
"name": "@unraid/web",
"version": "4.26.2",
"version": "4.27.2",
"private": true,
"type": "module",
"license": "GPL-2.0-or-later",

View File

@@ -52,7 +52,6 @@ const updateTheme = (themeName: string, skipUrlUpdate = false) => {
}
themeStore.setTheme({ name: themeName });
themeStore.setCssVars();
const linkId = 'dev-theme-css-link';
let themeLink = document.getElementById(linkId) as HTMLLinkElement | null;
@@ -101,7 +100,6 @@ onMounted(() => {
updateTheme(initialTheme, true);
} else {
themeStore.setTheme({ name: initialTheme });
themeStore.setCssVars();
}
});

View File

@@ -142,7 +142,6 @@ const reformattedTimestamp = computed<string>(() => {
<span class="text-sm">{{ t('notifications.item.archive') }}</span>
</Button>
<Button
v-if="type === NotificationType.ARCHIVE"
:disabled="deleteNotification.loading"
@click="() => deleteNotification.mutate({ id: props.id, type: props.type })"
>

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { computed, ref } from 'vue';
import { computed, onBeforeUnmount, reactive, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useMutation, useQuery, useSubscription } from '@vue/apollo-composable';
import { useApolloClient, useMutation, useQuery, useSubscription } from '@vue/apollo-composable';
import {
Button,
@@ -24,10 +24,12 @@ import { Settings } from 'lucide-vue-next';
import ConfirmDialog from '~/components/ConfirmDialog.vue';
import {
archiveAllNotifications,
deleteArchivedNotifications,
NOTIFICATION_FRAGMENT,
NOTIFICATION_JOB_FRAGMENT,
notificationJobStatus,
notificationsOverview,
resetOverview,
startDeleteNotifications,
} from '~/components/Notifications/graphql/notification.query';
import {
notificationAddedSubscription,
@@ -37,15 +39,100 @@ import NotificationsIndicator from '~/components/Notifications/Indicator.vue';
import NotificationsList from '~/components/Notifications/List.vue';
import { useTrackLatestSeenNotification } from '~/composables/api/use-notifications';
import { useFragment } from '~/composables/gql';
import { NotificationImportance as Importance, NotificationType } from '~/composables/gql/graphql';
import {
NotificationImportance as Importance,
NotificationJobState,
NotificationType,
} from '~/composables/gql/graphql';
import { useConfirm } from '~/composables/useConfirm';
const { mutate: archiveAll, loading: loadingArchiveAll } = useMutation(archiveAllNotifications);
const { mutate: deleteArchives, loading: loadingDeleteAll } = useMutation(deleteArchivedNotifications);
const { mutate: startArchiveAllJob, loading: loadingArchiveAll } = useMutation(archiveAllNotifications);
const { mutate: startDeleteAll, loading: loadingDeleteAll } = useMutation(startDeleteNotifications);
const { client } = useApolloClient();
const { mutate: recalculateOverview } = useMutation(resetOverview);
const { confirm } = useConfirm();
const importance = ref<Importance | undefined>(undefined);
const jobPollers: Record<
'archiveAll' | 'deleteArchived' | 'deleteUnread',
ReturnType<typeof setInterval> | null
> = {
archiveAll: null,
deleteArchived: null,
deleteUnread: null,
};
const activeJobs = reactive<
Record<
'archiveAll' | 'deleteArchived' | 'deleteUnread',
{
id: string;
state: NotificationJobState;
processed: number;
total: number;
error?: string | null;
} | null
>
>({
archiveAll: null,
deleteArchived: null,
deleteUnread: null,
});
const activeStates = [NotificationJobState.QUEUED, NotificationJobState.RUNNING];
const stopPolling = (key: keyof typeof jobPollers) => {
const interval = jobPollers[key];
if (interval) {
clearInterval(interval);
jobPollers[key] = null;
}
};
const setJob = (key: keyof typeof jobPollers, job?: unknown) => {
const parsed = job ? useFragment(NOTIFICATION_JOB_FRAGMENT, job) : null;
activeJobs[key] = parsed;
if (!parsed || !activeStates.includes(parsed.state)) {
stopPolling(key);
}
};
const pollJob = (key: keyof typeof jobPollers) => {
const job = activeJobs[key];
if (!job) return;
stopPolling(key);
if (!activeStates.includes(job.state)) return;
jobPollers[key] = setInterval(async () => {
const { data } = await client.query({
query: notificationJobStatus,
variables: { id: job.id },
fetchPolicy: 'network-only',
});
const updated = data?.notifications.job;
if (updated) {
setJob(key, updated);
}
}, 750);
};
const isJobActive = (key: keyof typeof jobPollers) => {
const job = activeJobs[key];
return Boolean(job && activeStates.includes(job.state));
};
const jobLabel = (key: keyof typeof jobPollers, fallback: string) => {
const job = activeJobs[key];
if (job && activeStates.includes(job.state)) {
return t('notifications.sidebar.processingStatus', {
processed: job.processed,
total: job.total,
});
}
return fallback;
};
const { t } = useI18n();
const filterOptions = computed<Array<{ label: string; value?: Importance }>>(() => [
@@ -63,7 +150,12 @@ const confirmAndArchiveAll = async () => {
confirmVariant: 'primary',
});
if (confirmed) {
await archiveAll();
const { data } = await startArchiveAllJob({ importance: importance.value });
const job = data?.notifications.startArchiveAll;
if (job) {
setJob('archiveAll', job);
pollJob('archiveAll');
}
}
};
@@ -75,7 +167,29 @@ const confirmAndDeleteArchives = async () => {
confirmVariant: 'destructive',
});
if (confirmed) {
await deleteArchives();
const { data } = await startDeleteAll({ type: NotificationType.ARCHIVE });
const job = data?.notifications.startDeleteAll;
if (job) {
setJob('deleteArchived', job);
pollJob('deleteArchived');
}
}
};
const confirmAndDeleteUnread = async () => {
const confirmed = await confirm({
title: t('notifications.sidebar.confirmDeleteUnread.title'),
description: t('notifications.sidebar.confirmDeleteUnread.description'),
confirmText: t('notifications.sidebar.confirmDeleteUnread.confirmText'),
confirmVariant: 'destructive',
});
if (confirmed) {
const { data } = await startDeleteAll({ type: NotificationType.UNREAD });
const job = data?.notifications.startDeleteAll;
if (job) {
setJob('deleteUnread', job);
pollJob('deleteUnread');
}
}
};
@@ -139,6 +253,10 @@ const readArchivedCount = computed(() => {
const prepareToViewNotifications = () => {
void recalculateOverview();
};
onBeforeUnmount(() => {
Object.values(jobPollers).forEach((interval) => interval && clearInterval(interval));
});
</script>
<template>
@@ -179,24 +297,33 @@ const prepareToViewNotifications = () => {
</TabsList>
<TabsContent value="unread" class="flex-col items-end">
<Button
:disabled="loadingArchiveAll"
:disabled="loadingArchiveAll || isJobActive('archiveAll')"
variant="link"
size="sm"
class="text-foreground hover:text-destructive transition-none"
@click="confirmAndArchiveAll"
>
{{ t('notifications.sidebar.archiveAllAction') }}
{{ jobLabel('archiveAll', t('notifications.sidebar.archiveAllAction')) }}
</Button>
<Button
:disabled="loadingDeleteAll || isJobActive('deleteUnread')"
variant="link"
size="sm"
class="text-foreground hover:text-destructive transition-none"
@click="confirmAndDeleteUnread"
>
{{ jobLabel('deleteUnread', t('notifications.sidebar.deleteAllAction')) }}
</Button>
</TabsContent>
<TabsContent value="archived" class="flex-col items-end">
<Button
:disabled="loadingDeleteAll"
:disabled="loadingDeleteAll || isJobActive('deleteArchived')"
variant="link"
size="sm"
class="text-foreground hover:text-destructive transition-none"
@click="confirmAndDeleteArchives"
>
{{ t('notifications.sidebar.deleteAllAction') }}
{{ jobLabel('deleteArchived', t('notifications.sidebar.deleteAllAction')) }}
</Button>
</TabsContent>
</div>

View File

@@ -23,6 +23,17 @@ export const NOTIFICATION_COUNT_FRAGMENT = graphql(/* GraphQL */ `
}
`);
export const NOTIFICATION_JOB_FRAGMENT = graphql(/* GraphQL */ `
fragment NotificationJobFragment on NotificationJob {
id
operation
state
processed
total
error
}
`);
export const getNotifications = graphql(/* GraphQL */ `
query Notifications($filter: NotificationFilter!) {
notifications {
@@ -42,40 +53,36 @@ export const archiveNotification = graphql(/* GraphQL */ `
}
`);
export const archiveAllNotifications = graphql(/* GraphQL */ `
mutation ArchiveAllNotifications {
archiveAll {
unread {
total
}
archive {
info
warning
alert
total
}
}
}
`);
export const deleteNotification = graphql(/* GraphQL */ `
mutation DeleteNotification($id: PrefixedID!, $type: NotificationType!) {
deleteNotification(id: $id, type: $type) {
archive {
total
notifications {
delete(id: $id, type: $type) {
archive {
total
}
unread {
total
}
}
}
}
`);
export const deleteArchivedNotifications = graphql(/* GraphQL */ `
mutation DeleteAllNotifications {
deleteArchivedNotifications {
archive {
total
export const archiveAllNotifications = graphql(/* GraphQL */ `
mutation StartArchiveAllNotifications($importance: NotificationImportance) {
notifications {
startArchiveAll(importance: $importance) {
...NotificationJobFragment
}
unread {
total
}
}
`);
export const startDeleteNotifications = graphql(/* GraphQL */ `
mutation StartDeleteNotifications($type: NotificationType) {
notifications {
startDeleteAll(type: $type) {
...NotificationJobFragment
}
}
}
@@ -100,6 +107,16 @@ export const notificationsOverview = graphql(/* GraphQL */ `
}
`);
export const notificationJobStatus = graphql(/* GraphQL */ `
query NotificationJobStatus($id: ID!) {
notifications {
job(id: $id) {
...NotificationJobFragment
}
}
}
`);
/** Re-calculates the notifications overview (i.e. notification counts) */
export const resetOverview = graphql(/* GraphQL */ `
mutation RecomputeOverview {

View File

@@ -198,7 +198,7 @@ const navigateToRegistration = () => {
variant="yellow"
:icon="() => h(ExclamationTriangleIcon, { style: 'width: 16px; height: 16px;' })"
>
{{ t(rebootTypeText) }}
{{ rebootTypeText }}
</Badge>
</template>

View File

@@ -18,7 +18,7 @@ const { rebootTypeText } = storeToRefs(useUpdateOsActionsStore());
<div class="grid gap-y-4">
<h3 class="flex flex-row items-center gap-2 text-xl leading-normal font-semibold">
<ExclamationTriangleIcon class="w-5 shrink-0" />
{{ t(rebootTypeText) }}
{{ rebootTypeText }}
</h3>
<div class="text-base leading-relaxed whitespace-normal opacity-75">
<p>

View File

@@ -21,7 +21,7 @@ const serverStore = useServerStore();
const updateOsStore = useUpdateOsStore();
const updateOsActionsStore = useUpdateOsActionsStore();
const { dateTimeFormat, regTy, renewAction, updateOsResponse } = storeToRefs(serverStore);
const { dateTimeFormat, renewAction, updateOsResponse } = storeToRefs(serverStore);
const { availableWithRenewal } = storeToRefs(updateOsStore);
const { ineligibleText } = storeToRefs(updateOsActionsStore);
@@ -43,7 +43,7 @@ const heading = computed((): string => {
});
const text = computed(() => {
return t(ineligibleText.value, [regTy.value, formattedReleaseDate.value]);
return ineligibleText.value;
});
const updateButton = ref<UserProfileLink | undefined>();

View File

@@ -58,12 +58,20 @@ const output = computed(() => {
return {
title:
state.value === 'EEXPIRED'
? t(props.shortText ? 'Expired at {0}' : 'Trial Key Expired at {0}', [formatted.value])
: t(props.shortText ? 'Expires at {0}' : 'Trial Key Expires at {0}', [formatted.value]),
? props.shortText
? t('userProfile.uptimeExpire.expiredAt', [formatted.value])
: t('userProfile.uptimeExpire.trialKeyExpiredAt', [formatted.value])
: props.shortText
? t('userProfile.uptimeExpire.expiresAt', [formatted.value])
: t('userProfile.uptimeExpire.trialKeyExpiresAt', [formatted.value]),
text:
state.value === 'EEXPIRED'
? t(props.shortText ? 'Expired {0}' : 'Trial Key Expired {0}', [readableDiff.value])
: t(props.shortText ? 'Expires in {0}' : 'Trial Key Expires in {0}', [readableDiff.value]),
? props.shortText
? t('userProfile.uptimeExpire.expired', [readableDiff.value])
: t('userProfile.uptimeExpire.trialKeyExpired', [readableDiff.value])
: props.shortText
? t('userProfile.uptimeExpire.expiresIn', [readableDiff.value])
: t('userProfile.uptimeExpire.trialKeyExpiresIn', [readableDiff.value]),
};
}
return {

View File

@@ -10,7 +10,6 @@ import { createI18nInstance, ensureLocale, getWindowLocale } from '~/helpers/i18
// Import Pinia for use in Vue apps
import { globalPinia } from '~/store/globalPinia';
import { useThemeStore } from '~/store/theme';
// Ensure Apollo client is singleton
const apolloClient = (typeof window !== 'undefined' && window.apolloClient) || client;
@@ -91,8 +90,6 @@ export async function mountUnifiedApp() {
app.use(ui);
app.provide(DefaultApolloClient, apolloClient);
const themeStore = useThemeStore();
// Mount the app to establish context
let rootElement = document.getElementById('unraid-unified-root');
if (!rootElement) {
@@ -189,9 +186,6 @@ export async function mountUnifiedApp() {
});
});
// Re-apply theme classes/variables now that new scoped roots exist
themeStore.setCssVars();
console.debug(`[UnifiedMount] Mounted ${mountedComponents.length} components`);
return app;

View File

@@ -20,7 +20,12 @@ export const createHtmlEntityDecoder = () => {
const parser = new DOMParser();
return <T>(translated: T) => {
if (typeof translated !== 'string') return translated;
const decoded = parser.parseFromString(translated, 'text/html').documentElement.textContent;
return decoded ?? translated;
const doc = parser.parseFromString(translated, 'text/html');
const bodyContent = doc.body.innerHTML;
const hasHtmlTags = /<[^>]+>/.test(bodyContent);
if (hasHtmlTags) {
return bodyContent;
}
return doc.documentElement.textContent ?? translated;
};
};

View File

@@ -55,13 +55,6 @@
"connectSettings.updatedApiSettingsToast": "تم تحديث إعدادات API",
"downgradeOs.downgradeUnraidOs": "تخفيض نسخة نظام Unraid",
"downgradeOs.pleaseFinishTheInitiatedUpdateTo": "الرجاء إنهاء التحديث المُبادر بتمكين عملية التخفيض.",
"downloadApiLogs.downloadUnraidApiLogs": "تنزيل سجلات unraid-api",
"downloadApiLogs.ifYouAreAskedToSupply": "إذا تم طلب سجلات منك، يرجى فتح طلب دعم على صفحة الاتصال الخاصة بنا والرد على رسالة البريد الإلكتروني التي تتلقاها مع السجلات المرفقة.",
"downloadApiLogs.theLogsMayContainSensitiveInformation": "قد تحتوي السجلات على معلومات حساسة لذا لا تقم بنشرها علنياً.",
"downloadApiLogs.thePrimaryMethodOfSupportFor": "الطريقة الرئيسية للدعم لـ Unraid Connect من خلال منتدياتنا و Discord.",
"downloadApiLogs.unraidConnectForums": "منتديات Unraid Connect",
"downloadApiLogs.unraidContactPage": "صفحة تواصل Unraid",
"downloadApiLogs.unraidDiscord": "Unraid ديسكورد",
"headerOsVersion.apiVersionCopiedToClipboard": "إصدار API تم نسخه إلى الحافظة",
"headerOsVersion.osVersionCopiedToClipboard": "إصدار نظام التشغيل تم نسخه إلى الحافظة",
"headerOsVersion.unraidApi": "Unraid API",
@@ -473,12 +466,20 @@
"updateOs.downgrade.releaseNotes": "{0} ملاحظات الإصدار",
"updateOs.ignoredRelease.remove": "إزالة",
"updateOs.ignoredRelease.removeFromIgnoreList": "إزالة من قائمة التجاهل",
"updateOs.ineligible.guidRequired": "مطلوب GUID صالح للتحقق من تحديثات نظام التشغيل.",
"updateOs.ineligible.keyfileRequired": "مطلوب ملف مفتاح صالح للتحقق من تحديثات نظام التشغيل.",
"updateOs.ineligible.osVersionRequired": "مطلوب إصدار نظام تشغيل صالح للتحقق من تحديثات نظام التشغيل.",
"updateOs.ineligible.updatesExpired": "شملت رخصة {0} الخاصة بك سنة واحدة من التحديثات المجانية في وقت الشراء. الآن يمكنك تمديد رخصتك للوصول إلى أحدث تحديثات نظام التشغيل.",
"updateOs.ineligible.updatesExpiredWithAvailable": "تضمنت رخصتك {0} سنة واحدة من التحديثات المجانية عند الشراء. يمكنك الآن تمديد الرخصة والدخول إلى أحدث تحديثات النظام. لا زلت مؤهلاً للوصول إلى تحديثات النظام التي تم نشرها في أو قبل {1}.",
"updateOs.pleaseFinishTheInitiatedDowngradeTo": "يرجى إكمال عملية التخفيض التي تم بدءها لتمكين التحديثات.",
"updateOs.rawChangelogRenderer.errorParsingChangelog": "خطأ في تحليل سجل التغيرات • {0}",
"updateOs.rawChangelogRenderer.itSHighlyRecommendedToReview": "ينصح بشدة بمراجعة سجل التغيرات قبل متابعة التحديث",
"updateOs.rawChangelogRenderer.loadingChangelog": "يتم تحميل سجل التغيرات...",
"updateOs.rawChangelogRenderer.noChangelogContentAvailable": "لا توجد محتويات سجل التغيرات المتاحة",
"updateOs.rawChangelogRenderer.viewChangelogOnDocs": "عرض سجل التغيرات على المستندات",
"updateOs.reboot.downgrade": "إعادة التشغيل المطلوبة للتخفيض",
"updateOs.reboot.thirdPartyDriversDownloading": "تحديث برامج التشغيل التابعة لجهات خارجية",
"updateOs.reboot.update": "إعادة التشغيل المطلوبة للتحديث",
"updateOs.status.cancel": "إلغاء {0}",
"updateOs.status.checking": "جاري التحقق...",
"updateOs.status.downgrade": "تخفيض",
@@ -594,7 +595,15 @@
"userProfile.trial.startingYourFreeDayTrial": "بدأت تجربتك المجانية لمدة 30 يومًا",
"userProfile.trial.trialKeyCreated": "تم إنشاء مفتاح تجريبي",
"userProfile.trial.trialKeyCreationFailed": "فشل في إنشاء مفتاح تجريبي",
"userProfile.uptimeExpire.expired": "انتهت {0}",
"userProfile.uptimeExpire.expiredAt": "انتهت في {0}",
"userProfile.uptimeExpire.expiresAt": "تنتهي في {0}",
"userProfile.uptimeExpire.expiresIn": "تنتهي في {0}",
"userProfile.uptimeExpire.serverUpSince": "الخادم في حالة تشغيل منذ {0}",
"userProfile.uptimeExpire.trialKeyExpired": "انتهت صلاحية المفتاح التجريبي {0}",
"userProfile.uptimeExpire.trialKeyExpiredAt": "انتهت صلاحية المفتاح التجريبي في {0}",
"userProfile.uptimeExpire.trialKeyExpiresAt": "تنتهي صلاحية المفتاح التجريبي في {0}",
"userProfile.uptimeExpire.trialKeyExpiresIn": "تنتهي صلاحية المفتاح التجريبي في {0}",
"userProfile.uptimeExpire.uptime": "وقت التشغيل {0}",
"wanIpCheck.checkingWanIps": "جارٍ التحقق من عناوين WAN ...",
"wanIpCheck.dnsIssueUnableToResolveWanip4": "مشكلة DNS، غير قادر على حل wanip4.unraid.net",

View File

@@ -55,13 +55,6 @@
"connectSettings.updatedApiSettingsToast": "API সেটিংস আপডেট হয়েছে",
"downgradeOs.downgradeUnraidOs": "Unraid OS নিচে পাঠান",
"downgradeOs.pleaseFinishTheInitiatedUpdateTo": "ডাউনগ্রেড সক্রিয় করতে শুরু করা আপডেট পূরণ করুন।",
"downloadApiLogs.downloadUnraidApiLogs": "unraid-api লগ ডাউনলোড করুন",
"downloadApiLogs.ifYouAreAskedToSupply": "যদি আপনাকে লগ সরবরাহ করতে বলা হয়, অনুগ্রহ করে আমাদের যোগাযোগ পৃষ্ঠায় একটি সমর্থন অনুরোধ খুলুন এবং আপনার সংযুক্ত লগ সহ প্রাপ্ত ইমেল বার্তায় উত্তর দিন।",
"downloadApiLogs.theLogsMayContainSensitiveInformation": "লগগুলি সংবেদনশীল তথ্য অন্তর্ভুক্ত করতে পারে তাই সেগুলি সর্বজনীনভাবে পোস্ট করবেন না।",
"downloadApiLogs.thePrimaryMethodOfSupportFor": "Unraid Connect-এর জন্য প্রাথমিক সহায়তা পদ্ধতি হল আমাদের ফোরাম এবং ডিসকর্ডের মাধ্যমে।",
"downloadApiLogs.unraidConnectForums": "Unraid Connect ফোরাম",
"downloadApiLogs.unraidContactPage": "Unraid যোগাযোগ পৃষ্ঠা",
"downloadApiLogs.unraidDiscord": "Unraid ডিসকর্ড",
"headerOsVersion.apiVersionCopiedToClipboard": "API সংস্করণ ক্লিপবোর্ডে কপি করা হয়েছে",
"headerOsVersion.osVersionCopiedToClipboard": "OS সংস্করণ ক্লিপবোর্ডে কপি করা হয়েছে",
"headerOsVersion.unraidApi": "Unraid API",
@@ -473,12 +466,20 @@
"updateOs.downgrade.releaseNotes": "{0} রিলিজ নোটস",
"updateOs.ignoredRelease.remove": "অপসারণ",
"updateOs.ignoredRelease.removeFromIgnoreList": "অগ্রাহ্য তালিকা থেকে সরান",
"updateOs.ineligible.guidRequired": "OS আপডেটের জন্য একটি বৈধ GUID প্রয়োজন।",
"updateOs.ineligible.keyfileRequired": "OS আপডেটের জন্য একটি বৈধ কীফাইল প্রয়োজন।",
"updateOs.ineligible.osVersionRequired": "OS আপডেটের জন্য একটি বৈধ OS সংস্করণ প্রয়োজন।",
"updateOs.ineligible.updatesExpired": "আপনার {0} লাইসেন্স ক্রয়ের সময় এক বছরের বিনামূল্যে আপডেট অন্তর্ভুক্ত ছিল। আপনি এখন আপনার লাইসেন্স বাড়াতে এবং সর্বশেষ OS আপডেটগুলি অ্যাক্সেস করতে যোগ্য।",
"updateOs.ineligible.updatesExpiredWithAvailable": "আপনার {0} লাইসেন্স ক্রয়ের সময় এক বছরের জন্য বিনামূল্যে আপডেট অন্তর্ভুক্ত ছিল। আপনি এখন আপনার লাইসেন্স বৃদ্ধি করতে এবং সর্বশেষ OS আপডেট অ্যাক্সেস করতে পারবেন। আপনি এখনও {1} এ বা তার পূর্বে প্রকাশিত OS আপডেটগুলি অ্যাক্সেস করার জন্য যোগ্য।",
"updateOs.pleaseFinishTheInitiatedDowngradeTo": "আপডেট সক্রিয় করতে শুরু করা ডাউনগ্রেড সম্পূর্ণ করুন।",
"updateOs.rawChangelogRenderer.errorParsingChangelog": "চেঞ্জলগ পার্সিং ত্রুটি • {0}",
"updateOs.rawChangelogRenderer.itSHighlyRecommendedToReview": "আপডেট চালিয়ে যাওয়ার আগে চেঞ্জলগ পর্যালোচনা করার পরামর্শ দেওয়া হচ্ছে",
"updateOs.rawChangelogRenderer.loadingChangelog": "চেঞ্জলগ লোড হচ্ছে...",
"updateOs.rawChangelogRenderer.noChangelogContentAvailable": "কোনো চেঞ্জলগ কন্টেন্ট উপলব্ধ নেই",
"updateOs.rawChangelogRenderer.viewChangelogOnDocs": "ডক্সে চেঞ্জলগ দেখুন",
"updateOs.reboot.downgrade": "ডাউনগ্রেডের জন্য পুনরায় বুট প্রয়োজন",
"updateOs.reboot.thirdPartyDriversDownloading": "তৃতীয় পক্ষের ড্রাইভার আপডেট হচ্ছে",
"updateOs.reboot.update": "আপডেটের জন্য পুনরায় বুট প্রয়োজন",
"updateOs.status.cancel": "{0} বাতিল করুন",
"updateOs.status.checking": "পরীক্ষা করা হচ্ছে...",
"updateOs.status.downgrade": "ডাউনগ্রেড",
@@ -594,7 +595,15 @@
"userProfile.trial.startingYourFreeDayTrial": "আপনার ফ্রি ৩০ দিনের ট্রায়াল শুরু হচ্ছে",
"userProfile.trial.trialKeyCreated": "ট্রায়াল কী তৈরি করা হয়েছে",
"userProfile.trial.trialKeyCreationFailed": "ট্রায়াল কী তৈরি ব্যর্থ",
"userProfile.uptimeExpire.expired": "মেয়াদ পেরিয়েছে {0}",
"userProfile.uptimeExpire.expiredAt": "মেয়াদ পেরিয়েছে {0} এ",
"userProfile.uptimeExpire.expiresAt": "মেয়াদ শেষ হবে {0} এ",
"userProfile.uptimeExpire.expiresIn": "মেয়াদ শেষ হবে {0} মধ্যে",
"userProfile.uptimeExpire.serverUpSince": "সার্ভার আপ {0} থেকে",
"userProfile.uptimeExpire.trialKeyExpired": "পরীক্ষামূলক কী মেয়াদ পেরিয়েছে {0}",
"userProfile.uptimeExpire.trialKeyExpiredAt": "পরীক্ষামূলক কী মেয়াদ পেরিয়েছে {0} এ",
"userProfile.uptimeExpire.trialKeyExpiresAt": "পরীক্ষামূলক কী মেয়াদ শেষ হবে {0} এ",
"userProfile.uptimeExpire.trialKeyExpiresIn": "পরীক্ষামূলক কী মেয়াদ শেষ হবে {0} মধ্যে",
"userProfile.uptimeExpire.uptime": "আপটাইম {0}",
"wanIpCheck.checkingWanIps": "WAN IP পরীক্ষা করা হচ্ছে…",
"wanIpCheck.dnsIssueUnableToResolveWanip4": "DNS সমস্যা, wanip4.unraid.net সমাধান করতে অক্ষম",

View File

@@ -55,13 +55,6 @@
"connectSettings.updatedApiSettingsToast": "Configuració API actualitzada",
"downgradeOs.downgradeUnraidOs": "Degrada Unraid OS",
"downgradeOs.pleaseFinishTheInitiatedUpdateTo": "Si us plau, completa l'actualització iniciada per habilitar una degradació.",
"downloadApiLogs.downloadUnraidApiLogs": "Descarrega registres unraid-api",
"downloadApiLogs.ifYouAreAskedToSupply": "Si se't demana subministrar registres, si us plau, obre una petició de suport al nostre Pàgina de Contacte i respon al missatge de correu electrònic que rebis amb els teus registres adjunts.",
"downloadApiLogs.theLogsMayContainSensitiveInformation": "Els registres poden contenir informació sensible, així que no els publiquis públicament.",
"downloadApiLogs.thePrimaryMethodOfSupportFor": "El mètode principal de suport per a Unraid Connecta és a través dels nostres fòrums i Discord.",
"downloadApiLogs.unraidConnectForums": "Fòrums de Connecta de Unraid",
"downloadApiLogs.unraidContactPage": "Pàgina de Contacte de Unraid",
"downloadApiLogs.unraidDiscord": "Unraid Discord",
"headerOsVersion.apiVersionCopiedToClipboard": "Versió de l'API copiada al porta-retalls",
"headerOsVersion.osVersionCopiedToClipboard": "Versió OS copiada al porta-retalls",
"headerOsVersion.unraidApi": "Unraid API",
@@ -473,12 +466,20 @@
"updateOs.downgrade.releaseNotes": "Notes de llançament {0}",
"updateOs.ignoredRelease.remove": "Eliminar",
"updateOs.ignoredRelease.removeFromIgnoreList": "Eliminar de la llista d'ignorats",
"updateOs.ineligible.guidRequired": "Es requereix un GUID vàlid per comprovar actualitzacions del sistema operatiu.",
"updateOs.ineligible.keyfileRequired": "Es requereix un fitxer de clau vàlid per comprovar actualitzacions del sistema operatiu.",
"updateOs.ineligible.osVersionRequired": "Es requereix una versió del sistema operatiu vàlida per comprovar actualitzacions.",
"updateOs.ineligible.updatesExpired": "La teva llicència de {0} incloïa un any d'actualitzacions gratuïtes en el moment de la compra. Ara pots ampliar la teva llicència i accedir a les últimes actualitzacions del sistema operatiu.",
"updateOs.ineligible.updatesExpiredWithAvailable": "La teva llicència de {0} incloïa un any d'actualitzacions gratuïtes en el moment de la compra. Ara pots prorrogar la teva llicència i accedir a les darreres actualitzacions del sistema operatiu. Encara ets elegible per accedir a les actualitzacions publicades el dia o abans de {1}.",
"updateOs.pleaseFinishTheInitiatedDowngradeTo": "Si us plau, acabeu el desglossament iniciat per habilitar les actualitzacions.",
"updateOs.rawChangelogRenderer.errorParsingChangelog": "Error en analitzar el canvi de registre • {0}",
"updateOs.rawChangelogRenderer.itSHighlyRecommendedToReview": "Es recomana revisar el canvi de registre abans de continuar l'actualització",
"updateOs.rawChangelogRenderer.loadingChangelog": "Carregant canvi de registre...",
"updateOs.rawChangelogRenderer.noChangelogContentAvailable": "No hi ha contingut de canvi de registre disponible",
"updateOs.rawChangelogRenderer.viewChangelogOnDocs": "Veure canvi de registre en Docs",
"updateOs.reboot.downgrade": "Reinici requerit per desactualitzar",
"updateOs.reboot.thirdPartyDriversDownloading": "Actualitzant els controladors de tercers",
"updateOs.reboot.update": "Reinici requerit per actualitzar",
"updateOs.status.cancel": "Cancel·lar {0}",
"updateOs.status.checking": "Comprovant...",
"updateOs.status.downgrade": "Desactualitzar",
@@ -594,7 +595,15 @@
"userProfile.trial.startingYourFreeDayTrial": "Començant la vostra prova gratuïta de 30 dies",
"userProfile.trial.trialKeyCreated": "Clau de prova creada",
"userProfile.trial.trialKeyCreationFailed": "Creació de la clau de prova fallida",
"userProfile.uptimeExpire.expired": "Caducat {0}",
"userProfile.uptimeExpire.expiredAt": "Caducat a les {0}",
"userProfile.uptimeExpire.expiresAt": "Caducarà a les {0}",
"userProfile.uptimeExpire.expiresIn": "Caducarà en {0}",
"userProfile.uptimeExpire.serverUpSince": "Servidor actiu des de {0}",
"userProfile.uptimeExpire.trialKeyExpired": "Clau de prova caducada {0}",
"userProfile.uptimeExpire.trialKeyExpiredAt": "Clau de prova caducada a les {0}",
"userProfile.uptimeExpire.trialKeyExpiresAt": "La clau de prova caducarà a les {0}",
"userProfile.uptimeExpire.trialKeyExpiresIn": "La clau de prova caducarà en {0}",
"userProfile.uptimeExpire.uptime": "Temps d'activitat {0}",
"wanIpCheck.checkingWanIps": "Comprovant IP WAN...",
"wanIpCheck.dnsIssueUnableToResolveWanip4": "Problema de DNS, no es pot resoldre wanip4.unraid.net",

View File

@@ -55,13 +55,6 @@
"connectSettings.updatedApiSettingsToast": "Aktualizovaná nastavení API",
"downgradeOs.downgradeUnraidOs": "Downgrade Unraid OS",
"downgradeOs.pleaseFinishTheInitiatedUpdateTo": "Pro povolení downgradů prosím dokončete zahájenou aktualizaci.",
"downloadApiLogs.downloadUnraidApiLogs": "Stáhnout logy unraid-api",
"downloadApiLogs.ifYouAreAskedToSupply": "Pokud budete požádáni o dodání logů, otevřete prosím požadavek o podporu na naší kontaktní stránce a odpovězte na email, který obdržíte, s připojenými logy.",
"downloadApiLogs.theLogsMayContainSensitiveInformation": "Logy mohou obsahovat citlivé informace, proto je nezveřejňujte veřejně.",
"downloadApiLogs.thePrimaryMethodOfSupportFor": "Primární metoda podpory pro Unraid Connect je prostřednictvím našich fór a Discordu.",
"downloadApiLogs.unraidConnectForums": "Fóra Unraid Connect",
"downloadApiLogs.unraidContactPage": "Kontaktní stránka Unraid",
"downloadApiLogs.unraidDiscord": "Discord Unraid",
"headerOsVersion.apiVersionCopiedToClipboard": "Verze API zkopírována do schránky",
"headerOsVersion.osVersionCopiedToClipboard": "Verze OS zkopírována do schránky",
"headerOsVersion.unraidApi": "Unraid API",
@@ -473,12 +466,20 @@
"updateOs.downgrade.releaseNotes": "{0} Poznámky k verzi",
"updateOs.ignoredRelease.remove": "Odstranit",
"updateOs.ignoredRelease.removeFromIgnoreList": "Odstranit z ignorovaných seznamů",
"updateOs.ineligible.guidRequired": "Platný GUID je vyžadován pro kontrolu aktualizací OS.",
"updateOs.ineligible.keyfileRequired": "Platný klíčový soubor je vyžadován pro kontrolu aktualizací OS.",
"updateOs.ineligible.osVersionRequired": "Platná verze OS je vyžadována pro kontrolu aktualizací OS.",
"updateOs.ineligible.updatesExpired": "Vaše licence {0} zahrnovala jeden rok bezplatných aktualizací v době nákupu. Nyní máte možnost prodloužit svou licenci a získat přístup k nejnovějším aktualizacím OS.",
"updateOs.ineligible.updatesExpiredWithAvailable": "Vaše licence {0} zahrnovala jeden rok bezplatných aktualizací v době nákupu. Nyní máte nárok na prodloužení licence a přístup k nejnovějším aktualizacím OS. Stále máte nárok na přístup k aktualizacím OS zveřejněným dne nebo před {1}.",
"updateOs.pleaseFinishTheInitiatedDowngradeTo": "Dokončete zahájené downgrade pro povolení aktualizací.",
"updateOs.rawChangelogRenderer.errorParsingChangelog": "Chyba při parsování changelogu • {0}",
"updateOs.rawChangelogRenderer.itSHighlyRecommendedToReview": "Důrazně doporučujeme zkontrolovat changelog před pokračováním v aktualizaci",
"updateOs.rawChangelogRenderer.loadingChangelog": "Načítání changelogu...",
"updateOs.rawChangelogRenderer.noChangelogContentAvailable": "Není k dispozici žádný obsah changelogu",
"updateOs.rawChangelogRenderer.viewChangelogOnDocs": "Zobrazit changelog v dokumentech",
"updateOs.reboot.downgrade": "Je vyžadován restart pro downgrade",
"updateOs.reboot.thirdPartyDriversDownloading": "Aktualizace ovladačů třetích stran",
"updateOs.reboot.update": "Je vyžadován restart pro aktualizaci",
"updateOs.status.cancel": "Zrušit {0}",
"updateOs.status.checking": "kontrola...",
"updateOs.status.downgrade": "Downgrade",
@@ -594,7 +595,15 @@
"userProfile.trial.startingYourFreeDayTrial": "Spouštíte svou bezplatnou 30denní zkušební verzi",
"userProfile.trial.trialKeyCreated": "Zkušební klíč vytvořen",
"userProfile.trial.trialKeyCreationFailed": "Vytvoření zkušebního klíče selhalo",
"userProfile.uptimeExpire.expired": "Platnost vypršela {0}",
"userProfile.uptimeExpire.expiredAt": "Platnost vypršela v {0}",
"userProfile.uptimeExpire.expiresAt": "Platnost vyprší v {0}",
"userProfile.uptimeExpire.expiresIn": "Platnost vyprší za {0}",
"userProfile.uptimeExpire.serverUpSince": "Server je v provozu od {0}",
"userProfile.uptimeExpire.trialKeyExpired": "Zkušební klíč vypršela platnost {0}",
"userProfile.uptimeExpire.trialKeyExpiredAt": "Platnost zkušebního klíče vypršela v {0}",
"userProfile.uptimeExpire.trialKeyExpiresAt": "Platnost zkušebního klíče vyprší v {0}",
"userProfile.uptimeExpire.trialKeyExpiresIn": "Platnost zkušebního klíče vyprší za {0}",
"userProfile.uptimeExpire.uptime": "Doba provozu {0}",
"wanIpCheck.checkingWanIps": "Kontrola WAN IPs…",
"wanIpCheck.dnsIssueUnableToResolveWanip4": "Problém DNS, nelze vyřešit wanip4.unraid.net",

View File

@@ -55,13 +55,6 @@
"connectSettings.updatedApiSettingsToast": "Opdaterede API-indstillinger",
"downgradeOs.downgradeUnraidOs": "Nedgrader Unraid OS",
"downgradeOs.pleaseFinishTheInitiatedUpdateTo": "Afslut venligst den påbegyndte opdatering for at muliggøre en nedgradering.",
"downloadApiLogs.downloadUnraidApiLogs": "Download unraid-api logs",
"downloadApiLogs.ifYouAreAskedToSupply": "Hvis du bliver bedt om at levere logfiler, skal du åbne en supportanmodning på vores kontaktside og svare på e-mailmeddelelsen du modtager, med dine logfiler vedhæftet.",
"downloadApiLogs.theLogsMayContainSensitiveInformation": "Logfilerne kan indeholde følsomme oplysninger, så post dem ikke offentligt.",
"downloadApiLogs.thePrimaryMethodOfSupportFor": "Den primære metode til support for Unraid Connect er gennem vores fora og Discord.",
"downloadApiLogs.unraidConnectForums": "Unraid Connect Fora",
"downloadApiLogs.unraidContactPage": "Unraid Kontaktside",
"downloadApiLogs.unraidDiscord": "Unraid Discord",
"headerOsVersion.apiVersionCopiedToClipboard": "API-version kopieret til udklipsholder",
"headerOsVersion.osVersionCopiedToClipboard": "OS-version kopieret til udklipsholder",
"headerOsVersion.unraidApi": "Unraid API",
@@ -473,12 +466,20 @@
"updateOs.downgrade.releaseNotes": "{0} Udgivelsesnoter",
"updateOs.ignoredRelease.remove": "Fjern",
"updateOs.ignoredRelease.removeFromIgnoreList": "Fjern fra ignorliste",
"updateOs.ineligible.guidRequired": "En gyldig GUID er påkrævet for at søge efter OS-opdateringer.",
"updateOs.ineligible.keyfileRequired": "En gyldig nøglefil er påkrævet for at søge efter OS-opdateringer.",
"updateOs.ineligible.osVersionRequired": "En gyldig OS-version er påkrævet for at søge efter OS-opdateringer.",
"updateOs.ineligible.updatesExpired": "Din {0} licens inkluderede et år med gratis opdateringer ved købstidspunktet. Du er nu berettiget til at forlænge din licens og få adgang til de nyeste OS-opdateringer.",
"updateOs.ineligible.updatesExpiredWithAvailable": "Din {0} licens inkluderede et år med gratis opdateringer på købstidspunktet. Du er nu berettiget til at forlænge din licens og få adgang til de nyeste OS-opdateringer. Du er stadig berettiget til at få adgang til OS-opdateringer, der blev udgivet den eller før {1}.",
"updateOs.pleaseFinishTheInitiatedDowngradeTo": "Afslut venligst den påbegyndte nedgradering for at aktivere opdateringer.",
"updateOs.rawChangelogRenderer.errorParsingChangelog": "Fejl ved indlæsning af ændringslog • {0}",
"updateOs.rawChangelogRenderer.itSHighlyRecommendedToReview": "Det anbefales stærkt at gennemgå ændringsloggen, før du fortsætter din opdatering",
"updateOs.rawChangelogRenderer.loadingChangelog": "Indlæser ændringslog...",
"updateOs.rawChangelogRenderer.noChangelogContentAvailable": "Ingen ændringslog-indhold tilgængeligt",
"updateOs.rawChangelogRenderer.viewChangelogOnDocs": "Se ændringslog på dokumenter",
"updateOs.reboot.downgrade": "Genstart krævet for nedgradering",
"updateOs.reboot.thirdPartyDriversDownloading": "Opdaterer tredjepartsdrivere",
"updateOs.reboot.update": "Genstart krævet for opdatering",
"updateOs.status.cancel": "Annuller {0}",
"updateOs.status.checking": "kontrollerer...",
"updateOs.status.downgrade": "Nedgradering",
@@ -594,7 +595,15 @@
"userProfile.trial.startingYourFreeDayTrial": "Starter din gratis 30 dages prøveperiode",
"userProfile.trial.trialKeyCreated": "Prøveversion nøgle oprettet",
"userProfile.trial.trialKeyCreationFailed": "Prøveversion nøgle oprettelse mislykkedes",
"userProfile.uptimeExpire.expired": "Udløbet {0}",
"userProfile.uptimeExpire.expiredAt": "Udløbet kl. {0}",
"userProfile.uptimeExpire.expiresAt": "Udløber kl. {0}",
"userProfile.uptimeExpire.expiresIn": "Udløber om {0}",
"userProfile.uptimeExpire.serverUpSince": "Server oppe siden {0}",
"userProfile.uptimeExpire.trialKeyExpired": "Prøvenøgle udløbet {0}",
"userProfile.uptimeExpire.trialKeyExpiredAt": "Prøvenøgle udløbet kl. {0}",
"userProfile.uptimeExpire.trialKeyExpiresAt": "Prøvenøgle udløber kl. {0}",
"userProfile.uptimeExpire.trialKeyExpiresIn": "Prøvenøgle udløber om {0}",
"userProfile.uptimeExpire.uptime": "Oppetid {0}",
"wanIpCheck.checkingWanIps": "Kontrollerer WAN IP'er...",
"wanIpCheck.dnsIssueUnableToResolveWanip4": "DNS-problem, kan ikke løse wanip4.unraid.net",

View File

@@ -55,13 +55,6 @@
"connectSettings.updatedApiSettingsToast": "API-Einstellungen aktualisiert",
"downgradeOs.downgradeUnraidOs": "Unraid OS herabstufen",
"downgradeOs.pleaseFinishTheInitiatedUpdateTo": "Bitte beenden Sie das gestartete Update, um eine Herabstufung zu ermöglichen.",
"downloadApiLogs.downloadUnraidApiLogs": "Unraid-API-Protokolle herunterladen",
"downloadApiLogs.ifYouAreAskedToSupply": "Wenn Sie gebeten werden, Protokolle bereitzustellen, öffnen Sie bitte eine Supportanfrage auf unserer Kontaktseite und antworten Sie auf die E-Mail-Nachricht, die Sie mit Ihren angehängten Protokollen erhalten.",
"downloadApiLogs.theLogsMayContainSensitiveInformation": "Die Protokolle können sensible Informationen enthalten, posten Sie sie daher nicht öffentlich.",
"downloadApiLogs.thePrimaryMethodOfSupportFor": "Die primäre Supportmethode für Unraid Connect erfolgt über unsere Foren und Discord.",
"downloadApiLogs.unraidConnectForums": "Unraid Connect-Foren",
"downloadApiLogs.unraidContactPage": "Unraid-Kontaktseite",
"downloadApiLogs.unraidDiscord": "Unraid Discord",
"headerOsVersion.apiVersionCopiedToClipboard": "API-Version in die Zwischenablage kopiert",
"headerOsVersion.osVersionCopiedToClipboard": "Betriebssystemversion in die Zwischenablage kopiert",
"headerOsVersion.unraidApi": "Unraid API",
@@ -473,12 +466,20 @@
"updateOs.downgrade.releaseNotes": "{0} Versionshinweise",
"updateOs.ignoredRelease.remove": "Entfernen",
"updateOs.ignoredRelease.removeFromIgnoreList": "Von der Ignorierliste entfernen",
"updateOs.ineligible.guidRequired": "Eine gültige GUID ist erforderlich, um nach OS-Updates zu suchen.",
"updateOs.ineligible.keyfileRequired": "Eine gültige Schlüsseldatei ist erforderlich, um nach OS-Updates zu suchen.",
"updateOs.ineligible.osVersionRequired": "Eine gültige OS-Version ist erforderlich, um nach OS-Updates zu suchen.",
"updateOs.ineligible.updatesExpired": "Ihre {0}-Lizenz enthielt beim Kauf ein Jahr kostenlose Updates. Sie sind jetzt berechtigt, Ihre Lizenz zu verlängern und auf die neuesten OS-Updates zuzugreifen.",
"updateOs.ineligible.updatesExpiredWithAvailable": "Ihre {0}-Lizenz umfasste zum Zeitpunkt des Kaufs ein Jahr kostenloser Updates. Jetzt können Sie Ihre Lizenz verlängern und die neuesten Betriebssystemupdates nutzen. Sie haben weiterhin Zugriff auf Betriebssystemupdates, die am oder vor dem {1} veröffentlicht wurden.",
"updateOs.pleaseFinishTheInitiatedDowngradeTo": "Bitte schließen Sie das gestartete Downgrade ab, um Updates zu aktivieren.",
"updateOs.rawChangelogRenderer.errorParsingChangelog": "Fehler beim Parsen des Änderungsprotokolls • {0}",
"updateOs.rawChangelogRenderer.itSHighlyRecommendedToReview": "Es wird dringend empfohlen, das Änderungsprotokoll zu überprüfen, bevor Sie mit Ihrem Update fortfahren.",
"updateOs.rawChangelogRenderer.loadingChangelog": "Änderungsprotokoll wird geladen...",
"updateOs.rawChangelogRenderer.noChangelogContentAvailable": "Kein Inhalt im Änderungsprotokoll verfügbar",
"updateOs.rawChangelogRenderer.viewChangelogOnDocs": "Änderungsprotokoll in Dokumenten anzeigen",
"updateOs.reboot.downgrade": "Neustart erforderlich für Downgrade",
"updateOs.reboot.thirdPartyDriversDownloading": "3rd-Party-Treiber werden aktualisiert",
"updateOs.reboot.update": "Neustart erforderlich für Update",
"updateOs.status.cancel": "Abbrechen {0}",
"updateOs.status.checking": "Überprüfung...",
"updateOs.status.downgrade": "Downgrade",
@@ -594,7 +595,15 @@
"userProfile.trial.startingYourFreeDayTrial": "Starten Ihrer kostenlosen 30-tägigen Testversion",
"userProfile.trial.trialKeyCreated": "Testschlüssel erstellt",
"userProfile.trial.trialKeyCreationFailed": "Erstellung des Testschlüssels fehlgeschlagen",
"userProfile.uptimeExpire.expired": "Abgelaufen {0}",
"userProfile.uptimeExpire.expiredAt": "Abgelaufen am {0}",
"userProfile.uptimeExpire.expiresAt": "Läuft ab am {0}",
"userProfile.uptimeExpire.expiresIn": "Läuft ab in {0}",
"userProfile.uptimeExpire.serverUpSince": "Server hoch seit {0}",
"userProfile.uptimeExpire.trialKeyExpired": "Testschlüssel abgelaufen {0}",
"userProfile.uptimeExpire.trialKeyExpiredAt": "Testschlüssel abgelaufen am {0}",
"userProfile.uptimeExpire.trialKeyExpiresAt": "Testschlüssel läuft ab am {0}",
"userProfile.uptimeExpire.trialKeyExpiresIn": "Testschlüssel läuft ab in {0}",
"userProfile.uptimeExpire.uptime": "Betriebszeit {0}",
"wanIpCheck.checkingWanIps": "WAN-IPs werden überprüft…",
"wanIpCheck.dnsIssueUnableToResolveWanip4": "DNS-Problem, kann wanip4.unraid.net nicht auflösen",

View File

@@ -55,13 +55,6 @@
"connectSettings.updatedApiSettingsToast": "Updated API Settings",
"downgradeOs.downgradeUnraidOs": "Downgrade Unraid OS",
"downgradeOs.pleaseFinishTheInitiatedUpdateTo": "Please finish the initiated update to enable a downgrade.",
"downloadApiLogs.downloadUnraidApiLogs": "Download unraid-api Logs",
"downloadApiLogs.ifYouAreAskedToSupply": "If you are asked to supply logs, please open a support request on our Contact Page and reply to the email message you receive with your logs attached.",
"downloadApiLogs.theLogsMayContainSensitiveInformation": "The logs may contain sensitive information so do not post them publicly.",
"downloadApiLogs.thePrimaryMethodOfSupportFor": "The primary method of support for Unraid Connect is through our forums and Discord.",
"downloadApiLogs.unraidConnectForums": "Unraid Connect Forums",
"downloadApiLogs.unraidContactPage": "Unraid Contact Page",
"downloadApiLogs.unraidDiscord": "Unraid Discord",
"headerOsVersion.apiVersionCopiedToClipboard": "API version copied to clipboard",
"headerOsVersion.osVersionCopiedToClipboard": "OS version copied to clipboard",
"headerOsVersion.unraidApi": "Unraid API",
@@ -311,7 +304,11 @@
"notifications.sidebar.confirmDeleteAll.confirmText": "Delete All",
"notifications.sidebar.confirmDeleteAll.description": "This will permanently delete all archived notifications currently on your Unraid server. This action cannot be undone.",
"notifications.sidebar.confirmDeleteAll.title": "Delete All Archived Notifications",
"notifications.sidebar.confirmDeleteUnread.confirmText": "Delete",
"notifications.sidebar.confirmDeleteUnread.description": "Are you sure you want to permanently delete all unread notifications? This cannot be undone.",
"notifications.sidebar.confirmDeleteUnread.title": "Delete unread notifications",
"notifications.sidebar.deleteAllAction": "Delete All",
"notifications.sidebar.processingStatus": "Processing {{processed}} / {{total}}",
"notifications.sidebar.editSettingsTooltip": "Edit Notification Settings",
"notifications.sidebar.filters.alert": "Alert",
"notifications.sidebar.filters.all": "All Types",
@@ -473,12 +470,20 @@
"updateOs.downgrade.releaseNotes": "{0} Release Notes",
"updateOs.ignoredRelease.remove": "Remove",
"updateOs.ignoredRelease.removeFromIgnoreList": "Remove from ignore list",
"updateOs.ineligible.guidRequired": "A valid GUID is required to check for OS updates.",
"updateOs.ineligible.keyfileRequired": "A valid keyfile is required to check for OS updates.",
"updateOs.ineligible.osVersionRequired": "A valid OS version is required to check for OS updates.",
"updateOs.ineligible.updatesExpired": "Your {0} license included one year of free updates at the time of purchase. You are now eligible to extend your license and access the latest OS updates.",
"updateOs.ineligible.updatesExpiredWithAvailable": "Your {0} license included one year of free updates at the time of purchase. You are now eligible to extend your license and access the latest OS updates. You are still eligible to access OS updates that were published on or before {1}.",
"updateOs.pleaseFinishTheInitiatedDowngradeTo": "Please finish the initiated downgrade to enable updates.",
"updateOs.rawChangelogRenderer.errorParsingChangelog": "Error Parsing Changelog • {0}",
"updateOs.rawChangelogRenderer.itSHighlyRecommendedToReview": "It's highly recommended to review the changelog before continuing your update",
"updateOs.rawChangelogRenderer.loadingChangelog": "Loading changelog...",
"updateOs.rawChangelogRenderer.noChangelogContentAvailable": "No changelog content available",
"updateOs.rawChangelogRenderer.viewChangelogOnDocs": "View Changelog on Docs",
"updateOs.reboot.downgrade": "Reboot Required for Downgrade",
"updateOs.reboot.thirdPartyDriversDownloading": "Updating 3rd party drivers",
"updateOs.reboot.update": "Reboot Required for Update",
"updateOs.status.cancel": "Cancel {0}",
"updateOs.status.checking": "Checking...",
"updateOs.status.downgrade": "Downgrade",
@@ -594,7 +599,15 @@
"userProfile.trial.startingYourFreeDayTrial": "Starting your free 30 day trial",
"userProfile.trial.trialKeyCreated": "Trial Key Created",
"userProfile.trial.trialKeyCreationFailed": "Trial Key Creation Failed",
"userProfile.uptimeExpire.expired": "Expired {0}",
"userProfile.uptimeExpire.expiredAt": "Expired at {0}",
"userProfile.uptimeExpire.expiresAt": "Expires at {0}",
"userProfile.uptimeExpire.expiresIn": "Expires in {0}",
"userProfile.uptimeExpire.serverUpSince": "Server Up Since {0}",
"userProfile.uptimeExpire.trialKeyExpired": "Trial Key Expired {0}",
"userProfile.uptimeExpire.trialKeyExpiredAt": "Trial Key Expired at {0}",
"userProfile.uptimeExpire.trialKeyExpiresAt": "Trial Key Expires at {0}",
"userProfile.uptimeExpire.trialKeyExpiresIn": "Trial Key Expires in {0}",
"userProfile.uptimeExpire.uptime": "Uptime {0}",
"wanIpCheck.checkingWanIps": "Checking WAN IPs…",
"wanIpCheck.dnsIssueUnableToResolveWanip4": "DNS issue, unable to resolve wanip4.unraid.net",

View File

@@ -55,13 +55,6 @@
"connectSettings.updatedApiSettingsToast": "Configuraciones del API actualizadas",
"downgradeOs.downgradeUnraidOs": "Degradar Sistemas Operativos de Unraid",
"downgradeOs.pleaseFinishTheInitiatedUpdateTo": "Por favor termine la actualización iniciada para permitir una degradación.",
"downloadApiLogs.downloadUnraidApiLogs": "Descargar registros de unraid-api",
"downloadApiLogs.ifYouAreAskedToSupply": "Si se le solicita adjuntar registros, por favor abra una solicitud de soporte en nuestra Página de Contacto y responda al mensaje de correo electrónico que reciba con sus registros adjuntos.",
"downloadApiLogs.theLogsMayContainSensitiveInformation": "Los registros pueden contener información sensible, así que no los publique públicamente.",
"downloadApiLogs.thePrimaryMethodOfSupportFor": "El método principal de soporte para Unraid Connect es a través de nuestros foros y Discord.",
"downloadApiLogs.unraidConnectForums": "Foros de Unraid Connect",
"downloadApiLogs.unraidContactPage": "Página de Contacto de Unraid",
"downloadApiLogs.unraidDiscord": "Unraid Discord",
"headerOsVersion.apiVersionCopiedToClipboard": "Versión del API copiada al portapapeles",
"headerOsVersion.osVersionCopiedToClipboard": "Versión del SO copiada al portapapeles",
"headerOsVersion.unraidApi": "API de Unraid",
@@ -473,12 +466,20 @@
"updateOs.downgrade.releaseNotes": "{0} Notas de Version",
"updateOs.ignoredRelease.remove": "Eliminar",
"updateOs.ignoredRelease.removeFromIgnoreList": "Eliminar de la lista de ignorados",
"updateOs.ineligible.guidRequired": "Se requiere un GUID válido para verificar actualizaciones del sistema operativo.",
"updateOs.ineligible.keyfileRequired": "Se requiere un archivo de clave válido para verificar actualizaciones del sistema operativo.",
"updateOs.ineligible.osVersionRequired": "Se requiere una versión válida del sistema operativo para verificar actualizaciones.",
"updateOs.ineligible.updatesExpired": "Tu licencia de {0} incluyó un año de actualizaciones gratuitas al momento de la compra. Ahora puedes extender tu licencia y acceder a las últimas actualizaciones del sistema operativo.",
"updateOs.ineligible.updatesExpiredWithAvailable": "Su licencia {0} incluyó un año de actualizaciones gratuitas en el momento de la compra. Ahora es elegible para extender su licencia y acceder a las últimas actualizaciones del sistema operativo. Todavía es elegible para acceder a las actualizaciones del sistema operativo que se publicaron en o antes del {1}.",
"updateOs.pleaseFinishTheInitiatedDowngradeTo": "Por favor, termine la degradación iniciada para habilitar las actualizaciones.",
"updateOs.rawChangelogRenderer.errorParsingChangelog": "Error al Analizar el Registro de Cambios • {0}",
"updateOs.rawChangelogRenderer.itSHighlyRecommendedToReview": "Es muy recomendable revisar el registro de cambios antes de continuar con su actualización",
"updateOs.rawChangelogRenderer.loadingChangelog": "Cargando registro de cambios...",
"updateOs.rawChangelogRenderer.noChangelogContentAvailable": "No hay contenido del registro de cambios disponible",
"updateOs.rawChangelogRenderer.viewChangelogOnDocs": "Ver Registro de Cambios en Documentos",
"updateOs.reboot.downgrade": "Se requiere reinicio para Degradar",
"updateOs.reboot.thirdPartyDriversDownloading": "Actualizando controladores de terceros",
"updateOs.reboot.update": "Se requiere reinicio para Actualizar",
"updateOs.status.cancel": "Cancelar {0}",
"updateOs.status.checking": "Verificando...",
"updateOs.status.downgrade": "Degradar",
@@ -594,7 +595,15 @@
"userProfile.trial.startingYourFreeDayTrial": "Iniciando su prueba gratuita de 30 días",
"userProfile.trial.trialKeyCreated": "Clave de Prueba Creada",
"userProfile.trial.trialKeyCreationFailed": "Falló la Creación de Clave de Prueba",
"userProfile.uptimeExpire.expired": "Expirado {0}",
"userProfile.uptimeExpire.expiredAt": "Expirado el {0}",
"userProfile.uptimeExpire.expiresAt": "Expira el {0}",
"userProfile.uptimeExpire.expiresIn": "Expira en {0}",
"userProfile.uptimeExpire.serverUpSince": "Servidor Activado Desde {0}",
"userProfile.uptimeExpire.trialKeyExpired": "Clave de Prueba Expirada {0}",
"userProfile.uptimeExpire.trialKeyExpiredAt": "Clave de Prueba Expirada el {0}",
"userProfile.uptimeExpire.trialKeyExpiresAt": "Clave de Prueba Expira el {0}",
"userProfile.uptimeExpire.trialKeyExpiresIn": "Clave de Prueba Expira en {0}",
"userProfile.uptimeExpire.uptime": "Tiempo de Actividad {0}",
"wanIpCheck.checkingWanIps": "Verificando IPs WAN…",
"wanIpCheck.dnsIssueUnableToResolveWanip4": "Problema de DNS, incapaz de resolver wanip4.unraid.net",

View File

@@ -55,13 +55,6 @@
"connectSettings.updatedApiSettingsToast": "Paramètres API mis à jour",
"downgradeOs.downgradeUnraidOs": "Rétrograder le système Unraid OS",
"downgradeOs.pleaseFinishTheInitiatedUpdateTo": "Veuillez terminer la mise à jour initiée pour autoriser une rétrogradation.",
"downloadApiLogs.downloadUnraidApiLogs": "Télécharger les journaux Unraid API",
"downloadApiLogs.ifYouAreAskedToSupply": "Si l'on vous demande de fournir des journaux, veuillez ouvrir une demande d'assistance sur notre page de contact et répondre au message électronique que vous recevez avec vos journaux en pièce jointe.",
"downloadApiLogs.theLogsMayContainSensitiveInformation": "Les journaux peuvent contenir des informations sensibles, ne les publiez donc pas publiquement.",
"downloadApiLogs.thePrimaryMethodOfSupportFor": "La méthode principale de support pour Unraid Connect se fait via nos forums et Discord.",
"downloadApiLogs.unraidConnectForums": "Forums de connexion Unraid",
"downloadApiLogs.unraidContactPage": "Page de contact Unraid",
"downloadApiLogs.unraidDiscord": "Discord Unraid",
"headerOsVersion.apiVersionCopiedToClipboard": "Version de l'API copiée dans le presse-papiers",
"headerOsVersion.osVersionCopiedToClipboard": "Version de l'OS copiée dans le presse-papiers",
"headerOsVersion.unraidApi": "API Unraid",
@@ -473,12 +466,20 @@
"updateOs.downgrade.releaseNotes": "{0} Notes de version",
"updateOs.ignoredRelease.remove": "Supprimer",
"updateOs.ignoredRelease.removeFromIgnoreList": "Retirer de la liste d'ignorance",
"updateOs.ineligible.guidRequired": "Un GUID valide est requis pour vérifier les mises à jour du système d'exploitation.",
"updateOs.ineligible.keyfileRequired": "Un fichier de clé valide est requis pour vérifier les mises à jour du système d'exploitation.",
"updateOs.ineligible.osVersionRequired": "Une version valide du système d'exploitation est requise pour vérifier les mises à jour du système d'exploitation.",
"updateOs.ineligible.updatesExpired": "Votre licence {0} comprenait un an de mises à jour gratuites au moment de l'achat. Vous êtes maintenant éligible pour prolonger votre licence et accéder aux dernières mises à jour du système d'exploitation.",
"updateOs.ineligible.updatesExpiredWithAvailable": "Votre licence {0} comprenait un an de mises à jour gratuites au moment de l'achat. Vous êtes maintenant éligible pour prolonger votre licence et accéder aux dernières mises à jour du système d'exploitation. Vous êtes toujours éligible pour accéder aux mises à jour du système d'exploitation publiées le {1} ou avant cette date.",
"updateOs.pleaseFinishTheInitiatedDowngradeTo": "Veuillez terminer la rétrogradation initiée pour activer les mises à jour.",
"updateOs.rawChangelogRenderer.errorParsingChangelog": "Erreur d'analyse du journal des modifications • {0}",
"updateOs.rawChangelogRenderer.itSHighlyRecommendedToReview": "Il est fortement recommandé de revoir le journal des modifications avant de continuer votre mise à jour",
"updateOs.rawChangelogRenderer.loadingChangelog": "Chargement du journal des modifications...",
"updateOs.rawChangelogRenderer.noChangelogContentAvailable": "Aucun contenu du journal des modifications disponible",
"updateOs.rawChangelogRenderer.viewChangelogOnDocs": "Voir le journal des modifications dans les documents",
"updateOs.reboot.downgrade": "Redémarrage requis pour la rétrogradation",
"updateOs.reboot.thirdPartyDriversDownloading": "Mise à jour des pilotes tiers",
"updateOs.reboot.update": "Redémarrage requis pour la mise à jour",
"updateOs.status.cancel": "Annuler {0}",
"updateOs.status.checking": "vérification...",
"updateOs.status.downgrade": "Rétrograder",
@@ -594,7 +595,15 @@
"userProfile.trial.startingYourFreeDayTrial": "Démarrage de votre essai gratuit de 30 jours",
"userProfile.trial.trialKeyCreated": "Clé d'essai créée",
"userProfile.trial.trialKeyCreationFailed": "Échec de la création de la clé d'essai",
"userProfile.uptimeExpire.expired": "Expiré {0}",
"userProfile.uptimeExpire.expiredAt": "Expiré à {0}",
"userProfile.uptimeExpire.expiresAt": "Expire à {0}",
"userProfile.uptimeExpire.expiresIn": "Expire dans {0}",
"userProfile.uptimeExpire.serverUpSince": "Serveur en marche depuis {0}",
"userProfile.uptimeExpire.trialKeyExpired": "Clé d'essai expirée {0}",
"userProfile.uptimeExpire.trialKeyExpiredAt": "Clé d'essai expirée à {0}",
"userProfile.uptimeExpire.trialKeyExpiresAt": "Clé d'essai expire à {0}",
"userProfile.uptimeExpire.trialKeyExpiresIn": "Clé d'essai expire dans {0}",
"userProfile.uptimeExpire.uptime": "Disponibilité {0}",
"wanIpCheck.checkingWanIps": "Vérification des adresses IP WAN…",
"wanIpCheck.dnsIssueUnableToResolveWanip4": "Problème DNS, impossible de résoudre wanip4.unraid.net",

View File

@@ -55,13 +55,6 @@
"connectSettings.updatedApiSettingsToast": "अपडेटेड एपीआई सेटिंग्स",
"downgradeOs.downgradeUnraidOs": "Unraid OS डाउनग्रेड करें",
"downgradeOs.pleaseFinishTheInitiatedUpdateTo": "डाउनग्रेड को सक्षम करने के लिए शुरू की गई अपडेट को पूरा करें।",
"downloadApiLogs.downloadUnraidApiLogs": "unraid-api लॉग डाउनलोड करें",
"downloadApiLogs.ifYouAreAskedToSupply": "यदि आपको लॉग प्रदान करने के लिए कहा जाता है, तो कृपया हमारे संपर्क पृष्ठ पर एक सहायता अनुरोध भेजें और आपको प्राप्त होने वाले ईमेल संदेश का उत्तर अपने लॉग्स संलग्न करके दें।",
"downloadApiLogs.theLogsMayContainSensitiveInformation": "लॉग्स में संवेदनशील जानकारी हो सकती है, इसलिए उन्हें सार्वजनिक रूप से पोस्ट न करें।",
"downloadApiLogs.thePrimaryMethodOfSupportFor": "Unraid Connect के लिए समर्थन का प्रमुख तरीका हमारे मंचों और Discord के माध्यम से है।",
"downloadApiLogs.unraidConnectForums": "Unraid Connect Forums",
"downloadApiLogs.unraidContactPage": "Unraid संपर्क पृष्ठ",
"downloadApiLogs.unraidDiscord": "Unraid Discord",
"headerOsVersion.apiVersionCopiedToClipboard": "एपीआई संस्करण क्लिपबोर्ड में कॉपी किया गया",
"headerOsVersion.osVersionCopiedToClipboard": "OS संस्करण क्लिपबोर्ड में कॉपी किया गया",
"headerOsVersion.unraidApi": "Unraid एपीआई",
@@ -473,12 +466,20 @@
"updateOs.downgrade.releaseNotes": "{0} रिलीज नोट्स",
"updateOs.ignoredRelease.remove": "निकालें",
"updateOs.ignoredRelease.removeFromIgnoreList": "अवरोध सूची से हटाएं",
"updateOs.ineligible.guidRequired": "OS अपडेट्स की जाँच के लिए एक वैध GUID आवश्यक है।",
"updateOs.ineligible.keyfileRequired": "OS अपडेट्स की जाँच के लिए एक वैध कीफ़ाइल आवश्यक है।",
"updateOs.ineligible.osVersionRequired": "OS अपडेट्स की जाँच के लिए एक वैध OS संस्करण आवश्यक है।",
"updateOs.ineligible.updatesExpired": "आपकी {0} लाइसेंस में खरीद के समय एक वर्ष मुफ्त अपडेट शामिल थे। अब आप अपनी लाइसेंस का विस्तार करने और नवीनतम OS अपडेट्स का उपयोग करने के लिए पात्र हैं।",
"updateOs.ineligible.updatesExpiredWithAvailable": "आपकी {0} लाइसेंस में खरीदारी के समय एक साल का मुफ्त अपडेट शामिल था। अब आप अपनी लाइसेंस को बढ़ाने और नवीनतम OS अपडेट्स तक पहुंच प्राप्त करने के पात्र हैं। आप {1} को या उससे पहले प्रकाशित OS अपडेट्स का अब भी उपयोग कर सकते हैं।",
"updateOs.pleaseFinishTheInitiatedDowngradeTo": "अपडेट्स को सक्षम करने के लिए कृपया शुरू की गई डाउनग्रेड को पूरा करें।",
"updateOs.rawChangelogRenderer.errorParsingChangelog": "चेंज लॉग को पार्स करने में त्रुटि • {0}",
"updateOs.rawChangelogRenderer.itSHighlyRecommendedToReview": "अपडेट जारी रखने से पहले चेंज लॉग की समीक्षा करना अत्यधिक अनुशंसित है",
"updateOs.rawChangelogRenderer.loadingChangelog": "चेंज लॉग लोड हो रहा है...",
"updateOs.rawChangelogRenderer.noChangelogContentAvailable": "कोई चेंज लॉग सामग्री उपलब्ध नहीं",
"updateOs.rawChangelogRenderer.viewChangelogOnDocs": "डॉक्स में चेंज लॉग देखें",
"updateOs.reboot.downgrade": "डाउनग्रेड के लिए रिबूट की आवश्यकता",
"updateOs.reboot.thirdPartyDriversDownloading": "3rd पार्टी ड्राइवर्स अपडेट कर रहे हैं",
"updateOs.reboot.update": "अपडेट के लिए रिबूट की आवश्यकता",
"updateOs.status.cancel": "रद्द करें {0}",
"updateOs.status.checking": "जाँच हो रही है...",
"updateOs.status.downgrade": "डाउनग्रेड",
@@ -594,7 +595,15 @@
"userProfile.trial.startingYourFreeDayTrial": "आपकी मुफ्त 30 दिन की प्रयोग अवधि शुरू हो रही है",
"userProfile.trial.trialKeyCreated": "प्रयोग कुंजी बनाई गई",
"userProfile.trial.trialKeyCreationFailed": "प्रयोग कुंजी बनाना विफल हुआ",
"userProfile.uptimeExpire.expired": "अवधि समाप्त {0}",
"userProfile.uptimeExpire.expiredAt": "अवधि समाप्त हुई {0} पर",
"userProfile.uptimeExpire.expiresAt": "अवधि समाप्त होगी {0} पर",
"userProfile.uptimeExpire.expiresIn": "अवधि समाप्त होगी {0} में",
"userProfile.uptimeExpire.serverUpSince": "सर्वर ऑन है {0} से",
"userProfile.uptimeExpire.trialKeyExpired": "परीक्षण कुंजी समाप्त हुई {0}",
"userProfile.uptimeExpire.trialKeyExpiredAt": "परीक्षण कुंजी समाप्त हुई {0} पर",
"userProfile.uptimeExpire.trialKeyExpiresAt": "परीक्षण कुंजी समाप्त होगी {0} पर",
"userProfile.uptimeExpire.trialKeyExpiresIn": "परीक्षण कुंजी समाप्त होगा {0} में",
"userProfile.uptimeExpire.uptime": "उप समय {0}",
"wanIpCheck.checkingWanIps": "WAN IPs की जाँच कर रहे हैं...",
"wanIpCheck.dnsIssueUnableToResolveWanip4": "DNS समस्या, wanip4.unraid.net को हल करने में असमर्थ",

View File

@@ -55,13 +55,6 @@
"connectSettings.updatedApiSettingsToast": "Ažurirane API postavke",
"downgradeOs.downgradeUnraidOs": "Nadogradite Unraid OS",
"downgradeOs.pleaseFinishTheInitiatedUpdateTo": "Završite započeto ažuriranje kako biste omogućili nadogradnju.",
"downloadApiLogs.downloadUnraidApiLogs": "Preuzmite unraid-api dnevnike",
"downloadApiLogs.ifYouAreAskedToSupply": "Ako se od vas traži da dostavite dnevnike, otvorite zahtjev za podršku na našoj Kontakt stranici i odgovorite na e-poruku koju primite sa svojim dnevnicima u privitku.",
"downloadApiLogs.theLogsMayContainSensitiveInformation": "Dnevnici mogu sadržavati osjetljive informacije pa ih nemojte objavljivati javno.",
"downloadApiLogs.thePrimaryMethodOfSupportFor": "Primarna metoda podrške za Unraid Connect je kroz naše forume i Discord.",
"downloadApiLogs.unraidConnectForums": "Unraid Connect Forumi",
"downloadApiLogs.unraidContactPage": "Unraid Kontakt stranica",
"downloadApiLogs.unraidDiscord": "Unraid Discord",
"headerOsVersion.apiVersionCopiedToClipboard": "API verzija kopirana u međuspremnik",
"headerOsVersion.osVersionCopiedToClipboard": "OS verzija kopirana u međuspremnik",
"headerOsVersion.unraidApi": "Unraid API",
@@ -473,12 +466,20 @@
"updateOs.downgrade.releaseNotes": "{0} Napomene izdanja",
"updateOs.ignoredRelease.remove": "Ukloni",
"updateOs.ignoredRelease.removeFromIgnoreList": "Ukloni s liste za ignoriranje",
"updateOs.ineligible.guidRequired": "Potrebna je valjana GUID za provjeru OS ažuriranja.",
"updateOs.ineligible.keyfileRequired": "Potrebna je valjana ključna datoteka za provjeru OS ažuriranja.",
"updateOs.ineligible.osVersionRequired": "Potrebna je valjana verzija OS-a za provjeru OS ažuriranja.",
"updateOs.ineligible.updatesExpired": "Vaša {0} licenca uključivala je jednogodišnje besplatne ažuriranja u trenutku kupnje. Sada možete produžiti licencu i pristupiti najnovijim ažuriranjima OS-a.",
"updateOs.ineligible.updatesExpiredWithAvailable": "Vaša {0} licenca uključivala je godinu dana besplatnih ažuriranja u trenutku kupnje. Sada možete produžiti licencu i pristupiti najnovijim ažuriranjima OS-a. Još uvijek imate pravo na pristup OS ažuriranjima objavljenim na ili prije {1}.",
"updateOs.pleaseFinishTheInitiatedDowngradeTo": "Završite započeto vraćanje starije verzije da omogućite ažuriranja.",
"updateOs.rawChangelogRenderer.errorParsingChangelog": "Greška pri parsiranju promjena • {0}",
"updateOs.rawChangelogRenderer.itSHighlyRecommendedToReview": "Preporučuje se pregled promjena prije nastavka ažuriranja.",
"updateOs.rawChangelogRenderer.loadingChangelog": "Učitavanje promjena...",
"updateOs.rawChangelogRenderer.noChangelogContentAvailable": "Nema dostupnog sadržaja promjena",
"updateOs.rawChangelogRenderer.viewChangelogOnDocs": "Pogledaj promjene u dokumentaciji",
"updateOs.reboot.downgrade": "Za vraćanje starije verzije potreban je ponovni pokret",
"updateOs.reboot.thirdPartyDriversDownloading": "Ažuriranje upravljačkih programa treće strane",
"updateOs.reboot.update": "Za ažuriranje potreban je ponovni pokret",
"updateOs.status.cancel": "Otkaži {0}",
"updateOs.status.checking": "Provjeravanje...",
"updateOs.status.downgrade": "Vraćanje starije verzije",
@@ -594,7 +595,15 @@
"userProfile.trial.startingYourFreeDayTrial": "Pokretanje vašeg besplatnog 30-dnevnog probnog perioda",
"userProfile.trial.trialKeyCreated": "Probni ključ kreiran",
"userProfile.trial.trialKeyCreationFailed": "Kreiranje probnog ključa nije uspjelo",
"userProfile.uptimeExpire.expired": "Istekla {0}",
"userProfile.uptimeExpire.expiredAt": "Isteklo na {0}",
"userProfile.uptimeExpire.expiresAt": "Istječe na {0}",
"userProfile.uptimeExpire.expiresIn": "Istječe u {0}",
"userProfile.uptimeExpire.serverUpSince": "Server u radu od {0}",
"userProfile.uptimeExpire.trialKeyExpired": "Probni ključ istekao {0}",
"userProfile.uptimeExpire.trialKeyExpiredAt": "Probni ključ istekao na {0}",
"userProfile.uptimeExpire.trialKeyExpiresAt": "Probni ključ istječe na {0}",
"userProfile.uptimeExpire.trialKeyExpiresIn": "Probni ključ istječe u {0}",
"userProfile.uptimeExpire.uptime": "Uptime {0}",
"wanIpCheck.checkingWanIps": "Provjeravanje WAN IPs…",
"wanIpCheck.dnsIssueUnableToResolveWanip4": "DNS problem, nemoguće riješiti wanip4.unraid.net",

View File

@@ -55,13 +55,6 @@
"connectSettings.updatedApiSettingsToast": "API beállítások frissítve",
"downgradeOs.downgradeUnraidOs": "Unraid OS leminősítése",
"downgradeOs.pleaseFinishTheInitiatedUpdateTo": "Kérjük, fejezze be a megkezdett frissítést, hogy engedélyezze a leminősítést.",
"downloadApiLogs.downloadUnraidApiLogs": "Unraid API Naplók letöltése",
"downloadApiLogs.ifYouAreAskedToSupply": "Ha naplók biztosítására kérik, kérjük, nyisson meg egy támogatási kérelmet Kapcsolat oldalon, és válaszoljon a kapott e-mail üzenetre a naplók csatolásával.",
"downloadApiLogs.theLogsMayContainSensitiveInformation": "A naplók érzékeny információkat tartalmazhatnak, ezért ne tegye közzé őket nyilvánosan.",
"downloadApiLogs.thePrimaryMethodOfSupportFor": "Az Unraid Connect elsődleges támogatási módszere fórumokon és Discordon keresztül érhető el.",
"downloadApiLogs.unraidConnectForums": "Unraid Connect Fórumok",
"downloadApiLogs.unraidContactPage": "Unraid Kapcsolat oldal",
"downloadApiLogs.unraidDiscord": "Unraid Discord",
"headerOsVersion.apiVersionCopiedToClipboard": "API verzió másolva a vágólapra",
"headerOsVersion.osVersionCopiedToClipboard": "OS verzió másolva a vágólapra",
"headerOsVersion.unraidApi": "Unraid API",
@@ -473,12 +466,20 @@
"updateOs.downgrade.releaseNotes": "{0} Kiadási Megjegyzések",
"updateOs.ignoredRelease.remove": "Eltávolítás",
"updateOs.ignoredRelease.removeFromIgnoreList": "Eltávolítása a figyelmen kívül hagyott listáról",
"updateOs.ineligible.guidRequired": "Egy érvényes GUID szükséges az OS frissítések ellenőrzéséhez.",
"updateOs.ineligible.keyfileRequired": "Egy érvényes kulcsfájl szükséges az OS frissítések ellenőrzéséhez.",
"updateOs.ineligible.osVersionRequired": "Egy érvényes OS verzió szükséges az OS frissítések ellenőrzéséhez.",
"updateOs.ineligible.updatesExpired": "Az Ön {0} licencénél egy év ingyenes frissítés volt biztosított a vásárláskor. Most meghosszabbíthatja licencét és hozzáférhet a legújabb OS frissítésekhez.",
"updateOs.ineligible.updatesExpiredWithAvailable": "Az ön {0} licencéhez egy év ingyenes frissítés járt a vásárlás idején. Most jogosult a licencének meghosszabbítására és a legújabb OS frissítések elérésére. Még mindig jogosult az {1}-én vagy azelőtt közzétett OS frissítések elérésére.",
"updateOs.pleaseFinishTheInitiatedDowngradeTo": "Kérlek, fejezd be a megkezdett visszaállítást a frissítések engedélyezése érdekében.",
"updateOs.rawChangelogRenderer.errorParsingChangelog": "Hiba a változásnapló elemzése során • {0}",
"updateOs.rawChangelogRenderer.itSHighlyRecommendedToReview": "Erősen ajánlott a változásnapló átnézése, mielőtt folytatná a frissítést.",
"updateOs.rawChangelogRenderer.loadingChangelog": "Változásnapló betöltése...",
"updateOs.rawChangelogRenderer.noChangelogContentAvailable": "Nincs elérhető változásnapló tartalom",
"updateOs.rawChangelogRenderer.viewChangelogOnDocs": "Változásnapló megtekintése a dokumentációban",
"updateOs.reboot.downgrade": "Újraindítás szükséges a visszaállításhoz.",
"updateOs.reboot.thirdPartyDriversDownloading": "Harmadik féltől származó illesztőprogramok frissítése",
"updateOs.reboot.update": "Újraindítás szükséges a frissítéshez.",
"updateOs.status.cancel": "Mégse {0}",
"updateOs.status.checking": "Ellenőrzés…",
"updateOs.status.downgrade": "Visszaállítás",
@@ -594,7 +595,15 @@
"userProfile.trial.startingYourFreeDayTrial": "Az ingyenes 30 napos próba elindul.",
"userProfile.trial.trialKeyCreated": "Próba kulcs létrehozva",
"userProfile.trial.trialKeyCreationFailed": "Próba kulcs létrehozása sikertelen.",
"userProfile.uptimeExpire.expired": "Lejárt {0}",
"userProfile.uptimeExpire.expiredAt": "Lejárt ekkor: {0}",
"userProfile.uptimeExpire.expiresAt": "Lejár ekkor: {0}",
"userProfile.uptimeExpire.expiresIn": "Lejár {0} múlva",
"userProfile.uptimeExpire.serverUpSince": "Szerver fut {0} óta",
"userProfile.uptimeExpire.trialKeyExpired": "Próba kulcs lejárt {0}",
"userProfile.uptimeExpire.trialKeyExpiredAt": "Próba kulcs lejárt ekkor: {0}",
"userProfile.uptimeExpire.trialKeyExpiresAt": "Próba kulcs lejár ekkor: {0}",
"userProfile.uptimeExpire.trialKeyExpiresIn": "Próba kulcs lejár {0} múlva",
"userProfile.uptimeExpire.uptime": "Üzemidő {0}",
"wanIpCheck.checkingWanIps": "WAN IP-k ellenőrzése…",
"wanIpCheck.dnsIssueUnableToResolveWanip4": "DNS probléma, nem sikerült feloldani a wanip4.unraid.net címet.",

View File

@@ -55,13 +55,6 @@
"connectSettings.updatedApiSettingsToast": "Impostazioni API aggiornate",
"downgradeOs.downgradeUnraidOs": "Downgrade Unraid OS",
"downgradeOs.pleaseFinishTheInitiatedUpdateTo": "Completa l'aggiornamento avviato per abilitare un downgrade.",
"downloadApiLogs.downloadUnraidApiLogs": "Scarica registri unraid-api",
"downloadApiLogs.ifYouAreAskedToSupply": "Se ti viene chiesto di fornire i registri, apri una richiesta di supporto sulla nostra pagina di contatto e rispondi al messaggio email che ricevi con i tuoi registri allegati.",
"downloadApiLogs.theLogsMayContainSensitiveInformation": "I registri possono contenere informazioni sensibili pertanto non pubblicarli pubblicamente.",
"downloadApiLogs.thePrimaryMethodOfSupportFor": "Il metodo principale di supporto per Unraid Connect è attraverso i nostri forum e Discord.",
"downloadApiLogs.unraidConnectForums": "Forum di Unraid Connect",
"downloadApiLogs.unraidContactPage": "Pagina Contatti Unraid",
"downloadApiLogs.unraidDiscord": "Discord di Unraid",
"headerOsVersion.apiVersionCopiedToClipboard": "Versione API copiata negli appunti",
"headerOsVersion.osVersionCopiedToClipboard": "Versione OS copiata negli appunti",
"headerOsVersion.unraidApi": "API di Unraid",
@@ -473,12 +466,20 @@
"updateOs.downgrade.releaseNotes": "Note di rilascio {0}",
"updateOs.ignoredRelease.remove": "Rimuovi",
"updateOs.ignoredRelease.removeFromIgnoreList": "Rimuovi dalla lista ignorati",
"updateOs.ineligible.guidRequired": "È necessario un GUID valido per verificare gli aggiornamenti del sistema operativo.",
"updateOs.ineligible.keyfileRequired": "È necessario un file chiave valido per verificare gli aggiornamenti del sistema operativo.",
"updateOs.ineligible.osVersionRequired": "È necessaria una versione del sistema operativo valida per verificare gli aggiornamenti del sistema operativo.",
"updateOs.ineligible.updatesExpired": "La tua licenza {0} includeva un anno di aggiornamenti gratuiti al momento dell'acquisto. Ora sei idoneo ad estendere la tua licenza e accedere agli ultimi aggiornamenti del sistema operativo.",
"updateOs.ineligible.updatesExpiredWithAvailable": "La tua licenza {0} includeva un anno di aggiornamenti gratuiti al momento dell\\'acquisto. Ora sei idoneo per estendere la tua licenza e accedere agli ultimi aggiornamenti del sistema operativo. Sei ancora idoneo per accedere agli aggiornamenti del sistema operativo pubblicati il o prima del {1}.",
"updateOs.pleaseFinishTheInitiatedDowngradeTo": "Completa il downgrade avviato per abilitare gli aggiornamenti.",
"updateOs.rawChangelogRenderer.errorParsingChangelog": "Errore durante l'analisi del changelog • {0}",
"updateOs.rawChangelogRenderer.itSHighlyRecommendedToReview": "È altamente raccomandato controllare il changelog prima di continuare l'aggiornamento",
"updateOs.rawChangelogRenderer.loadingChangelog": "Caricamento del changelog in corso...",
"updateOs.rawChangelogRenderer.noChangelogContentAvailable": "Nessun contenuto changelog disponibile",
"updateOs.rawChangelogRenderer.viewChangelogOnDocs": "Visualizza il changelog sui documenti",
"updateOs.reboot.downgrade": "Riavvio richiesto per downgrade",
"updateOs.reboot.thirdPartyDriversDownloading": "Aggiornamento dei driver di terze parti",
"updateOs.reboot.update": "Riavvio richiesto per aggiornamento",
"updateOs.status.cancel": "Annulla {0}",
"updateOs.status.checking": "Controllo in corso...",
"updateOs.status.downgrade": "Downgrade",
@@ -594,7 +595,15 @@
"userProfile.trial.startingYourFreeDayTrial": "Inizia la tua prova gratuita di 30 giorni",
"userProfile.trial.trialKeyCreated": "Chiave di Prova Creata",
"userProfile.trial.trialKeyCreationFailed": "Creazione della Chiave di Prova Fallita",
"userProfile.uptimeExpire.expired": "Scaduto {0}",
"userProfile.uptimeExpire.expiredAt": "Scaduto il {0}",
"userProfile.uptimeExpire.expiresAt": "Scade il {0}",
"userProfile.uptimeExpire.expiresIn": "Scade tra {0}",
"userProfile.uptimeExpire.serverUpSince": "Server attivo dal {0}",
"userProfile.uptimeExpire.trialKeyExpired": "Chiave di prova scaduta {0}",
"userProfile.uptimeExpire.trialKeyExpiredAt": "Chiave di prova scaduta il {0}",
"userProfile.uptimeExpire.trialKeyExpiresAt": "La chiave di prova scade il {0}",
"userProfile.uptimeExpire.trialKeyExpiresIn": "Chiave di prova scade tra {0}",
"userProfile.uptimeExpire.uptime": "Tempo di attività {0}",
"wanIpCheck.checkingWanIps": "Controllo degli IP WAN in corso…",
"wanIpCheck.dnsIssueUnableToResolveWanip4": "Problema DNS, impossibile risolvere wanip4.unraid.net",

View File

@@ -55,13 +55,6 @@
"connectSettings.updatedApiSettingsToast": "API設定を更新しました",
"downgradeOs.downgradeUnraidOs": "Unraid OSのダウングレード",
"downgradeOs.pleaseFinishTheInitiatedUpdateTo": "ダウングレードを有効にするには、開始されたアップデートを完了してください。",
"downloadApiLogs.downloadUnraidApiLogs": "unraid-apiログをダウンロード",
"downloadApiLogs.ifYouAreAskedToSupply": "ログの提供を求められた場合は、サポートリクエストを弊社のコンタクトページで開き、受信したメールメッセージにログを添付して返信してください。",
"downloadApiLogs.theLogsMayContainSensitiveInformation": "ログには機密情報が含まれている可能性があるため、公開しないでください。",
"downloadApiLogs.thePrimaryMethodOfSupportFor": "Unraid Connectのサポートの主な方法はフォーラムとDiscordです。",
"downloadApiLogs.unraidConnectForums": "Unraid Connectフォーラム",
"downloadApiLogs.unraidContactPage": "Unraidコンタクトページ",
"downloadApiLogs.unraidDiscord": "Unraid Discord",
"headerOsVersion.apiVersionCopiedToClipboard": "APIバージョンがクリップボードにコピーされました",
"headerOsVersion.osVersionCopiedToClipboard": "OSバージョンがクリップボードにコピーされました",
"headerOsVersion.unraidApi": "Unraid API",
@@ -473,12 +466,20 @@
"updateOs.downgrade.releaseNotes": "{0}のリリースノート\n",
"updateOs.ignoredRelease.remove": "削除",
"updateOs.ignoredRelease.removeFromIgnoreList": "無視リストから削除する\n",
"updateOs.ineligible.guidRequired": "OSアップデートを確認するには、有効なGUIDが必要です。",
"updateOs.ineligible.keyfileRequired": "OSアップデートを確認するには、有効なキーファイルが必要です。",
"updateOs.ineligible.osVersionRequired": "OSアップデートを確認するには、有効なOSバージョンが必要です。",
"updateOs.ineligible.updatesExpired": "{0}ライセンスには購入時に1年間の無料アップデートが含まれていました。ライセンスを延長して最新のOSアップデートにアクセスする資格が今あります。",
"updateOs.ineligible.updatesExpiredWithAvailable": "ご購入時に{0}ライセンスには1年間の無料アップデートが含まれていました。現在、ライセンスを延長し、最新のOSアップデートにアクセスする資格があります。ただし、{1}以前またはその日に公開されたOSアップデートへのアクセスはまだ可能です。",
"updateOs.pleaseFinishTheInitiatedDowngradeTo": "初期のダウングレードを完了して更新を有効にしてください。",
"updateOs.rawChangelogRenderer.errorParsingChangelog": "変更履歴の解析エラー • {0}",
"updateOs.rawChangelogRenderer.itSHighlyRecommendedToReview": "更新の前に変更履歴の確認を強くお勧めします",
"updateOs.rawChangelogRenderer.loadingChangelog": "変更履歴を読み込み中...",
"updateOs.rawChangelogRenderer.noChangelogContentAvailable": "変更履歴の内容がありません",
"updateOs.rawChangelogRenderer.viewChangelogOnDocs": "Docsで変更履歴を見る",
"updateOs.reboot.downgrade": "ダウングレードに再起動が必要",
"updateOs.reboot.thirdPartyDriversDownloading": "サードパーティドライバの更新",
"updateOs.reboot.update": "更新に再起動が必要",
"updateOs.status.cancel": "キャンセル {0}",
"updateOs.status.checking": "確認中...",
"updateOs.status.downgrade": "ダウングレード",
@@ -594,7 +595,15 @@
"userProfile.trial.startingYourFreeDayTrial": "30日間の無料試用を開始",
"userProfile.trial.trialKeyCreated": "試用版キーが作成されました",
"userProfile.trial.trialKeyCreationFailed": "試用版キーの作成に失敗しました",
"userProfile.uptimeExpire.expired": "期限切れ{0}",
"userProfile.uptimeExpire.expiredAt": "{0}に期限切れ",
"userProfile.uptimeExpire.expiresAt": "{0}に期限が切れます",
"userProfile.uptimeExpire.expiresIn": "{0}後に期限が切れます",
"userProfile.uptimeExpire.serverUpSince": "サーバー稼働時間 {0}",
"userProfile.uptimeExpire.trialKeyExpired": "試用キーの期限切れ{0}",
"userProfile.uptimeExpire.trialKeyExpiredAt": "{0}に試用キーが期限切れ",
"userProfile.uptimeExpire.trialKeyExpiresAt": "{0}に試用キーの期限が切れます",
"userProfile.uptimeExpire.trialKeyExpiresIn": "{0}後に試用キーの期限が切れます",
"userProfile.uptimeExpire.uptime": "稼働時間 {0}",
"wanIpCheck.checkingWanIps": "WAN IPを確認中…",
"wanIpCheck.dnsIssueUnableToResolveWanip4": "DNSの問題、wanip4.unraid.netを解決できません",

View File

@@ -55,13 +55,6 @@
"connectSettings.updatedApiSettingsToast": "API 설정 업데이트됨",
"downgradeOs.downgradeUnraidOs": "Unraid OS 다운그레이드",
"downgradeOs.pleaseFinishTheInitiatedUpdateTo": "다운그레이드를 활성화하려면 시작된 업데이트를 마치십시오.",
"downloadApiLogs.downloadUnraidApiLogs": "unraid-api 로그 다운로드",
"downloadApiLogs.ifYouAreAskedToSupply": "로그 제공이 요청된 경우, 문의 페이지에서 지원 요청을 열고 받은 이메일 메시지에 회신하여 로그를 첨부하세요.",
"downloadApiLogs.theLogsMayContainSensitiveInformation": "로그에는 민감한 정보가 포함될 수 있으므로 공개적으로 게시하지 마십시오.",
"downloadApiLogs.thePrimaryMethodOfSupportFor": "Unraid Connect의 기본 지원 방법은 포럼과 디스코드를 통해 이루어집니다.",
"downloadApiLogs.unraidConnectForums": "Unraid Connect 포럼",
"downloadApiLogs.unraidContactPage": "Unraid 연락 페이지",
"downloadApiLogs.unraidDiscord": "Unraid 디스코드",
"headerOsVersion.apiVersionCopiedToClipboard": "API 버전이 클립보드에 복사됨",
"headerOsVersion.osVersionCopiedToClipboard": "OS 버전이 클립보드에 복사됨",
"headerOsVersion.unraidApi": "Unraid API",
@@ -473,12 +466,20 @@
"updateOs.downgrade.releaseNotes": "{0} 릴리스 노트",
"updateOs.ignoredRelease.remove": "제거",
"updateOs.ignoredRelease.removeFromIgnoreList": "무시 목록에서 제거",
"updateOs.ineligible.guidRequired": "OS 업데이트를 확인하려면 유효한 GUID가 필요합니다.",
"updateOs.ineligible.keyfileRequired": "OS 업데이트를 확인하려면 유효한 키 파일이 필요합니다.",
"updateOs.ineligible.osVersionRequired": "OS 업데이트를 확인하려면 유효한 OS 버전이 필요합니다.",
"updateOs.ineligible.updatesExpired": "구매 당시 {0} 라이선스에는 1년간 무료 업데이트가 포함되어 있었습니다. 이제 라이선스를 연장하고 최신 OS 업데이트에 액세스 할 수 있습니다.",
"updateOs.ineligible.updatesExpiredWithAvailable": "귀하의 {0} 라이선스는 구매 시 1년 동안 무료 업데이트가 포함되어 있었습니다. 이제 라이선스를 연장하고 최신 OS 업데이트에 액세스할 수 있습니다. 여전히 {1} 이전 또는 새로운 OS 업데이트에 액세스할 수 있습니다.",
"updateOs.pleaseFinishTheInitiatedDowngradeTo": "업데이트를 활성화하려면 시작된 다운그레이드를 완료하세요.",
"updateOs.rawChangelogRenderer.errorParsingChangelog": "변경 로그 구문 분석 오류 • {0}",
"updateOs.rawChangelogRenderer.itSHighlyRecommendedToReview": "업데이트를 진행하기 전에 변경 로그를 검토하는 것이 강력히 권장됩니다.",
"updateOs.rawChangelogRenderer.loadingChangelog": "변경 로그 로딩 중...",
"updateOs.rawChangelogRenderer.noChangelogContentAvailable": "사용 가능한 변경 로그 내용 없음",
"updateOs.rawChangelogRenderer.viewChangelogOnDocs": "문서에서 변경 로그 보기",
"updateOs.reboot.downgrade": "다운그레이드에는 재부팅이 필요합니다.",
"updateOs.reboot.thirdPartyDriversDownloading": "타사 드라이버 업데이트 중",
"updateOs.reboot.update": "업데이트에는 재부팅이 필요합니다.",
"updateOs.status.cancel": "{0} 취소",
"updateOs.status.checking": "확인 중...",
"updateOs.status.downgrade": "다운그레이드",
@@ -594,7 +595,15 @@
"userProfile.trial.startingYourFreeDayTrial": "무료 30일 트라이얼 시작 중",
"userProfile.trial.trialKeyCreated": "트라이얼 키 생성됨",
"userProfile.trial.trialKeyCreationFailed": "트라이얼 키 생성 실패",
"userProfile.uptimeExpire.expired": "만료된 {0}",
"userProfile.uptimeExpire.expiredAt": "{0}에 만료됨",
"userProfile.uptimeExpire.expiresAt": "{0}에 만료됩니다",
"userProfile.uptimeExpire.expiresIn": "{0} 후 만료됩니다",
"userProfile.uptimeExpire.serverUpSince": "서버작동시점 {0}",
"userProfile.uptimeExpire.trialKeyExpired": "시험 키 만료 {0}",
"userProfile.uptimeExpire.trialKeyExpiredAt": "{0}에 시험 키 만료됨",
"userProfile.uptimeExpire.trialKeyExpiresAt": "{0}에 시험 키 만료 예정",
"userProfile.uptimeExpire.trialKeyExpiresIn": "{0} 후 시험 키 만료 예정",
"userProfile.uptimeExpire.uptime": "업타임 {0}",
"wanIpCheck.checkingWanIps": "WAN IPs 확인 중...",
"wanIpCheck.dnsIssueUnableToResolveWanip4": "DNS 문제, wanip4.unraid.net을 해결할 수 없습니다.",

View File

@@ -55,13 +55,6 @@
"connectSettings.updatedApiSettingsToast": "Atjaunināta API iestatījumi",
"downgradeOs.downgradeUnraidOs": "Pazemināt Unraid OS versiju",
"downgradeOs.pleaseFinishTheInitiatedUpdateTo": "Lūdzu, pabeidziet ierosināto atjauninājumu, lai aktivizētu pazemināšanu.",
"downloadApiLogs.downloadUnraidApiLogs": "Lejupielādēt unraid-api žurnālus",
"downloadApiLogs.ifYouAreAskedToSupply": "Ja jums tiek lūgts iesniegt žurnālus, lūdzu, atveriet atbalsta pieprasījumu mūsu kontaktu lapā un atbildiet uz e-pasta ziņojumu ar pievienotiem žurnāliem.",
"downloadApiLogs.theLogsMayContainSensitiveInformation": "Žurnāli var saturēt sensitīvu informāciju, tāpēc nelieciet tos publiski.",
"downloadApiLogs.thePrimaryMethodOfSupportFor": "Unraid Connect galvenais atbalsta veids ir caur mūsu forumu un Discord.",
"downloadApiLogs.unraidConnectForums": "Unraid Connect Forumi",
"downloadApiLogs.unraidContactPage": "Unraid Kontaktlapa",
"downloadApiLogs.unraidDiscord": "Unraid Discord",
"headerOsVersion.apiVersionCopiedToClipboard": "API versija nokopēta klipīšdēlī",
"headerOsVersion.osVersionCopiedToClipboard": "OS versija nokopēta klipīšdēlī",
"headerOsVersion.unraidApi": "Unraid API",
@@ -473,12 +466,20 @@
"updateOs.downgrade.releaseNotes": "{0} Izlaišanas Piezīmes",
"updateOs.ignoredRelease.remove": "Dzēst",
"updateOs.ignoredRelease.removeFromIgnoreList": "Noņemt no ignorējamo saraksta",
"updateOs.ineligible.guidRequired": "Ir nepieciešams derīgs GUID, lai pārbaudītu OS atjauninājumus.",
"updateOs.ineligible.keyfileRequired": "Ir nepieciešams derīgs atslēgas fails, lai pārbaudītu OS atjauninājumus.",
"updateOs.ineligible.osVersionRequired": "Ir nepieciešama derīga OS versija, lai pārbaudītu OS atjauninājumus.",
"updateOs.ineligible.updatesExpired": "Jūsu {0} licence ietvēra vienu gadu bezmaksas atjauninājumu iegādes brīdī. Tagad jūs esat tiesīgs pagarināt savu licenci un piekļūt jaunākajiem OS atjauninājumiem.",
"updateOs.ineligible.updatesExpiredWithAvailable": "Jūsu {0} licence iekļāva vienu gadu bezmaksas atjauninājumu iegādes brīdī. Jūs tagad varat paplašināt savu licenci un piekļūt jaunākajiem OS atjauninājumiem. Jūs joprojām esat tiesīgs piekļūt OS atjauninājumiem, kas tika publicēti līdz {1}.",
"updateOs.pleaseFinishTheInitiatedDowngradeTo": "Lūdzu, pabeidziet uzsākto pazemināšanu, lai iespējotu atjauninājumus.",
"updateOs.rawChangelogRenderer.errorParsingChangelog": "Kļūda, analizējot izmaiņu pārskatu • {0}",
"updateOs.rawChangelogRenderer.itSHighlyRecommendedToReview": "Ļoti ieteicams izskatīt izmaiņu sarakstu pirms turpināt atjaunināšanu",
"updateOs.rawChangelogRenderer.loadingChangelog": "Ielādē izmaiņu sarakstu...",
"updateOs.rawChangelogRenderer.noChangelogContentAvailable": "Izmaiņu saturs nav pieejams",
"updateOs.rawChangelogRenderer.viewChangelogOnDocs": "Skatīt izmaiņu sarakstu dokumentos",
"updateOs.reboot.downgrade": "Pazemināšanai nepieciešams restartēt",
"updateOs.reboot.thirdPartyDriversDownloading": "Trešās puses draiveru atjaunināšana",
"updateOs.reboot.update": "Atjaunināšanai nepieciešams restartēt",
"updateOs.status.cancel": "Atcelt {0}",
"updateOs.status.checking": "Pārbauda...",
"updateOs.status.downgrade": "Pazemināt versiju",
@@ -594,7 +595,15 @@
"userProfile.trial.startingYourFreeDayTrial": "Sākas jūsu bezmaksas 30 dienu izmēģinājums",
"userProfile.trial.trialKeyCreated": "Izmēģinājuma Atslēga Izveidota",
"userProfile.trial.trialKeyCreationFailed": "Izmēģinājuma Atslēgas Izveide Neizdevās",
"userProfile.uptimeExpire.expired": "Beidzies {0}",
"userProfile.uptimeExpire.expiredAt": "Beidzies {0}",
"userProfile.uptimeExpire.expiresAt": "Beidzas {0}",
"userProfile.uptimeExpire.expiresIn": "Beidzas pēc {0}",
"userProfile.uptimeExpire.serverUpSince": "Serveris Darbojas Kopš {0}",
"userProfile.uptimeExpire.trialKeyExpired": "Izmēģinājuma atslēga {0} beigusies",
"userProfile.uptimeExpire.trialKeyExpiredAt": "Izmēģinājuma atslēga beigusies {0}",
"userProfile.uptimeExpire.trialKeyExpiresAt": "Izmēģinājuma atslēga beidzas {0}",
"userProfile.uptimeExpire.trialKeyExpiresIn": "Izmēģinājuma atslēga beidzas pēc {0}",
"userProfile.uptimeExpire.uptime": "Uptime {0}",
"wanIpCheck.checkingWanIps": "Pārbauda WAN IPs…",
"wanIpCheck.dnsIssueUnableToResolveWanip4": "DNS problēma, nevar atrisināt wanip4.unraid.net",

View File

@@ -55,13 +55,6 @@
"connectSettings.updatedApiSettingsToast": "Bijgewerkte API-instellingen",
"downgradeOs.downgradeUnraidOs": "Downgrade Unraid OS",
"downgradeOs.pleaseFinishTheInitiatedUpdateTo": "Voltooi de gestarte update om een downgrading mogelijk te maken.",
"downloadApiLogs.downloadUnraidApiLogs": "Download unraid-api logs",
"downloadApiLogs.ifYouAreAskedToSupply": "Als u wordt gevraagd logs te verstrekken, open dan een ondersteuningsverzoek op onze contactpagina en beantwoord het e-mailbericht dat u ontvangt met uw logs als bijlage.",
"downloadApiLogs.theLogsMayContainSensitiveInformation": "De logs kunnen gevoelige informatie bevatten, dus post ze niet openbaar.",
"downloadApiLogs.thePrimaryMethodOfSupportFor": "De primaire ondersteuningsmethode voor Unraid Connect is via onze forums en Discord.",
"downloadApiLogs.unraidConnectForums": "Unraid Connect Forums",
"downloadApiLogs.unraidContactPage": "Unraid Contactpagina",
"downloadApiLogs.unraidDiscord": "Unraid Discord",
"headerOsVersion.apiVersionCopiedToClipboard": "API-versie gekopieerd naar klembord",
"headerOsVersion.osVersionCopiedToClipboard": "OS-versie gekopieerd naar klembord",
"headerOsVersion.unraidApi": "Unraid API",
@@ -473,12 +466,20 @@
"updateOs.downgrade.releaseNotes": "{0} Release Notities",
"updateOs.ignoredRelease.remove": "Verwijderen",
"updateOs.ignoredRelease.removeFromIgnoreList": "Verwijderen van negeren lijst",
"updateOs.ineligible.guidRequired": "Een geldige GUID is vereist om te controleren op OS-updates.",
"updateOs.ineligible.keyfileRequired": "Een geldig sleutelbestand is vereist om te controleren op OS-updates.",
"updateOs.ineligible.osVersionRequired": "Een geldige OS-versie is vereist om te controleren op OS-updates.",
"updateOs.ineligible.updatesExpired": "Uw {0} licentie bevatte een jaar gratis updates op het moment van aankoop. U komt nu in aanmerking om uw licentie uit te breiden en toegang te krijgen tot de nieuwste OS-updates.",
"updateOs.ineligible.updatesExpiredWithAvailable": "Uw {0} licentie bevatte één jaar gratis updates op het moment van aankoop. U komt nu in aanmerking om uw licentie te verlengen en toegang te krijgen tot de nieuwste OS-updates. U bent nog steeds in aanmerking om toegang te krijgen tot OS-updates die zijn gepubliceerd op of voor {1}.",
"updateOs.pleaseFinishTheInitiatedDowngradeTo": "Beëindig de geïnitieerde downgrade om updates mogelijk te maken.",
"updateOs.rawChangelogRenderer.errorParsingChangelog": "Fout bij het parseren van wijzigingslogboek • {0}",
"updateOs.rawChangelogRenderer.itSHighlyRecommendedToReview": "Het wordt ten zeerste aanbevolen om het wijzigingslogboek te bekijken voordat u doorgaat met updaten",
"updateOs.rawChangelogRenderer.loadingChangelog": "Wijzigingslogboek laden...",
"updateOs.rawChangelogRenderer.noChangelogContentAvailable": "Geen inhoud van het wijzigingslogboek beschikbaar",
"updateOs.rawChangelogRenderer.viewChangelogOnDocs": "Bekijk wijzigingslogboek in Docs",
"updateOs.reboot.downgrade": "Herstart vereist voor downgrade",
"updateOs.reboot.thirdPartyDriversDownloading": "3e partij stuurprogramma's bijwerken",
"updateOs.reboot.update": "Herstart vereist voor update",
"updateOs.status.cancel": "Annuleren {0}",
"updateOs.status.checking": "Controleren...",
"updateOs.status.downgrade": "Downgrade",
@@ -594,7 +595,15 @@
"userProfile.trial.startingYourFreeDayTrial": "Uw gratis 30-daagse proefperiode gestart",
"userProfile.trial.trialKeyCreated": "Proefsleutel aangemaakt",
"userProfile.trial.trialKeyCreationFailed": "Aanmaken van de proefsleutel mislukt",
"userProfile.uptimeExpire.expired": "Verlopen {0}",
"userProfile.uptimeExpire.expiredAt": "Verlopen op {0}",
"userProfile.uptimeExpire.expiresAt": "Verloopt op {0}",
"userProfile.uptimeExpire.expiresIn": "Verloopt over {0}",
"userProfile.uptimeExpire.serverUpSince": "Server actief sinds {0}",
"userProfile.uptimeExpire.trialKeyExpired": "Proeflijke'sleutel Verlopen {0}",
"userProfile.uptimeExpire.trialKeyExpiredAt": "Proeflijke'sleutel Verlopen op {0}",
"userProfile.uptimeExpire.trialKeyExpiresAt": "Proeflijke'sleutel Verloopt op {0}",
"userProfile.uptimeExpire.trialKeyExpiresIn": "Proeflijke'sleutel Verloopt over {0}",
"userProfile.uptimeExpire.uptime": "Actief {0}",
"wanIpCheck.checkingWanIps": "WAN IP's aan het controleren…",
"wanIpCheck.dnsIssueUnableToResolveWanip4": "DNS-probleem, niet in staat om wanip4.unraid.net op te lossen",

View File

@@ -55,13 +55,6 @@
"connectSettings.updatedApiSettingsToast": "Oppdaterte API-innstillinger",
"downgradeOs.downgradeUnraidOs": "Nedgradering av Unraid OS",
"downgradeOs.pleaseFinishTheInitiatedUpdateTo": "Vennligst fullfør den påbegynte oppdateringen for å muliggjøre en nedgradering.",
"downloadApiLogs.downloadUnraidApiLogs": "Last ned unraid-api-logg",
"downloadApiLogs.ifYouAreAskedToSupply": "Hvis du blir bedt om å levere logger, vennligst åpne en supportforespørsel på vår kontaktside og svar på e-postmeldingen du mottar med loggene dine vedlagt.",
"downloadApiLogs.theLogsMayContainSensitiveInformation": "Loggene kan inneholde sensitiv informasjon, så ikke legg dem ut offentlig.",
"downloadApiLogs.thePrimaryMethodOfSupportFor": "Den primære metoden for støtte for Unraid Connect er gjennom våre fora og Discord.",
"downloadApiLogs.unraidConnectForums": "Unraid Connect-forum",
"downloadApiLogs.unraidContactPage": "Unraid kontaktside",
"downloadApiLogs.unraidDiscord": "Unraid Discord",
"headerOsVersion.apiVersionCopiedToClipboard": "API-versjon kopiert til utklippstavlen",
"headerOsVersion.osVersionCopiedToClipboard": "OS-versjon kopiert til utklippstavlen",
"headerOsVersion.unraidApi": "Unraid API",
@@ -473,12 +466,20 @@
"updateOs.downgrade.releaseNotes": "{0} Utgivelsesnotater",
"updateOs.ignoredRelease.remove": "Fjern",
"updateOs.ignoredRelease.removeFromIgnoreList": "Fjern fra ignorelist",
"updateOs.ineligible.guidRequired": "Et gyldig GUID er nødvendig for å sjekke for OS-oppdateringer.",
"updateOs.ineligible.keyfileRequired": "En gyldig nøkkelfil er nødvendig for å sjekke for OS-oppdateringer.",
"updateOs.ineligible.osVersionRequired": "En gyldig OS-versjon er nødvendig for å sjekke for OS-oppdateringer.",
"updateOs.ineligible.updatesExpired": "Din {0} lisens inkluderte ett år med gratis oppdateringer på kjøpstidspunktet. Du er nå kvalifisert til å forlenge lisensen og få tilgang til de nyeste OS-oppdateringene.",
"updateOs.ineligible.updatesExpiredWithAvailable": "Din {0} lisens inkluderte ett års gratis oppdateringer på kjøpstidspunktet. Du er nå kvalifisert til å forlenge lisensen og få tilgang til de nyeste OS-oppdateringene. Du er fortsatt kvalifisert til å få tilgang til OS-oppdateringer som ble publisert på eller før {1}.",
"updateOs.pleaseFinishTheInitiatedDowngradeTo": "Vennligst avslutt den påbegynte nedgraderingen for å aktivere oppdateringer.",
"updateOs.rawChangelogRenderer.errorParsingChangelog": "Feil ved parsing av endringslogg • {0}",
"updateOs.rawChangelogRenderer.itSHighlyRecommendedToReview": "Det anbefales sterkt å gå gjennom endringsloggen før du fortsetter oppdateringen",
"updateOs.rawChangelogRenderer.loadingChangelog": "Laster endringslogg...",
"updateOs.rawChangelogRenderer.noChangelogContentAvailable": "Ingen tilgjengelig endringslogg",
"updateOs.rawChangelogRenderer.viewChangelogOnDocs": "Vis endringslogg på dokumenter",
"updateOs.reboot.downgrade": "Omstart kreves for nedgradering",
"updateOs.reboot.thirdPartyDriversDownloading": "Oppdaterer tredjepartsdrivere",
"updateOs.reboot.update": "Omstart kreves for oppdatering",
"updateOs.status.cancel": "Avbryt {0}",
"updateOs.status.checking": "Sjekker...",
"updateOs.status.downgrade": "Nedgrader",
@@ -594,7 +595,15 @@
"userProfile.trial.startingYourFreeDayTrial": "Starter din gratis 30-dagers rett",
"userProfile.trial.trialKeyCreated": "Prøvenøkkel opprettet",
"userProfile.trial.trialKeyCreationFailed": "Oppretting av prøvenøkkel feilet",
"userProfile.uptimeExpire.expired": "Utløpt {0}",
"userProfile.uptimeExpire.expiredAt": "Utløpt kl. {0}",
"userProfile.uptimeExpire.expiresAt": "Utløper kl. {0}",
"userProfile.uptimeExpire.expiresIn": "Utløper om {0}",
"userProfile.uptimeExpire.serverUpSince": "Server oppe siden {0}",
"userProfile.uptimeExpire.trialKeyExpired": "Prøve-nøkkel utløpt {0}",
"userProfile.uptimeExpire.trialKeyExpiredAt": "Prøve-nøkkel utløpt kl. {0}",
"userProfile.uptimeExpire.trialKeyExpiresAt": "Prøve-nøkkel utløper kl. {0}",
"userProfile.uptimeExpire.trialKeyExpiresIn": "Prøve-nøkkel utløper om {0}",
"userProfile.uptimeExpire.uptime": "Oppetid {0}",
"wanIpCheck.checkingWanIps": "Sjekker WAN IP-er…",
"wanIpCheck.dnsIssueUnableToResolveWanip4": "DNS-problem, kan ikke løse wanip4.unraid.net",

View File

@@ -55,13 +55,6 @@
"connectSettings.updatedApiSettingsToast": "Zaktualizowane ustawienia API",
"downgradeOs.downgradeUnraidOs": "Obniż wersję Unraid OS",
"downgradeOs.pleaseFinishTheInitiatedUpdateTo": "Proszę dokończyć zainicjowaną aktualizację, aby umożliwić obniżenie wersji.",
"downloadApiLogs.downloadUnraidApiLogs": "Pobierz logi unraid-api",
"downloadApiLogs.ifYouAreAskedToSupply": "Jeśli poproszono cię o dostarczenie dzienników, proszę otwórz zgłoszenie o wsparcie na naszej stronie Kontaktowej i odpowiedz na otrzymaną wiadomość e-mail, dołączając swoje dzienniki.",
"downloadApiLogs.theLogsMayContainSensitiveInformation": "Dzienniki mogą zawierać poufne informacje, więc nie publikuj ich publicznie.",
"downloadApiLogs.thePrimaryMethodOfSupportFor": "Główna metoda wsparcia dla Unraid Connect jest poprzez nasze fora i Discord.",
"downloadApiLogs.unraidConnectForums": "Fora Unraid Connect",
"downloadApiLogs.unraidContactPage": "Strona Kontaktowa Unraid",
"downloadApiLogs.unraidDiscord": "Unraid Discord",
"headerOsVersion.apiVersionCopiedToClipboard": "Wersja API skopiowana do schowka",
"headerOsVersion.osVersionCopiedToClipboard": "Wersja OS skopiowana do schowka",
"headerOsVersion.unraidApi": "Unraid API",
@@ -473,12 +466,20 @@
"updateOs.downgrade.releaseNotes": "{0} Informacje o wydaniu",
"updateOs.ignoredRelease.remove": "Usuń",
"updateOs.ignoredRelease.removeFromIgnoreList": "Usuń z listy ignorowanych",
"updateOs.ineligible.guidRequired": "Wymagany jest prawidłowy GUID, aby sprawdzić dostępność aktualizacji systemu operacyjnego.",
"updateOs.ineligible.keyfileRequired": "Wymagany jest prawidłowy plik klucza, aby sprawdzić dostępność aktualizacji systemu operacyjnego.",
"updateOs.ineligible.osVersionRequired": "Wymagana jest prawidłowa wersja systemu operacyjnego, aby sprawdzić dostępność aktualizacji systemu.",
"updateOs.ineligible.updatesExpired": "Twoja licencja {0} obejmowała jeden rok darmowych aktualizacji w momencie zakupu. Teraz możesz przedłużyć swoją licencję i uzyskać dostęp do najnowszych aktualizacji systemu operacyjnego.",
"updateOs.ineligible.updatesExpiredWithAvailable": "Twoja licencja {0} obejmowała rok bezpłatnych aktualizacji w momencie zakupu. Obecnie kwalifikujesz się do przedłużenia licencji i uzyskania dostępu do najnowszych aktualizacji systemu operacyjnego. Nadal masz dostęp do aktualizacji systemu OS opublikowanych w dniu lub przed {1}.",
"updateOs.pleaseFinishTheInitiatedDowngradeTo": "Proszę zakończyć rozpoczętą deaktualizację, aby włączyć aktualizacje.",
"updateOs.rawChangelogRenderer.errorParsingChangelog": "Błąd podczas parsowania dziennika zmian • {0}",
"updateOs.rawChangelogRenderer.itSHighlyRecommendedToReview": "Zaleca się zapoznanie z dziennikiem zmian przed kontynuowaniem aktualizacji",
"updateOs.rawChangelogRenderer.loadingChangelog": "Ładowanie dziennika zmian...",
"updateOs.rawChangelogRenderer.noChangelogContentAvailable": "Brak dostępnej treści dziennika zmian",
"updateOs.rawChangelogRenderer.viewChangelogOnDocs": "Zobacz dziennik zmian w Dokumentacji",
"updateOs.reboot.downgrade": "Wymagane ponowne uruchomienie, aby przeprowadzić deaktualizację",
"updateOs.reboot.thirdPartyDriversDownloading": "Aktualizowanie sterowników firm trzecich",
"updateOs.reboot.update": "Wymagane ponowne uruchomienie, aby zaktualizować",
"updateOs.status.cancel": "Anuluj {0}",
"updateOs.status.checking": "Sprawdzanie...",
"updateOs.status.downgrade": "Deaktualizacja",
@@ -594,7 +595,15 @@
"userProfile.trial.startingYourFreeDayTrial": "Rozpoczęcie darmowego 30-dniowego okresu próbnego",
"userProfile.trial.trialKeyCreated": "Klucz období próbnego został utworzony",
"userProfile.trial.trialKeyCreationFailed": "Tworzenie klucza próbnego nie powiodło się",
"userProfile.uptimeExpire.expired": "Wygasły {0}",
"userProfile.uptimeExpire.expiredAt": "Wygasł o {0}",
"userProfile.uptimeExpire.expiresAt": "Wygasa o {0}",
"userProfile.uptimeExpire.expiresIn": "Wygasa za {0}",
"userProfile.uptimeExpire.serverUpSince": "Serwer włączony od {0}",
"userProfile.uptimeExpire.trialKeyExpired": "Klucz próbny wygasł {0}",
"userProfile.uptimeExpire.trialKeyExpiredAt": "Klucz próbny wygasł o {0}",
"userProfile.uptimeExpire.trialKeyExpiresAt": "Klucz próbny wygasa o {0}",
"userProfile.uptimeExpire.trialKeyExpiresIn": "Klucz próbny wygasa za {0}",
"userProfile.uptimeExpire.uptime": "Czas działania {0}",
"wanIpCheck.checkingWanIps": "Sprawdzanie adresów IP WAN…",
"wanIpCheck.dnsIssueUnableToResolveWanip4": "Problem z DNS, nie można rozwiązać wanip4.unraid.net",

View File

@@ -55,13 +55,6 @@
"connectSettings.updatedApiSettingsToast": "Configurações da API Atualizadas",
"downgradeOs.downgradeUnraidOs": "Reduzir Unraid OS",
"downgradeOs.pleaseFinishTheInitiatedUpdateTo": "Por favor, termine a atualização iniciada para permitir uma redução de versão.",
"downloadApiLogs.downloadUnraidApiLogs": "Baixar logs unraid-api",
"downloadApiLogs.ifYouAreAskedToSupply": "Se você for solicitado a fornecer logs, por favor, abra uma solicitação de suporte em nossa Página de Contato e responda à mensagem de e-mail que receber com seus logs anexados.",
"downloadApiLogs.theLogsMayContainSensitiveInformation": "Os logs podem conter informações confidenciais, então não os publique publicamente.",
"downloadApiLogs.thePrimaryMethodOfSupportFor": "O método principal de suporte para Unraid Connect é através dos nossos fóruns e Discord.",
"downloadApiLogs.unraidConnectForums": "Fóruns do Unraid Connect",
"downloadApiLogs.unraidContactPage": "Página de Contato do Unraid",
"downloadApiLogs.unraidDiscord": "Unraid Discord",
"headerOsVersion.apiVersionCopiedToClipboard": "Versão da API copiada para a área de transferência",
"headerOsVersion.osVersionCopiedToClipboard": "Versão do SO copiada para a área de transferência",
"headerOsVersion.unraidApi": "API Unraid",
@@ -473,12 +466,20 @@
"updateOs.downgrade.releaseNotes": "Notas de Lançamento de {0}",
"updateOs.ignoredRelease.remove": "Remover",
"updateOs.ignoredRelease.removeFromIgnoreList": "Remover da lista de ignorados",
"updateOs.ineligible.guidRequired": "É necessário um GUID válido para verificar atualizações do Sistema Operacional.",
"updateOs.ineligible.keyfileRequired": "É necessário um arquivo de chave válido para verificar atualizações do Sistema Operacional.",
"updateOs.ineligible.osVersionRequired": "É necessária uma versão válida do Sistema Operacional para verificar atualizações.",
"updateOs.ineligible.updatesExpired": "Sua licença {0} incluía um ano de atualizações gratuitas no momento da compra. Agora você está elegível para estender sua licença e acessar as últimas atualizações do Sistema Operacional.",
"updateOs.ineligible.updatesExpiredWithAvailable": "Sua licença {0} incluía um ano de atualizações gratuitas na época da compra. Agora você está elegível para estender sua licença e acessar as últimas atualizações do sistema operacional. Você ainda pode acessar as atualizações do sistema operacional publicadas em ou antes de {1}.",
"updateOs.pleaseFinishTheInitiatedDowngradeTo": "Por favor, termine o downgrade iniciado para permitir atualizações.",
"updateOs.rawChangelogRenderer.errorParsingChangelog": "Erro ao Analisar o Log de Alterações • {0}",
"updateOs.rawChangelogRenderer.itSHighlyRecommendedToReview": "É altamente recomendado revisar o log de alterações antes de continuar sua atualização",
"updateOs.rawChangelogRenderer.loadingChangelog": "Carregando log de alterações...",
"updateOs.rawChangelogRenderer.noChangelogContentAvailable": "Sem conteúdo de log de alterações disponível",
"updateOs.rawChangelogRenderer.viewChangelogOnDocs": "Ver Log de Alterações nos Docs",
"updateOs.reboot.downgrade": "Reinicialização Necessária para Downgrade",
"updateOs.reboot.thirdPartyDriversDownloading": "Atualizando drivers de terceiros",
"updateOs.reboot.update": "Reinicialização Necessária para Atualização",
"updateOs.status.cancel": "Cancelar {0}",
"updateOs.status.checking": "verificando...",
"updateOs.status.downgrade": "Downgrade",
@@ -594,7 +595,15 @@
"userProfile.trial.startingYourFreeDayTrial": "Iniciando seu teste gratuito de 30 dias",
"userProfile.trial.trialKeyCreated": "Chave de Teste Criada",
"userProfile.trial.trialKeyCreationFailed": "Falha na Criação da Chave de Teste",
"userProfile.uptimeExpire.expired": "Expirado {0}",
"userProfile.uptimeExpire.expiredAt": "Expirado em {0}",
"userProfile.uptimeExpire.expiresAt": "Expira em {0}",
"userProfile.uptimeExpire.expiresIn": "Expira em {0}",
"userProfile.uptimeExpire.serverUpSince": "Servidor Ativo Desde {0}",
"userProfile.uptimeExpire.trialKeyExpired": "Chave de Avaliação Expirada {0}",
"userProfile.uptimeExpire.trialKeyExpiredAt": "Chave de Avaliação Expirada em {0}",
"userProfile.uptimeExpire.trialKeyExpiresAt": "Chave de Avaliação Expira em {0}",
"userProfile.uptimeExpire.trialKeyExpiresIn": "Chave de Avaliação Expira em {0}",
"userProfile.uptimeExpire.uptime": "Tempo de Atividade {0}",
"wanIpCheck.checkingWanIps": "Verificando IPs WAN…",
"wanIpCheck.dnsIssueUnableToResolveWanip4": "Problema de DNS, incapaz de resolver wanip4.unraid.net",

View File

@@ -55,13 +55,6 @@
"connectSettings.updatedApiSettingsToast": "Setări API actualizate",
"downgradeOs.downgradeUnraidOs": "Retrogradare Unraid OS",
"downgradeOs.pleaseFinishTheInitiatedUpdateTo": "Finalizați actualizarea inițiată pentru a permite o retrogradare.",
"downloadApiLogs.downloadUnraidApiLogs": "Descărcare jurnale unraid-api",
"downloadApiLogs.ifYouAreAskedToSupply": "Dacă sunteți solicitat să furnizați jurnalele, vă rugăm să deschideți o cerere de suport pe pagina noastră de contact și să răspundeți la e-mailul primit cu jurnalele atașate.",
"downloadApiLogs.theLogsMayContainSensitiveInformation": "Jurnalele pot conține informații sensibile, deci nu le publicați public.",
"downloadApiLogs.thePrimaryMethodOfSupportFor": "Principala metodă de suport pentru Unraid Connect este prin forumurile și Discord-ul nostru.",
"downloadApiLogs.unraidConnectForums": "Forumurile Unraid Connect",
"downloadApiLogs.unraidContactPage": "Pagina de Contact Unraid",
"downloadApiLogs.unraidDiscord": "Unraid Discord",
"headerOsVersion.apiVersionCopiedToClipboard": "Versiune API copiată în clipboard",
"headerOsVersion.osVersionCopiedToClipboard": "Versiunea OS copiată în clipboard",
"headerOsVersion.unraidApi": "Unraid API",
@@ -473,12 +466,20 @@
"updateOs.downgrade.releaseNotes": "{0} Notițe de Lansare",
"updateOs.ignoredRelease.remove": "Elimină",
"updateOs.ignoredRelease.removeFromIgnoreList": "Eliminați din lista de ignorare",
"updateOs.ineligible.guidRequired": "Este necesar un GUID valid pentru a verifica actualizările sistemului de operare.",
"updateOs.ineligible.keyfileRequired": "Este necesar un fișier cheie valid pentru a verifica actualizările sistemului de operare.",
"updateOs.ineligible.osVersionRequired": "Este necesară o versiune validă a sistemului de operare pentru a verifica actualizările acestuia.",
"updateOs.ineligible.updatesExpired": "Licența dumneavoastră {0} a inclus un an de actualizări gratuite la momentul achiziției. Acum sunteți eligibil să vă extindeți licența și să accesați cele mai recente actualizări ale sistemului de operare.",
"updateOs.ineligible.updatesExpiredWithAvailable": "Licența dumneavoastră {0} a inclus un an de actualizări gratuite la momentul achiziției. Acum sunteți eligibil să extindeți licența și să accesați cele mai recente actualizări ale sistemului de operare. Încă sunteți eligibil să accesați actualizările de sistem publicate la sau înainte de {1}.",
"updateOs.pleaseFinishTheInitiatedDowngradeTo": "Vă rugăm să finalizați downgrade-ul inițiat pentru a permite actualizările.",
"updateOs.rawChangelogRenderer.errorParsingChangelog": "Eroare la analizarea jurnalului de modificări • {0}",
"updateOs.rawChangelogRenderer.itSHighlyRecommendedToReview": "Este extrem de recomandat să revizuiți jurnalul de modificări înainte de a continua actualizarea",
"updateOs.rawChangelogRenderer.loadingChangelog": "Încărcare jurnal de modificări...",
"updateOs.rawChangelogRenderer.noChangelogContentAvailable": "Nu există conținut disponibil pentru jurnalul de modificări",
"updateOs.rawChangelogRenderer.viewChangelogOnDocs": "Vizualizează jurnalul de modificări pe Docs",
"updateOs.reboot.downgrade": "Reboot necesar pentru downgrade",
"updateOs.reboot.thirdPartyDriversDownloading": "Actualizarea driverelor de la terți",
"updateOs.reboot.update": "Reboot necesar pentru actualizare",
"updateOs.status.cancel": "Anulează {0}",
"updateOs.status.checking": "Verificare...",
"updateOs.status.downgrade": "Downgrade",
@@ -594,7 +595,15 @@
"userProfile.trial.startingYourFreeDayTrial": "Începerea perioadei de probă gratuită de 30 de zile",
"userProfile.trial.trialKeyCreated": "Cheie de probă creată",
"userProfile.trial.trialKeyCreationFailed": "Crearea cheii de probă a eșuat",
"userProfile.uptimeExpire.expired": "Expirat {0}",
"userProfile.uptimeExpire.expiredAt": "Expirat la {0}",
"userProfile.uptimeExpire.expiresAt": "Expiră la {0}",
"userProfile.uptimeExpire.expiresIn": "Expiră în {0}",
"userProfile.uptimeExpire.serverUpSince": "Serverul este activ de la {0}",
"userProfile.uptimeExpire.trialKeyExpired": "Cheie de încercare expirată {0}",
"userProfile.uptimeExpire.trialKeyExpiredAt": "Cheie de încercare expirată la {0}",
"userProfile.uptimeExpire.trialKeyExpiresAt": "Cheie de încercare expiră la {0}",
"userProfile.uptimeExpire.trialKeyExpiresIn": "Cheie de încercare expiră în {0}",
"userProfile.uptimeExpire.uptime": "Timp de funcționare {0}",
"wanIpCheck.checkingWanIps": "Verificare IP WAN…",
"wanIpCheck.dnsIssueUnableToResolveWanip4": "Problemă DNS, nu se poate rezolva wanip4.unraid.net",

View File

@@ -55,13 +55,6 @@
"connectSettings.updatedApiSettingsToast": "Обновлены настройки API",
"downgradeOs.downgradeUnraidOs": "Понизить версию Unraid OS",
"downgradeOs.pleaseFinishTheInitiatedUpdateTo": "Пожалуйста, завершите начатое обновление для включения понижения версии.",
"downloadApiLogs.downloadUnraidApiLogs": "Скачать журналы unraid-api",
"downloadApiLogs.ifYouAreAskedToSupply": "Если вас попросят предоставить журналы, откройте запрос на поддержку на нашей странице Контакты и ответьте на полученное по электронной почте сообщение с прикрепленными журналами.",
"downloadApiLogs.theLogsMayContainSensitiveInformation": "Журналы могут содержать конфиденциальную информацию, поэтому не размещайте их в открытом доступе.",
"downloadApiLogs.thePrimaryMethodOfSupportFor": "Основной метод поддержки для Unraid Connect — через наши форумы и Discord.",
"downloadApiLogs.unraidConnectForums": "Форумы Unraid Connect",
"downloadApiLogs.unraidContactPage": "Страница контактов Unraid",
"downloadApiLogs.unraidDiscord": "Unraid Discord",
"headerOsVersion.apiVersionCopiedToClipboard": "Версия API скопирована в буфер обмена",
"headerOsVersion.osVersionCopiedToClipboard": "Версия ОС скопирована в буфер обмена",
"headerOsVersion.unraidApi": "Unraid API",
@@ -473,12 +466,20 @@
"updateOs.downgrade.releaseNotes": "{0} Заметки к выпуску",
"updateOs.ignoredRelease.remove": "Удалить",
"updateOs.ignoredRelease.removeFromIgnoreList": "Удалить из списка игнорируемых",
"updateOs.ineligible.guidRequired": "Для проверки обновлений ОС требуется действительный GUID.",
"updateOs.ineligible.keyfileRequired": "Для проверки обновлений ОС требуется действительный ключевой файл.",
"updateOs.ineligible.osVersionRequired": "Для проверки обновлений ОС требуется действительная версия ОС.",
"updateOs.ineligible.updatesExpired": "Ваш {0} лицензия включала один год бесплатных обновлений на момент покупки. Теперь вы можете продлить лицензию и получить доступ к последним обновлениям ОС.",
"updateOs.ineligible.updatesExpiredWithAvailable": "Ваша лицензия {0} включала один год бесплатных обновлений на момент покупки. Теперь вы можете продлить лицензию и получить доступ к последним обновлениям ОС. Вы по-прежнему можете получать доступ к обновлениям ОС, которые были опубликованы до или в день {1}.",
"updateOs.pleaseFinishTheInitiatedDowngradeTo": "Пожалуйста, завершите начатый откат, чтобы включить обновления.",
"updateOs.rawChangelogRenderer.errorParsingChangelog": "Ошибка разбора списка изменений • {0}",
"updateOs.rawChangelogRenderer.itSHighlyRecommendedToReview": "Настоятельно рекомендуется просмотреть список изменений перед продолжением обновления",
"updateOs.rawChangelogRenderer.loadingChangelog": "Загрузка списка изменений...",
"updateOs.rawChangelogRenderer.noChangelogContentAvailable": "Нет доступного содержания списка изменений",
"updateOs.rawChangelogRenderer.viewChangelogOnDocs": "Просмотреть список изменений в документации",
"updateOs.reboot.downgrade": "Требуется перезагрузка для понижения версии",
"updateOs.reboot.thirdPartyDriversDownloading": "Обновление сторонних драйверов",
"updateOs.reboot.update": "Требуется перезагрузка для обновления",
"updateOs.status.cancel": "Отмена {0}",
"updateOs.status.checking": "Проверка...",
"updateOs.status.downgrade": "Откат",
@@ -594,7 +595,15 @@
"userProfile.trial.startingYourFreeDayTrial": "Начало вашего бесплатного 30-дневного пробного периода",
"userProfile.trial.trialKeyCreated": "Пробный ключ создан",
"userProfile.trial.trialKeyCreationFailed": "Ошибка создания пробного ключа",
"userProfile.uptimeExpire.expired": "Истек срок действия {0}",
"userProfile.uptimeExpire.expiredAt": "Истек срок действия в {0}",
"userProfile.uptimeExpire.expiresAt": "Срок действия истекает в {0}",
"userProfile.uptimeExpire.expiresIn": "Срок действия истекает через {0}",
"userProfile.uptimeExpire.serverUpSince": "Сервер работает с {0}",
"userProfile.uptimeExpire.trialKeyExpired": "Срок действия пробного ключа истек {0}",
"userProfile.uptimeExpire.trialKeyExpiredAt": "Срок действия пробного ключа истек в {0}",
"userProfile.uptimeExpire.trialKeyExpiresAt": "Срок действия пробного ключа истекает в {0}",
"userProfile.uptimeExpire.trialKeyExpiresIn": "Пробный ключ истекает через {0}",
"userProfile.uptimeExpire.uptime": "Время работы: {0}",
"wanIpCheck.checkingWanIps": "Проверка WAN IPs…",
"wanIpCheck.dnsIssueUnableToResolveWanip4": "Проблема с DNS, невозможно разрешить wanip4.unraid.net",

View File

@@ -55,13 +55,6 @@
"connectSettings.updatedApiSettingsToast": "Uppdaterade API-inställningar",
"downgradeOs.downgradeUnraidOs": "Nedgradera Unraid OS",
"downgradeOs.pleaseFinishTheInitiatedUpdateTo": "Avsluta den påbörjade uppdateringen för att kunna nedgradera.",
"downloadApiLogs.downloadUnraidApiLogs": "Ladda ner unraid-api-loggar",
"downloadApiLogs.ifYouAreAskedToSupply": "Om du blir ombedd att tillhandahålla loggar, öppna en supportbegäran på vår Kontaktsida och svara på e-postmeddelandet du får med dina loggar bifogade.",
"downloadApiLogs.theLogsMayContainSensitiveInformation": "Loggarna kan innehålla känslig information så lägg inte ut dem offentligt.",
"downloadApiLogs.thePrimaryMethodOfSupportFor": "Den primära metoden för support för Unraid Connect är via våra forum och Discord.",
"downloadApiLogs.unraidConnectForums": "Unraid Connect Forum",
"downloadApiLogs.unraidContactPage": "Unraid Kontakt Sida",
"downloadApiLogs.unraidDiscord": "Unraid Discord",
"headerOsVersion.apiVersionCopiedToClipboard": "API-version kopierad till urklipp",
"headerOsVersion.osVersionCopiedToClipboard": "OS-version kopierad till urklipp",
"headerOsVersion.unraidApi": "Unraid API",
@@ -473,12 +466,20 @@
"updateOs.downgrade.releaseNotes": "{0} Versionsanteckningar",
"updateOs.ignoredRelease.remove": "Ta bort",
"updateOs.ignoredRelease.removeFromIgnoreList": "Ta bort från ignoreringslista",
"updateOs.ineligible.guidRequired": "Ett giltigt GUID krävs för att söka efter OS-uppdateringar.",
"updateOs.ineligible.keyfileRequired": "En giltig nyckelfil krävs för att söka efter OS-uppdateringar.",
"updateOs.ineligible.osVersionRequired": "En giltig OS-version krävs för att söka efter OS-uppdateringar.",
"updateOs.ineligible.updatesExpired": "Din {0} licens inkluderade ett års kostnadsfria uppdateringar vid köpetillfället. Du är nu berättigad att förlänga din licens och få tillgång till de senaste OS-uppdateringarna.",
"updateOs.ineligible.updatesExpiredWithAvailable": "Din {0} licens innehöll ett års fria uppdateringar vid köptillfället. Du är nu berättigad att förlänga din licens och få tillgång till de senaste OS-uppdateringarna. Du har fortfarande rätt att få tillgång till OS-uppdateringar som publicerades på eller före {1}.",
"updateOs.pleaseFinishTheInitiatedDowngradeTo": "Vänligen slutför den påbörjade nedgradering för att aktivera uppdateringar.",
"updateOs.rawChangelogRenderer.errorParsingChangelog": "Fel vid tolkning av ändringslogg • {0}",
"updateOs.rawChangelogRenderer.itSHighlyRecommendedToReview": "Det är starkt rekommenderat att granska ändringsloggen innan du fortsätter din uppdatering",
"updateOs.rawChangelogRenderer.loadingChangelog": "Laddar ändringslogg...",
"updateOs.rawChangelogRenderer.noChangelogContentAvailable": "Inget innehåll för ändringslogg tillgängligt",
"updateOs.rawChangelogRenderer.viewChangelogOnDocs": "Visa ändringslogg på dokumentation",
"updateOs.reboot.downgrade": "Omstart krävs för nedgradering",
"updateOs.reboot.thirdPartyDriversDownloading": "Uppdaterar tredjepartsdrivrutiner",
"updateOs.reboot.update": "Omstart krävs för uppdatering",
"updateOs.status.cancel": "Avbryt {0}",
"updateOs.status.checking": "Kontrollerar...",
"updateOs.status.downgrade": "Nedgradera",
@@ -594,7 +595,15 @@
"userProfile.trial.startingYourFreeDayTrial": "Startar din gratis 30 dagars testperiod",
"userProfile.trial.trialKeyCreated": "Testperiodnyckel skapad",
"userProfile.trial.trialKeyCreationFailed": "Misslyckades vid skapandet av testperiodnyckel",
"userProfile.uptimeExpire.expired": "Utgått {0}",
"userProfile.uptimeExpire.expiredAt": "Utgått kl {0}",
"userProfile.uptimeExpire.expiresAt": "Utgår kl {0}",
"userProfile.uptimeExpire.expiresIn": "Utgår om {0}",
"userProfile.uptimeExpire.serverUpSince": "Server i drift sedan {0}",
"userProfile.uptimeExpire.trialKeyExpired": "Prövonyckel Utgått {0}",
"userProfile.uptimeExpire.trialKeyExpiredAt": "Prövonyckel Utgått kl {0}",
"userProfile.uptimeExpire.trialKeyExpiresAt": "Prövonyckel Utgår kl {0}",
"userProfile.uptimeExpire.trialKeyExpiresIn": "Prövonyckel Utgår om {0}",
"userProfile.uptimeExpire.uptime": "Driftstid {0}",
"wanIpCheck.checkingWanIps": "Kontrollerar WAN IPs…",
"wanIpCheck.dnsIssueUnableToResolveWanip4": "DNS-problem, kunde inte lösa wanip4.unraid.net",

View File

@@ -55,13 +55,6 @@
"connectSettings.updatedApiSettingsToast": "Оновлені налаштування API",
"downgradeOs.downgradeUnraidOs": "Знизити версію Unraid OS",
"downgradeOs.pleaseFinishTheInitiatedUpdateTo": "Будь ласка, завершіть ініційоване оновлення, щоб увімкнути зниження версії.",
"downloadApiLogs.downloadUnraidApiLogs": "Завантажити журнали unraid-api",
"downloadApiLogs.ifYouAreAskedToSupply": "Якщо вам буде наказано надати журнали, відкрийте запит на підтримку на нашій сторінці контактів і відповідайте на електронний лист, який ви отримаєте, з вашими журналами у вигляді вкладення.",
"downloadApiLogs.theLogsMayContainSensitiveInformation": "Журнали можуть містити конфіденційну інформацію, тому не публікуйте їх публічно.",
"downloadApiLogs.thePrimaryMethodOfSupportFor": "Основним методом підтримки Unraid Connect є наші форуми та Discord.",
"downloadApiLogs.unraidConnectForums": "Форуми Unraid Connect",
"downloadApiLogs.unraidContactPage": "Сторінка контактів Unraid",
"downloadApiLogs.unraidDiscord": "Unraid Discord",
"headerOsVersion.apiVersionCopiedToClipboard": "Версія API скопійована в буфер обміну",
"headerOsVersion.osVersionCopiedToClipboard": "Версія OS скопійована в буфер обміну",
"headerOsVersion.unraidApi": "Unraid API",
@@ -473,12 +466,20 @@
"updateOs.downgrade.releaseNotes": "{0} Примітки про випуск",
"updateOs.ignoredRelease.remove": "Видалити",
"updateOs.ignoredRelease.removeFromIgnoreList": "Видалити зі списку ігнорування",
"updateOs.ineligible.guidRequired": "Потрібен дійсний GUID для перевірки наявності оновлень ОС.",
"updateOs.ineligible.keyfileRequired": "Потрібен дійсний ключовий файл для перевірки наявності оновлень ОС.",
"updateOs.ineligible.osVersionRequired": "Потрібна дійсна версія ОС для перевірки наявності оновлень ОС.",
"updateOs.ineligible.updatesExpired": "Ваша ліцензія {0} включала один рік безкоштовних оновлень під час покупки. Тепер ви можете продовжити свою ліцензію та отримати доступ до останніх оновлень ОС.",
"updateOs.ineligible.updatesExpiredWithAvailable": "Ваша ліцензія {0} включала один рік безкоштовних оновлень на момент покупки. Тепер ви можете продовжити ліцензію та отримати доступ до останніх оновлень ОС. Ви все ще можете отримати доступ до оновлень ОС, опублікованих на {1} або раніше.",
"updateOs.pleaseFinishTheInitiatedDowngradeTo": "Будь ласка, завершiть ініційоване зниження, щоб увімкнути оновлення.",
"updateOs.rawChangelogRenderer.errorParsingChangelog": "Помилка парсингу журналу змін ∙ {0}",
"updateOs.rawChangelogRenderer.itSHighlyRecommendedToReview": "Настійно рекомендовано переглянути журнал змін перед продовженням оновлення",
"updateOs.rawChangelogRenderer.loadingChangelog": "Завантаження журналу змін…",
"updateOs.rawChangelogRenderer.noChangelogContentAvailable": "Вміст журналу змін недоступний",
"updateOs.rawChangelogRenderer.viewChangelogOnDocs": "Перегляд журналу змін у документах",
"updateOs.reboot.downgrade": "Перезавантаження потрібно для зниження",
"updateOs.reboot.thirdPartyDriversDownloading": "Оновлення драйверів третіх сторін",
"updateOs.reboot.update": "Перезавантаження потрібно для оновлення",
"updateOs.status.cancel": "Скасувати {0}",
"updateOs.status.checking": "Перевіряється…",
"updateOs.status.downgrade": "Зниження версії",
@@ -594,7 +595,15 @@
"userProfile.trial.startingYourFreeDayTrial": "Запуск вашого безкоштовного 30-денного пробного періоду",
"userProfile.trial.trialKeyCreated": "Пробний ключ створено",
"userProfile.trial.trialKeyCreationFailed": "Створення пробного ключа не вдалося",
"userProfile.uptimeExpire.expired": "Термін дії {0} минув",
"userProfile.uptimeExpire.expiredAt": "Термін дії закінчився о {0}",
"userProfile.uptimeExpire.expiresAt": "Закінчується о {0}",
"userProfile.uptimeExpire.expiresIn": "Закінчується через {0}",
"userProfile.uptimeExpire.serverUpSince": "Сервер працює з {0}",
"userProfile.uptimeExpire.trialKeyExpired": "Термін дії тестового ключа {0} минув",
"userProfile.uptimeExpire.trialKeyExpiredAt": "Термін дії тестового ключа закінчився о {0}",
"userProfile.uptimeExpire.trialKeyExpiresAt": "Термін дії тестового ключа закінчується о {0}",
"userProfile.uptimeExpire.trialKeyExpiresIn": "Термін дії тестового ключа закінчується через {0}",
"userProfile.uptimeExpire.uptime": "Час безперебійної роботи {0}",
"wanIpCheck.checkingWanIps": "Перевірка WAN IP…",
"wanIpCheck.dnsIssueUnableToResolveWanip4": "Проблема з DNS, не вдалося вирішити wanip4.unraid.net",

View File

@@ -55,13 +55,6 @@
"connectSettings.updatedApiSettingsToast": "已更新 API 设置",
"downgradeOs.downgradeUnraidOs": "降级 Unraid OS",
"downgradeOs.pleaseFinishTheInitiatedUpdateTo": "请完成已经初始化的更新以启用降级。",
"downloadApiLogs.downloadUnraidApiLogs": "下载 unraid-api 日志",
"downloadApiLogs.ifYouAreAskedToSupply": "如果您被要求提供日志,请在我们的联系页面上打开支持请求,并将收到的电子邮件与您的日志一起回复。",
"downloadApiLogs.theLogsMayContainSensitiveInformation": "日志可能包含敏感信息,请勿公开发布。",
"downloadApiLogs.thePrimaryMethodOfSupportFor": "Unraid Connect 的主要支持方法是通过我们的论坛和 Discord。",
"downloadApiLogs.unraidConnectForums": "Unraid Connect 论坛",
"downloadApiLogs.unraidContactPage": "Unraid 联系页面",
"downloadApiLogs.unraidDiscord": "Unraid Discord",
"headerOsVersion.apiVersionCopiedToClipboard": "API 版本已复制到剪贴板",
"headerOsVersion.osVersionCopiedToClipboard": "OS 版本已复制到剪贴板",
"headerOsVersion.unraidApi": "Unraid API",
@@ -473,12 +466,20 @@
"updateOs.downgrade.releaseNotes": "{0}发布说明",
"updateOs.ignoredRelease.remove": "删除",
"updateOs.ignoredRelease.removeFromIgnoreList": "从忽略列表中移除",
"updateOs.ineligible.guidRequired": "需要一个有效的GUID以检查操作系统更新。",
"updateOs.ineligible.keyfileRequired": "需要一个有效的密钥文件以检查操作系统更新。",
"updateOs.ineligible.osVersionRequired": "需要一个有效的操作系统版本以检查操作系统更新。",
"updateOs.ineligible.updatesExpired": "您的{0}许可证在购买时包括一年的免费更新。您现在可以延长许可证并访问最新的操作系统更新。",
"updateOs.ineligible.updatesExpiredWithAvailable": "您的{0}许可在购买时包含一年的免费更新。您现在可以延长许可并访问最新的操作系统更新。您仍然可以访问截至{1}或之前发布的操作系统更新。",
"updateOs.pleaseFinishTheInitiatedDowngradeTo": "请完成已启动的降级以启用更新。",
"updateOs.rawChangelogRenderer.errorParsingChangelog": "解析更新日志错误 • {0}",
"updateOs.rawChangelogRenderer.itSHighlyRecommendedToReview": "强烈建议在继续更新前查看更新日志",
"updateOs.rawChangelogRenderer.loadingChangelog": "加载更新日志...",
"updateOs.rawChangelogRenderer.noChangelogContentAvailable": "无更新日志内容可用",
"updateOs.rawChangelogRenderer.viewChangelogOnDocs": "在文档中查看更新日志",
"updateOs.reboot.downgrade": "降级需要重新启动",
"updateOs.reboot.thirdPartyDriversDownloading": "正在更新第三方驱动程序",
"updateOs.reboot.update": "更新需要重新启动",
"updateOs.status.cancel": "取消{0}",
"updateOs.status.checking": "检查中...",
"updateOs.status.downgrade": "降级",
@@ -594,7 +595,15 @@
"userProfile.trial.startingYourFreeDayTrial": "开始您的免费30天试用",
"userProfile.trial.trialKeyCreated": "试用密钥已创建",
"userProfile.trial.trialKeyCreationFailed": "试用密钥创建失败",
"userProfile.uptimeExpire.expired": "{0}已过期",
"userProfile.uptimeExpire.expiredAt": "于{0}过期",
"userProfile.uptimeExpire.expiresAt": "将于{0}过期",
"userProfile.uptimeExpire.expiresIn": "在{0}内过期",
"userProfile.uptimeExpire.serverUpSince": "服务器已上线自 {0}",
"userProfile.uptimeExpire.trialKeyExpired": "试用密钥已过期{0}",
"userProfile.uptimeExpire.trialKeyExpiredAt": "试用密钥在{0}时已过期",
"userProfile.uptimeExpire.trialKeyExpiresAt": "试用密钥将在{0}时过期",
"userProfile.uptimeExpire.trialKeyExpiresIn": "试用密钥在{0}内过期",
"userProfile.uptimeExpire.uptime": "在线时间{0}",
"wanIpCheck.checkingWanIps": "检查WAN IP中…",
"wanIpCheck.dnsIssueUnableToResolveWanip4": "DNS问题无法解析wanip4.unraid.net",

View File

@@ -3,7 +3,6 @@ import { defineStore } from 'pinia';
import { useQuery } from '@vue/apollo-composable';
import { defaultColors } from '~/themes/default';
import hexToRgba from 'hex-to-rgba';
import type { GetThemeQuery } from '~/composables/gql/graphql';
import type { Theme, ThemeVariables } from '~/themes/types';
@@ -27,8 +26,6 @@ export const GET_THEME_QUERY = graphql(`
}
`);
export const THEME_STORAGE_KEY = 'unraid.theme.publicTheme';
const DEFAULT_THEME: Theme = {
name: 'white',
banner: false,
@@ -41,6 +38,55 @@ const DEFAULT_THEME: Theme = {
type ThemeSource = 'local' | 'server';
let pendingDarkModeHandler: ((event: Event) => void) | null = null;
const syncBodyDarkClass = (method: 'add' | 'remove'): boolean => {
const body = typeof document !== 'undefined' ? document.body : null;
if (!body) {
return false;
}
body.classList[method]('dark');
return true;
};
const applyDarkClass = (isDark: boolean) => {
if (typeof document === 'undefined') return;
const method: 'add' | 'remove' = isDark ? 'add' : 'remove';
document.documentElement.classList[method]('dark');
const unapiElements = document.querySelectorAll('.unapi');
unapiElements.forEach((element) => {
element.classList[method]('dark');
});
if (pendingDarkModeHandler) {
document.removeEventListener('DOMContentLoaded', pendingDarkModeHandler);
pendingDarkModeHandler = null;
}
if (syncBodyDarkClass(method)) {
return;
}
const handler = () => {
if (syncBodyDarkClass(method)) {
const unapiElementsOnLoad = document.querySelectorAll('.unapi');
unapiElementsOnLoad.forEach((element) => {
element.classList[method]('dark');
});
document.removeEventListener('DOMContentLoaded', handler);
if (pendingDarkModeHandler === handler) {
pendingDarkModeHandler = null;
}
}
};
pendingDarkModeHandler = handler;
document.addEventListener('DOMContentLoaded', handler);
};
const sanitizeTheme = (data: Partial<Theme> | null | undefined): Theme | null => {
if (!data || typeof data !== 'object') {
return null;
@@ -59,289 +105,124 @@ const sanitizeTheme = (data: Partial<Theme> | null | undefined): Theme | null =>
};
};
const DYNAMIC_VAR_KEYS = [
'--custom-header-text-primary',
'--custom-header-text-secondary',
'--custom-header-background-color',
'--custom-header-gradient-start',
'--custom-header-gradient-end',
'--header-background-color',
'--header-gradient-start',
'--header-gradient-end',
'--color-header-background',
'--color-header-gradient-start',
'--color-header-gradient-end',
'--banner-gradient',
] as const;
export const useThemeStore = defineStore('theme', () => {
// State
const theme = ref<Theme>({ ...DEFAULT_THEME });
type DynamicVarKey = (typeof DYNAMIC_VAR_KEYS)[number];
const activeColorVariables = ref<ThemeVariables>(defaultColors.white);
const hasServerTheme = ref(false);
const devOverride = ref(false);
export const useThemeStore = defineStore(
'theme',
() => {
// State
const theme = ref<Theme>({ ...DEFAULT_THEME });
const { result, onResult, onError } = useQuery<GetThemeQuery>(GET_THEME_QUERY, null, {
fetchPolicy: 'cache-and-network',
nextFetchPolicy: 'cache-first',
});
const activeColorVariables = ref<ThemeVariables>(defaultColors.white);
const hasServerTheme = ref(false);
const devOverride = ref(false);
const mapPublicTheme = (publicTheme?: GetThemeQuery['publicTheme'] | null): Theme | null => {
if (!publicTheme) {
return null;
}
const { result, onResult, onError } = useQuery<GetThemeQuery>(GET_THEME_QUERY, null, {
fetchPolicy: 'cache-and-network',
nextFetchPolicy: 'cache-first',
return sanitizeTheme({
name: publicTheme.name?.toLowerCase(),
banner: publicTheme.showBannerImage,
bannerGradient: publicTheme.showBannerGradient,
bgColor: publicTheme.headerBackgroundColor ?? undefined,
descriptionShow: publicTheme.showHeaderDescription,
metaColor: publicTheme.headerSecondaryTextColor ?? undefined,
textColor: publicTheme.headerPrimaryTextColor ?? undefined,
});
};
const mapPublicTheme = (publicTheme?: GetThemeQuery['publicTheme'] | null): Theme | null => {
if (!publicTheme) {
return null;
}
const applyThemeFromQuery = (publicTheme?: GetThemeQuery['publicTheme'] | null) => {
const sanitized = mapPublicTheme(publicTheme);
if (!sanitized) {
return;
}
return sanitizeTheme({
name: publicTheme.name?.toLowerCase(),
banner: publicTheme.showBannerImage,
bannerGradient: publicTheme.showBannerGradient,
bgColor: publicTheme.headerBackgroundColor ?? undefined,
descriptionShow: publicTheme.showHeaderDescription,
metaColor: publicTheme.headerSecondaryTextColor ?? undefined,
textColor: publicTheme.headerPrimaryTextColor ?? undefined,
});
};
setTheme(sanitized, { source: 'server' });
};
const applyThemeFromQuery = (publicTheme?: GetThemeQuery['publicTheme'] | null) => {
const sanitized = mapPublicTheme(publicTheme);
if (!sanitized) {
onResult(({ data }) => {
if (data?.publicTheme) {
applyThemeFromQuery(data.publicTheme);
}
});
if (result.value?.publicTheme) {
applyThemeFromQuery(result.value.publicTheme);
}
onError((err) => {
console.warn('Failed to load theme from server, keeping existing theme:', err);
});
// Getters
// Apply dark mode for gray and black themes
const darkMode = computed<boolean>(() =>
DARK_UI_THEMES.includes(theme.value?.name as (typeof DARK_UI_THEMES)[number])
);
const bannerGradient = computed(() => {
if (!theme.value?.banner || !theme.value?.bannerGradient) {
return undefined;
}
const start = theme.value?.bgColor ? 'var(--header-gradient-start)' : 'rgba(0, 0, 0, 0)';
const end = theme.value?.bgColor ? 'var(--header-gradient-end)' : 'var(--header-background-color)';
return `background-image: linear-gradient(90deg, ${start} 0, ${end} 90%);`;
});
// Actions
function setTheme(data?: Partial<Theme>, options: { source?: ThemeSource } = {}) {
if (data) {
const { source = 'local' } = options;
if (source === 'server') {
hasServerTheme.value = true;
} else if (hasServerTheme.value && !devOverride.value) {
return;
}
setTheme(sanitized, { source: 'server' });
};
onResult(({ data }) => {
if (data?.publicTheme) {
applyThemeFromQuery(data.publicTheme);
}
});
if (result.value?.publicTheme) {
applyThemeFromQuery(result.value.publicTheme);
}
onError((err) => {
console.warn('Failed to load theme from server, keeping existing theme:', err);
});
// Getters
// Apply dark mode for gray and black themes
const darkMode = computed<boolean>(() =>
DARK_UI_THEMES.includes(theme.value?.name as (typeof DARK_UI_THEMES)[number])
);
const bannerGradient = computed(() => {
if (!theme.value?.banner || !theme.value?.bannerGradient) {
return undefined;
}
const start = theme.value?.bgColor ? 'var(--header-gradient-start)' : 'rgba(0, 0, 0, 0)';
const end = theme.value?.bgColor ? 'var(--header-gradient-end)' : 'var(--header-background-color)';
return `background-image: linear-gradient(90deg, ${start} 0, ${end} 90%);`;
});
// Actions
const setTheme = (data?: Partial<Theme>, options: { source?: ThemeSource } = {}) => {
if (data) {
const { source = 'local' } = options;
if (source === 'server') {
hasServerTheme.value = true;
} else if (hasServerTheme.value && !devOverride.value) {
return;
}
const sanitized = sanitizeTheme({
...theme.value,
...data,
});
if (sanitized) {
theme.value = sanitized;
}
}
};
const setDevOverride = (enabled: boolean) => {
devOverride.value = enabled;
};
const setCssVars = () => {
const selectedTheme = theme.value.name;
// Check if Unraid PHP has already set a Theme-- class
const hasExistingThemeClass =
typeof document !== 'undefined' &&
Array.from(document.documentElement.classList).some((cls) => cls.startsWith('Theme--'));
// Prepare Tailwind v4 theme classes
const themeClasses: string[] = [];
const customClasses: string[] = [];
// Apply dark/light mode using Tailwind v4 theme switching
if (darkMode.value) {
themeClasses.push('dark');
}
// Only apply theme-specific class if Unraid PHP hasn't already set it
if (!hasExistingThemeClass) {
themeClasses.push(`Theme--${selectedTheme}`);
}
// Only set CSS variables for dynamic/user-configured values from GraphQL
// Static theme values are handled by Tailwind v4 theme classes in @tailwind-shared
const dynamicVars: Partial<Record<DynamicVarKey, string>> = {};
// User-configured colors from webGUI @ /Settings/DisplaySettings
if (theme.value.textColor) {
dynamicVars['--custom-header-text-primary'] = theme.value.textColor;
customClasses.push('has-custom-header-text');
}
if (theme.value.metaColor) {
dynamicVars['--custom-header-text-secondary'] = theme.value.metaColor;
customClasses.push('has-custom-header-meta');
}
if (theme.value.bgColor) {
const gradientStart = hexToRgba(theme.value.bgColor, 0);
const gradientEnd = hexToRgba(theme.value.bgColor, 0.7);
dynamicVars['--custom-header-background-color'] = theme.value.bgColor;
dynamicVars['--custom-header-gradient-start'] = gradientStart;
dynamicVars['--custom-header-gradient-end'] = gradientEnd;
// Apply the resolved values directly to ensure they override base theme vars
dynamicVars['--header-background-color'] = theme.value.bgColor;
dynamicVars['--header-gradient-start'] = gradientStart;
dynamicVars['--header-gradient-end'] = gradientEnd;
dynamicVars['--color-header-background'] = theme.value.bgColor;
dynamicVars['--color-header-gradient-start'] = gradientStart;
dynamicVars['--color-header-gradient-end'] = gradientEnd;
customClasses.push('has-custom-header-bg');
}
// Set banner gradient if needed
if (theme.value.banner && theme.value.bannerGradient) {
const start = theme.value.bgColor
? hexToRgba(theme.value.bgColor, 0)
: 'var(--header-gradient-start)';
const end = theme.value.bgColor
? hexToRgba(theme.value.bgColor, 0.7)
: 'var(--header-gradient-end)';
dynamicVars['--banner-gradient'] = `linear-gradient(90deg, ${start} 0, ${end} 90%)`;
customClasses.push('has-banner-gradient');
}
requestAnimationFrame(() => {
const scopedTargets: HTMLElement[] = [
document.documentElement,
...Array.from(document.querySelectorAll<HTMLElement>('.unapi')),
];
const styleTargets = [...scopedTargets, document.body].filter(Boolean) as HTMLElement[];
const cleanClassList = (classList: string, isDocumentElement: boolean) => {
// Don't remove Theme-- classes from documentElement if Unraid PHP set them
if (isDocumentElement && hasExistingThemeClass) {
return classList
.split(' ')
.filter((c) => c !== 'dark' && !c.startsWith('has-custom-') && c !== 'has-banner-gradient')
.filter(Boolean)
.join(' ');
}
// For .unapi roots or when we're managing the theme class, clean everything
return classList
.split(' ')
.filter(
(c) =>
!c.startsWith('Theme--') &&
c !== 'dark' &&
!c.startsWith('has-custom-') &&
c !== 'has-banner-gradient'
)
.filter(Boolean)
.join(' ');
};
// Apply theme and custom classes to html element and all .unapi roots
scopedTargets.forEach((target) => {
const isDocumentElement = target === document.documentElement;
target.className = cleanClassList(target.className, isDocumentElement);
[...themeClasses, ...customClasses].forEach((cls) => target.classList.add(cls));
if (darkMode.value) {
target.classList.add('dark');
} else {
target.classList.remove('dark');
}
});
// Maintain dark mode flag on body for legacy components
if (darkMode.value) {
document.body.classList.add('dark');
} else {
document.body.classList.remove('dark');
}
// Only apply dynamic CSS variables for custom user values
// All theme defaults are handled by classes in @tailwind-shared/theme-variants.css
const activeDynamicKeys = Object.keys(dynamicVars) as DynamicVarKey[];
styleTargets.forEach((target) => {
activeDynamicKeys.forEach((key) => {
const value = dynamicVars[key];
if (value !== undefined) {
target.style.setProperty(key, value);
}
});
DYNAMIC_VAR_KEYS.forEach((key) => {
if (!Object.prototype.hasOwnProperty.call(dynamicVars, key)) {
target.style.removeProperty(key);
}
});
});
// Store active variables for reference (from defaultColors for compatibility)
const customTheme = { ...defaultColors[selectedTheme] };
activeColorVariables.value = customTheme;
const sanitized = sanitizeTheme({
...theme.value,
...data,
});
};
watch(
theme,
() => {
setCssVars();
},
{ immediate: true }
);
return {
// state
activeColorVariables,
bannerGradient,
darkMode,
theme,
// actions
setTheme,
setCssVars,
setDevOverride,
};
},
{
persist: {
key: THEME_STORAGE_KEY,
pick: ['theme'],
afterHydrate: (ctx) => {
const store = ctx.store as ReturnType<typeof useThemeStore>;
store.setTheme(store.theme);
},
},
if (sanitized) {
theme.value = sanitized;
const fallbackTheme = defaultColors[sanitized.name as keyof typeof defaultColors];
activeColorVariables.value = {
...(fallbackTheme ?? defaultColors.white),
};
}
}
}
);
const setDevOverride = (enabled: boolean) => {
devOverride.value = enabled;
};
const setCssVars = () => {
applyDarkClass(darkMode.value);
};
watch(
theme,
() => {
setCssVars();
},
{ immediate: true }
);
return {
// state
activeColorVariables,
bannerGradient,
darkMode,
theme,
// actions
setTheme,
setCssVars,
setDevOverride,
};
});

View File

@@ -31,9 +31,6 @@ export async function initializeTheme(): Promise<void> {
// Load theme from GraphQL
await themeStore.setTheme();
// Apply CSS variables and Tailwind classes
themeStore.setCssVars();
isThemeInitialized = true;
console.debug('[ThemeInitializer] Theme initialized successfully');

View File

@@ -1,4 +1,5 @@
import { computed, ref, watchEffect } from 'vue';
import { useI18n } from 'vue-i18n';
import { defineStore } from 'pinia';
import { ArrowPathIcon, BellAlertIcon } from '@heroicons/vue/24/solid';
@@ -32,6 +33,7 @@ export interface Release {
}
export const useUpdateOsActionsStore = defineStore('updateOsActions', () => {
const { t } = useI18n();
const accountStore = useAccountStore();
// const errorsStore = useErrorsStore();
const serverStore = useServerStore();
@@ -48,8 +50,13 @@ export const useUpdateOsActionsStore = defineStore('updateOsActions', () => {
const osVersion = computed(() => serverStore.osVersion);
const osVersionBranch = computed(() => serverStore.osVersionBranch);
const regUpdatesExpired = computed(() => serverStore.regUpdatesExpired);
const regTy = computed(() => serverStore.regTy);
const locale = computed(() => serverStore.locale);
const updateOsAvailable = computed(() => updateOsStore.available);
const availableWithRenewalRelease = computed(() =>
updateOsStore.availableWithRenewal ? serverStore.updateOsResponse : undefined
);
/** used when coming back from callback, this will be the release to install */
const status = ref<
| 'confirming'
@@ -65,14 +72,13 @@ export const useUpdateOsActionsStore = defineStore('updateOsActions', () => {
const callbackUpdateRelease = ref<Release | null>(null);
const rebootType = computed(() => serverStore.rebootType);
const rebootTypeText = computed(() => {
/** translations are handled by rendering template's `t()` */
switch (rebootType.value) {
case 'thirdPartyDriversDownloading':
return 'Updating 3rd party drivers';
return t('updateOs.reboot.thirdPartyDriversDownloading');
case 'downgrade':
return 'Reboot Required for Downgrade';
return t('updateOs.reboot.downgrade');
case 'update':
return 'Reboot Required for Update';
return t('updateOs.reboot.update');
default:
return '';
}
@@ -81,23 +87,37 @@ export const useUpdateOsActionsStore = defineStore('updateOsActions', () => {
const ineligible = computed(
() => !guid.value || !keyfile.value || !osVersion.value || regUpdatesExpired.value
);
const formattedReleaseDate = computed(() => {
if (!availableWithRenewalRelease.value?.date) return '';
const dateStr = availableWithRenewalRelease.value.date;
const [year, month, day] = dateStr.split('-').map(Number);
const date = new Date(year, month - 1, day);
const userLocale = locale.value?.replace('_', '-') || navigator.language || 'en-US';
return new Intl.DateTimeFormat(userLocale, {
year: 'numeric',
month: 'long',
day: 'numeric',
}).format(date);
});
const ineligibleText = computed(() => {
// translated in components
if (!guid.value) {
return 'A valid GUID is required to check for OS updates.';
return t('updateOs.ineligible.guidRequired');
}
if (!keyfile.value) {
return 'A valid keyfile is required to check for OS updates.';
return t('updateOs.ineligible.keyfileRequired');
}
if (!osVersion.value) {
return 'A valid OS version is required to check for OS updates.';
return t('updateOs.ineligible.osVersionRequired');
}
if (regUpdatesExpired.value) {
const base =
'Your {0} license included one year of free updates at the time of purchase. You are now eligible to extend your license and access the latest OS updates.';
const addtlText =
'You are still eligible to access OS updates that were published on or before {1}.';
return updateOsAvailable.value ? `${base} ${addtlText}` : base;
if (updateOsAvailable.value) {
return t('updateOs.ineligible.updatesExpiredWithAvailable', [
regTy.value,
formattedReleaseDate.value,
]);
}
return t('updateOs.ineligible.updatesExpired', [regTy.value]);
}
return '';
});
@@ -241,6 +261,7 @@ export const useUpdateOsActionsStore = defineStore('updateOsActions', () => {
status,
ineligible,
ineligibleText,
formattedReleaseDate,
toolsRegistrationAction,
// Actions
actOnUpdateOsAction,