diff --git a/src/apps/components/acceleratoroverview/AcceleratorOverview.svelte b/src/apps/components/acceleratoroverview/AcceleratorOverview.svelte index 185090d82..39f7095bc 100755 --- a/src/apps/components/acceleratoroverview/AcceleratorOverview.svelte +++ b/src/apps/components/acceleratoroverview/AcceleratorOverview.svelte @@ -6,9 +6,9 @@ {#if $store && $apps} -
-

Keyboard Shortcuts

-

Get more work done faster with these handy shortcuts!

+
+

%title%

+

%subtitle%

{#each $store as [name, shortcuts]} diff --git a/src/apps/components/appinfo/AppInfo.ts b/src/apps/components/appinfo/AppInfo.ts index a348b0e43..e9bd2013b 100644 --- a/src/apps/components/appinfo/AppInfo.ts +++ b/src/apps/components/appinfo/AppInfo.ts @@ -5,7 +5,7 @@ import { AppInfoRuntime } from "./runtime"; const AppInfoApp: App = { metadata: { - name: "App Info", + name: "%apps.AppInfo._name%", author: "Izaak Kuipers", version: "3.0.1", icon: "AppInfoIcon", diff --git a/src/apps/components/appinfo/AppInfo/Actions.svelte b/src/apps/components/appinfo/AppInfo/Actions.svelte index 584351b36..1ca5fd030 100755 --- a/src/apps/components/appinfo/AppInfo/Actions.svelte +++ b/src/apps/components/appinfo/AppInfo/Actions.svelte @@ -24,7 +24,7 @@ {/snippet} {#snippet rightContent()} - process.killAll()}>Kill all - process.closeWindow()}>Close + process.killAll()}>%actions.killAll% + process.closeWindow()}>%general.close% {/snippet} diff --git a/src/apps/components/appinfo/AppInfo/Header.svelte b/src/apps/components/appinfo/AppInfo/Header.svelte index bdba3d8f2..a12f785c5 100755 --- a/src/apps/components/appinfo/AppInfo/Header.svelte +++ b/src/apps/components/appinfo/AppInfo/Header.svelte @@ -46,17 +46,27 @@
-

{target?.metadata?.name || "Unknown"}

-

{target?.metadata?.author || "No author"}

+

{target?.metadata?.name || "%general.unknown%"}

+

{target?.metadata?.author || "%general.noAuthor%"}

{disabled ? "%general.enable%" : "%general.disable%"} {#if (target?.entrypoint || target?.workingDirectory) && installed} - + {/if} - +
diff --git a/src/apps/components/appinfo/AppInfo/IndepthInfo.svelte b/src/apps/components/appinfo/AppInfo/IndepthInfo.svelte index 5ce27a7eb..4ebe2fb20 100755 --- a/src/apps/components/appinfo/AppInfo/IndepthInfo.svelte +++ b/src/apps/components/appinfo/AppInfo/IndepthInfo.svelte @@ -10,43 +10,45 @@ - + {Math.max(0, target.size?.w || 0) || "F"} x {Math.max(0, target.size?.h || 0) || "F"} - + {Math.max(0, target.minSize?.w || 0) || "F"} x {Math.max(0, target.minSize?.h || 0) || "F"} - + {Math.max(0, target.maxSize?.w || 0) || "F"} x {Math.max(0, target.maxSize?.h || 0) || "F"} - +
- - - + + +
- + {#if target?.position?.centered} - Centered + %indepthInfo.centered% {:else if target?.position?.x || target.position?.y} {target?.position?.x}, {target?.position?.y} {:else} - Corner of screen + %indepthInfo.cornerOfScreen% {/if} - + {AppOrigins[target?.originId || "injected"]} - - {target?.core ? "Yes" : "No"} + + {target?.core ? "%general.yes%" : "%general.no%"} - - {target?.hidden ? "Yes" : "No"} + + {target?.hidden ? "%general.yes%" : "%general.no%"}
diff --git a/src/apps/components/appinfo/AppInfo/InternalInfo.svelte b/src/apps/components/appinfo/AppInfo/InternalInfo.svelte index b8c948ef8..24ff0f375 100644 --- a/src/apps/components/appinfo/AppInfo/InternalInfo.svelte +++ b/src/apps/components/appinfo/AppInfo/InternalInfo.svelte @@ -10,18 +10,18 @@ - + {target._internalLoadTime?.toFixed(2) || 0}ms - + {target._internalSysVer} - + {target._internalMinVer || "-"} - + {target._internalOriginalPath} diff --git a/src/apps/components/appinfo/AppInfo/ProcessInfo.svelte b/src/apps/components/appinfo/AppInfo/ProcessInfo.svelte index 57314d7e3..3d2347cb8 100755 --- a/src/apps/components/appinfo/AppInfo/ProcessInfo.svelte +++ b/src/apps/components/appinfo/AppInfo/ProcessInfo.svelte @@ -31,12 +31,12 @@ - - {count} instance(s) + + %processInfo.instances({count})% - - {pid < 0 ? "None" : pid} + + {pid < 0 ? "%general.none%" : pid} - + diff --git a/src/apps/components/appinfo/AppInfo/ThirdPartyInfo.svelte b/src/apps/components/appinfo/AppInfo/ThirdPartyInfo.svelte index 0b7185379..6822b09bd 100755 --- a/src/apps/components/appinfo/AppInfo/ThirdPartyInfo.svelte +++ b/src/apps/components/appinfo/AppInfo/ThirdPartyInfo.svelte @@ -10,19 +10,19 @@ - Yes - - {target.fileSignatures ? Object.entries(target.fileSignatures).length : "None"} + %general.yes% + + {target.fileSignatures ? Object.entries(target.fileSignatures).length : "%general.none%"} - - {target.process ? "Yes" : "No"} + + {target.process ? "%general.yes%" : "%general.no%"} - + {target.workingDirectory} - + {target.entrypoint} diff --git a/src/apps/components/appinfo/runtime.ts b/src/apps/components/appinfo/runtime.ts index 8140768e1..34ac41b65 100755 --- a/src/apps/components/appinfo/runtime.ts +++ b/src/apps/components/appinfo/runtime.ts @@ -23,12 +23,13 @@ export class AppInfoRuntime extends AppProcess implements IAppInfoRuntime { } async render() { + this.getBody().setAttribute("data-prefix", "apps.AppInfo"); const targetApp = this.appStore()?.getAppSynchronous(this.targetAppId); if (!targetApp) { Daemon?.notifications?.sendNotification({ - title: "App not found", - message: `AppInfo couldn't find any information about "${this.targetAppId}". Is it installed?`, + title: "%apps.AppInfo.noTargetApp.title%", + message: `%apps.AppInfo.noTargetApp.message(${this.targetAppId})%`, image: "AppInfoIcon", timeout: 6000, }); @@ -46,8 +47,8 @@ export class AppInfoRuntime extends AppProcess implements IAppInfoRuntime { async killAll() { this.Log(`killAll`); - const elevated = await Daemon?.elevation?.manuallyElevate({ - what: `ArcOS needs your permission to kill all instances of an app`, + const elevated = await this.userDaemon?.manuallyElevate({ + what: `%apps.AppInfo.killAll.what%`, image: `@app::${this.targetAppId}`, title: this.targetApp().metadata.name, description: this.targetAppId, diff --git a/src/apps/components/appinstaller/AppInstaller/Actions.svelte b/src/apps/components/appinstaller/AppInstaller/Actions.svelte index d9eac63af..40a44bffc 100755 --- a/src/apps/components/appinstaller/AppInstaller/Actions.svelte +++ b/src/apps/components/appinstaller/AppInstaller/Actions.svelte @@ -8,14 +8,14 @@
{#if $failReason} - + {:else if $installing || $completed} {#if !isLibrary} - + {/if} {:else} - - + + {/if}
diff --git a/src/apps/components/appinstaller/AppInstaller/Header.svelte b/src/apps/components/appinstaller/AppInstaller/Header.svelte index 5fdc8086b..f01e011bf 100755 --- a/src/apps/components/appinstaller/AppInstaller/Header.svelte +++ b/src/apps/components/appinstaller/AppInstaller/Header.svelte @@ -24,7 +24,7 @@ Installing package... {/if} {:else if $failReason} - Installation failed + %header.title.failed% {:else} {metadata?.name} {/if} @@ -37,11 +37,11 @@ Click Open now to launch the app {/if} {:else if $installing} - {metadata?.name} by {metadata?.author} + %header.subtitle.installing({metadata?.name}::{metadata?.author})% {:else if $failReason} {$failReason} {:else} - {metadata?.author} - {metadata?.version} + %header.subtitle.generic({metadata?.author}::{metadata?.version})% {/if}

diff --git a/src/apps/components/appinstaller/AppInstaller/Log.svelte b/src/apps/components/appinstaller/AppInstaller/Log.svelte index fc0f6c793..c6780f978 100755 --- a/src/apps/components/appinstaller/AppInstaller/Log.svelte +++ b/src/apps/components/appinstaller/AppInstaller/Log.svelte @@ -19,21 +19,21 @@ {#if !Object.entries($status).length && !$installing}
-

Ready to install

-

Click Install to install this package.

+

%readyToInstall.title%

+

%readyToInstall.message%

{:else} {#each Object.entries($status) as [uuid, item] (uuid)}

{#if item.type === "file"} - Writing file + %logType.file% {:else if item.type === "mkdir"} - Creating directory + %logType.mkdir% {:else if item.type === "registration"} - Registering + %logType.registration% {:else} - Status + %logType.generic% {/if}

{item.content}

diff --git a/src/apps/components/appinstaller/runtime.ts b/src/apps/components/appinstaller/runtime.ts index 1b84b20d4..885606006 100755 --- a/src/apps/components/appinstaller/runtime.ts +++ b/src/apps/components/appinstaller/runtime.ts @@ -37,8 +37,8 @@ export class AppInstallerRuntime extends AppProcess implements IAppInstallerRunt // Should never happen unless nik fucked something up (yes, nik) MessageBox( { - title: "Can't install package", - message: "The Distribution Service isn't running anymore. Please restart ArcOS to fix this problem.", + title: "%apps.AppInstaller.noDistrib.title%", + message: "%apps.AppInstaller.noDistrib.message%", buttons: [BTN_OKAY_SUG], image: "ErrorIcon", sound: "arcos.dialog.error", @@ -55,24 +55,25 @@ export class AppInstallerRuntime extends AppProcess implements IAppInstallerRunt } async render() { + this.getBody().setAttribute("data-prefix", "apps.AppInstaller"); + if (!this.userPreferences().security.enableThirdParty) { // The user has to allow TPAs explicitly MessageBox( { - title: "Can't install app", - message: - "Third-party apps aren't enabled on your account. Please enable third-party apps in the Settings to install this app.", + title: "%apps.AppInstaller.noEnableThirdParty.title%", + message: "%apps.AppInstaller.noEnableThirdParty.message%", image: "AppsIcon", sound: "arcos.dialog.warning", buttons: [ { - caption: "Take me there", + caption: "%apps.AppInstaller.noEnableThirdParty.takeMeThere%", action: () => { this.spawnApp("systemSettings", +Env.get("shell_pid"), "apps"); }, }, { - caption: "Okay", + caption: "%general.okay%", action: () => {}, suggested: true, }, diff --git a/src/apps/components/apppreinstall/AppPreinstall.svelte b/src/apps/components/apppreinstall/AppPreinstall.svelte index ae47b03d9..73006c730 100755 --- a/src/apps/components/apppreinstall/AppPreinstall.svelte +++ b/src/apps/components/apppreinstall/AppPreinstall.svelte @@ -11,17 +11,17 @@

{$metadata.name}

-

Do you want to install this package?

+

%header.title%

{$metadata.description}

-

Author

+

%info.author%

{$metadata.author}

-

Version

+

%info.version%

{$metadata.version}

@@ -29,9 +29,9 @@
- +
{:else}
diff --git a/src/apps/components/apppreinstall/runtime.ts b/src/apps/components/apppreinstall/runtime.ts index 87cafff34..7c7be6002 100755 --- a/src/apps/components/apppreinstall/runtime.ts +++ b/src/apps/components/apppreinstall/runtime.ts @@ -31,23 +31,24 @@ export class AppPreInstallRuntime extends AppProcess implements IAppPreInstallRu } async render() { + this.getBody().setAttribute("data-prefix", "apps.AppPreInstall"); + if (!this.userPreferences().security.enableThirdParty) { MessageBox( { - title: "Can't install app", - message: - "Third-party apps aren't enabled on your account. Please enable third-party apps in the Settings to install this app.", + title: "%apps.AppPreInstall.noEnableThirdParty.title%", + message: "%apps.AppPreInstall.noEnableThirdParty.message%", image: "AppsIcon", sound: "arcos.dialog.warning", buttons: [ { - caption: "Take me there", + caption: "%apps.AppPreInstall.noEnableThirdParty.takeMeThere%", action: () => { this.spawnApp("systemSettings", +Env.get("shell_pid"), "apps"); }, }, { - caption: "Okay", + caption: "%general.okay%", action: () => {}, suggested: true, }, @@ -65,7 +66,7 @@ export class AppPreInstallRuntime extends AppProcess implements IAppPreInstallRu { type: "size", icon: "DownloadIcon", - caption: "Reading ArcOS package", + caption: "%apps.AppPreInstall.readingPackage%", subtitle: this.pkgPath, }, +Env.get("shell_pid") @@ -87,16 +88,21 @@ export class AppPreInstallRuntime extends AppProcess implements IAppPreInstallRu await prog?.stop(); if (!content) { - return this.fail("The package contents could not be read"); + return this.fail("%apps.AppPreInstall.errors.noContents%"); } this.zip = new JSZip(); const buffer = await this.zip.loadAsync(content, {}); + + if (!buffer.files["_metadata.json"] || !buffer.files["payload/_app.tpa"]) { + return this.fail("%apps.AppPreInstall.errors.missingFiles%"); + } + const metaBinary = await buffer.files["_metadata.json"].async("arraybuffer"); const metadata = tryJsonParse(arrayBufferToText(metaBinary)); this.metadata.set(metadata); } catch { - return this.fail("Filesystem error"); + return this.fail("%apps.AppPreInstall.errors.fsError%"); } } @@ -108,8 +114,8 @@ export class AppPreInstallRuntime extends AppProcess implements IAppPreInstallRu MessageBox( { - title: "Failed to open package", - message: `ArcOS failed to open the specified package. ${reason}`, + title: "%apps.AppPreInstall.fail.title%", + message: `%apps.AppPreInstall.fail.messagePartial% ${reason}`, buttons: [BTN_OKAY_SUG], image: "ErrorIcon", sound: "arcos.dialog.error", @@ -125,9 +131,9 @@ export class AppPreInstallRuntime extends AppProcess implements IAppPreInstallRu const meta = this.metadata(); const elevated = await Daemon!.elevation!.manuallyElevate({ - what: "ArcOS wants to install an application", + what: "%apps.AppPreInstall.elevation.what%", title: meta.name, - description: `${meta.author} - ${meta.version}`, + description: `%apps.AppPreInstall.elevation.description(${meta.author}::${meta.version})%`, image: "ArcAppMimeIcon", level: ElevationLevel.medium, }); diff --git a/src/apps/components/contextmenu/system.ts b/src/apps/components/contextmenu/system.ts index 34768b2c8..30316fd0b 100755 --- a/src/apps/components/contextmenu/system.ts +++ b/src/apps/components/contextmenu/system.ts @@ -10,14 +10,14 @@ export function WindowSystemContextMenu(): AppContextMenu { return { "_window-titlebar": [ { - caption: "App Info", + caption: "%apps.contextMenu.system.windowTitlebar.appInfo%", icon: "info", action: (proc: IAppProcess) => { proc.spawnOverlayApp("AppInfo", +Env.get("shell_pid"), proc?.app.id); }, }, { - caption: "Process info", + caption: "%apps.contextMenu.system.windowTitlebar.processInfo%", icon: "cog", action: (proc: IAppProcess) => { proc.spawnOverlayApp("ProcessInfoApp", +Env.get("shell_pid"), proc); @@ -26,7 +26,7 @@ export function WindowSystemContextMenu(): AppContextMenu { { sep: true }, { - caption: "Minimize", + caption: "%general.minimize%", action: (proc: IAppProcess) => { Stack.renderer?.toggleMinimize(proc?.pid); }, @@ -35,7 +35,7 @@ export function WindowSystemContextMenu(): AppContextMenu { isActive: (proc: IAppProcess) => !!proc?.getWindow()?.classList.contains("minimized"), }, { - caption: "Maximize", + caption: "%general.maximize%", action: (proc: IAppProcess) => { Stack.renderer?.unsnapWindow(proc?.pid); Stack.renderer?.toggleMaximize(proc?.pid); @@ -46,64 +46,64 @@ export function WindowSystemContextMenu(): AppContextMenu { }, { sep: true }, { - caption: "Window snapping", + caption: "%apps.contextMenu.system.windowTitlebar.windowSnapping%", icon: "fullscreen", disabled: (proc: IAppProcess) => !proc?.app.data.controls.maximize, isActive: (proc: IAppProcess) => !!proc?.getWindow()?.classList.contains("snapped"), subItems: [ { - caption: "None", + caption: "%general.none%", icon: "x", action: (proc: IAppProcess) => Stack.renderer?.unsnapWindow(proc?.pid), }, { sep: true }, { - caption: "Left", + caption: "%apps.contextMenu.system.windowTitlebar.snappingLeft%", icon: "arrow-left", action: (proc: IAppProcess) => Stack.renderer?.snapWindow(proc?.pid, "left"), isActive: (proc: IAppProcess) => proc?.getWindow()?.dataset.snapstate === "left", }, { - caption: "Right", + caption: "%apps.contextMenu.system.windowTitlebar.snappingRight%", icon: "arrow-right", action: (proc: IAppProcess) => Stack.renderer?.snapWindow(proc?.pid, "right"), isActive: (proc: IAppProcess) => proc?.getWindow()?.dataset.snapstate === "right", }, { sep: true }, { - caption: "Top", + caption: "%apps.contextMenu.system.windowTitlebar.snappingTop%", icon: "arrow-up", action: (proc: IAppProcess) => Stack.renderer?.snapWindow(proc?.pid, "top"), isActive: (proc: IAppProcess) => proc?.getWindow()?.dataset.snapstate === "top", }, { - caption: "Bottom", + caption: "%apps.contextMenu.system.windowTitlebar.snappingBottom%", icon: "arrow-down", action: (proc: IAppProcess) => Stack.renderer?.snapWindow(proc?.pid, "bottom"), isActive: (proc: IAppProcess) => proc?.getWindow()?.dataset.snapstate === "bottom", }, { sep: true }, { - caption: "Top Left", + caption: "%apps.contextMenu.system.windowTitlebar.snappingTopLeft%", icon: "arrow-up-left", action: (proc: IAppProcess) => Stack.renderer?.snapWindow(proc?.pid, "top-left"), isActive: (proc: IAppProcess) => proc?.getWindow()?.dataset.snapstate === "top-left", }, { - caption: "Top Right", + caption: "%apps.contextMenu.system.windowTitlebar.snappingTopRight%", icon: "arrow-up-right", action: (proc: IAppProcess) => Stack.renderer?.snapWindow(proc?.pid, "top-right"), isActive: (proc: IAppProcess) => proc?.getWindow()?.dataset.snapstate === "top-right", }, { sep: true }, { - caption: "Bottom Left", + caption: "%apps.contextMenu.system.windowTitlebar.snappingBottomLeft%", icon: "arrow-down-left", action: (proc: IAppProcess) => Stack.renderer?.snapWindow(proc?.pid, "bottom-left"), isActive: (proc: IAppProcess) => proc?.getWindow()?.dataset.snapstate === "bottom-left", }, { - caption: "Bottom Right", + caption: "%apps.contextMenu.system.windowTitlebar.snappingBottomRight%", icon: "arrow-down-right", action: (proc: IAppProcess) => Stack.renderer?.snapWindow(proc?.pid, "bottom-right"), isActive: (proc: IAppProcess) => proc?.getWindow()?.dataset.snapstate === "bottom-right", @@ -111,11 +111,11 @@ export function WindowSystemContextMenu(): AppContextMenu { ], }, { - caption: "Move to workspace", + caption: "%apps.contextMenu.system.windowTitlebar.moveWorkspace%", icon: "rotate-ccw-square", subItems: [ { - caption: "Left workspace", + caption: "%apps.contextMenu.system.windowTitlebar.moveWorkspaceLeft%", icon: "arrow-left", action: (proc: IAppProcess) => { if (!proc?.pid) return; @@ -129,7 +129,7 @@ export function WindowSystemContextMenu(): AppContextMenu { }, }, { - caption: "Right workspace", + caption: "%apps.contextMenu.system.windowTitlebar.moveWorkspaceRight%", icon: "arrow-right", action: (proc: IAppProcess) => { if (!proc?.pid) return; @@ -146,7 +146,7 @@ export function WindowSystemContextMenu(): AppContextMenu { }, { sep: true }, { - caption: "Close", + caption: "%general.close%", action: (proc: IAppProcess) => { proc.closeWindow(); }, diff --git a/src/apps/components/driveinfo/DriveInfo.svelte b/src/apps/components/driveinfo/DriveInfo.svelte index 31f820ac8..f03b4281a 100755 --- a/src/apps/components/driveinfo/DriveInfo.svelte +++ b/src/apps/components/driveinfo/DriveInfo.svelte @@ -40,10 +40,11 @@ {#snippet leftContent()} {#if isUserFs} - (advanced = !advanced)}>{advanced ? "Simple" : "Advanced"} + (advanced = !advanced)}>{advanced ? "%actions.simple%" : "%actions.advanced%"} {/if} {/snippet} {#snippet rightContent()} - process.closeWindow()}>Close + process.closeWindow()}>%general.close% {/snippet} diff --git a/src/apps/components/driveinfo/DriveInfo.ts b/src/apps/components/driveinfo/DriveInfo.ts index 8e4c3c914..f843bd613 100644 --- a/src/apps/components/driveinfo/DriveInfo.ts +++ b/src/apps/components/driveinfo/DriveInfo.ts @@ -5,7 +5,7 @@ import { DriveInfoRuntime } from "./runtime"; const DriveInfoApp: App = { metadata: { - name: "Drive Info", + name: "%apps.DriveInfo._name%", version: "1.0.0", author: "Izaak Kuipers", icon: "DriveIcon", diff --git a/src/apps/components/driveinfo/DriveInfo/AdvancedInfo.svelte b/src/apps/components/driveinfo/DriveInfo/AdvancedInfo.svelte index 4896da8a8..a39166ecb 100755 --- a/src/apps/components/driveinfo/DriveInfo/AdvancedInfo.svelte +++ b/src/apps/components/driveinfo/DriveInfo/AdvancedInfo.svelte @@ -6,25 +6,25 @@
-

ID

+

%advanced.id%

{drive.uuid}

-

Mountpoint

+

%advanced.mountpoint%

{drive.driveLetter || drive.uuid}:/

-

Label

+

%advanced.label%

{drive.label}

-

Identifies As

+

%advanced.identifiesAs%

{drive.IDENTIFIES_AS}

-

Flags

+

%advanced.flags%

- {`${drive.BUSY ? "Busy" : ""} ${drive.FIXED ? "Fixed" : ""} ${drive.READONLY ? "Readonly" : ""} ${drive.REMOVABLE ? "Removable" : ""}`.trim()} + {`${drive.BUSY ? "%advanced.busy%" : ""} ${drive.FIXED ? "%advanced.fixed%" : ""} ${drive.READONLY ? "%advanced.readonly%" : ""} ${drive.REMOVABLE ? "%advanced.removable%" : ""}`.trim()}

diff --git a/src/apps/components/driveinfo/DriveInfo/Quota.svelte b/src/apps/components/driveinfo/DriveInfo/Quota.svelte index 3cd0dbd31..83cf0a100 100755 --- a/src/apps/components/driveinfo/DriveInfo/Quota.svelte +++ b/src/apps/components/driveinfo/DriveInfo/Quota.svelte @@ -12,7 +12,7 @@

- Used ({quota.percentage.toFixed(0)}%) + %quota.used({quota.percentage.toFixed(0)})%

{formatBytes(quota.used)}

@@ -21,7 +21,7 @@

- Free ({(100 - quota.percentage).toFixed(0)}%) + %quota.free({(100 - quota.percentage).toFixed(0)})%

{formatBytes(quota.free)}

diff --git a/src/apps/components/driveinfo/DriveInfo/Usage.svelte b/src/apps/components/driveinfo/DriveInfo/Usage.svelte index 18742cf83..7f655d21f 100755 --- a/src/apps/components/driveinfo/DriveInfo/Usage.svelte +++ b/src/apps/components/driveinfo/DriveInfo/Usage.svelte @@ -18,7 +18,7 @@

- {id.replace(id[0], id[0].toUpperCase())} ({percentage.toFixed(1)}%) + %legend.{id}({percentage.toFixed(1)})%

{formatBytes((usage.sizes as any)[id] || 0)}

diff --git a/src/apps/components/driveinfo/runtime.ts b/src/apps/components/driveinfo/runtime.ts index ec5ec8626..6b23c0579 100755 --- a/src/apps/components/driveinfo/runtime.ts +++ b/src/apps/components/driveinfo/runtime.ts @@ -6,6 +6,7 @@ import { FilesystemDrive } from "$ts/kernel/mods/fs/drives/generic"; import { UserDrive } from "$ts/kernel/mods/fs/drives/userfs"; import type { AppProcessData } from "$types/apps/app"; import type { UserQuota } from "$types/system/fs"; +import type { RenderArgs } from "$types/system/process"; import type { CategorizedDiskUsage } from "$types/user"; export class DriveInfoRuntime extends AppProcess implements IDriveInfoRuntime { @@ -42,5 +43,9 @@ export class DriveInfoRuntime extends AppProcess implements IDriveInfoRuntime { stop(); } + async render(args: RenderArgs) { + this.getBody().setAttribute("data-prefix", "apps.DriveInfo"); + } + //#endregion } diff --git a/src/apps/components/exit/Exit.svelte b/src/apps/components/exit/Exit.svelte index fa88778a2..90ca75172 100755 --- a/src/apps/components/exit/Exit.svelte +++ b/src/apps/components/exit/Exit.svelte @@ -29,8 +29,8 @@
-

Exit ArcOS

-

What's your escape route?

+

%header.title%

+

%header.message%

@@ -48,7 +48,7 @@
- +
diff --git a/src/apps/components/exit/ExitApp.ts b/src/apps/components/exit/ExitApp.ts index 8b57aee18..06432b9d0 100644 --- a/src/apps/components/exit/ExitApp.ts +++ b/src/apps/components/exit/ExitApp.ts @@ -5,7 +5,7 @@ import { ExitRuntime } from "./runtime"; const ExitApp: App = { metadata: { - name: "Exit", + name: "%apps.ExitApp._name%", author: "Izaak Kuipers", version: "6.0.0", icon: "ShutdownIcon", diff --git a/src/apps/components/exit/runtime.ts b/src/apps/components/exit/runtime.ts index 48793f4cf..a8118dc8c 100755 --- a/src/apps/components/exit/runtime.ts +++ b/src/apps/components/exit/runtime.ts @@ -3,6 +3,7 @@ import { AppProcess } from "$ts/apps/process"; import { Daemon } from "$ts/env"; import { Store } from "$ts/writable"; import type { AppProcessData } from "$types/apps/app"; +import type { RenderArgs } from "$types/process"; import { ExitActions } from "./store"; import type { ExitAction } from "./types"; @@ -34,4 +35,8 @@ export class ExitRuntime extends AppProcess implements IExitRuntime { option.alternateAction(Daemon!); // Alternate: when shift key is pressed else option.action(Daemon!); } + + render(args: RenderArgs) { + this.getBody().setAttribute("data-prefix", "apps.ExitApp"); + } } diff --git a/src/apps/components/exit/store.ts b/src/apps/components/exit/store.ts index 1b5a5d18f..bcf6aa8a4 100755 --- a/src/apps/components/exit/store.ts +++ b/src/apps/components/exit/store.ts @@ -4,19 +4,19 @@ import type { ExitAction } from "./types"; export const ExitActions: Record = { restart: { action: (daemon: IUserDaemon) => daemon.power!.restart(), - caption: "Restart", + caption: "%exitActions.restart%", icon: "RestartIcon", }, shutdown: { action: (daemon: IUserDaemon) => daemon.power!.shutdown(), - caption: "Shut down", + caption: "%exitActions.shutdown%", icon: "ShutdownIcon", }, logoff: { action: (daemon: IUserDaemon) => daemon.power!.logoff(), alternateAction: (daemon: IUserDaemon) => daemon.power!.logoffSafeMode(), alternateCaption: "Safe mode", - caption: "Log off", + caption: "%exitActions.logoff%", icon: "LogoutIcon", }, }; diff --git a/src/apps/components/firstrun/ChooseProfilePicture/ChooseProfilePicture.svelte b/src/apps/components/firstrun/ChooseProfilePicture/ChooseProfilePicture.svelte index e085e8e84..b14420823 100755 --- a/src/apps/components/firstrun/ChooseProfilePicture/ChooseProfilePicture.svelte +++ b/src/apps/components/firstrun/ChooseProfilePicture/ChooseProfilePicture.svelte @@ -9,8 +9,8 @@
-

Choose profile picture

-

What do you want to be?

+

%apps.FirstRun.ChooseProfilePicture.title%

+

%apps.FirstRun.ChooseProfilePicture.subtitle%

{#each Object.values(ProfilePictures) as pfp, i}
- +
diff --git a/src/apps/components/firstrun/ChooseProfilePicture/metadata.ts b/src/apps/components/firstrun/ChooseProfilePicture/metadata.ts index 6e4eb0da1..640b8b8a5 100755 --- a/src/apps/components/firstrun/ChooseProfilePicture/metadata.ts +++ b/src/apps/components/firstrun/ChooseProfilePicture/metadata.ts @@ -4,7 +4,7 @@ import ChooseProfilePicture from "./ChooseProfilePicture.svelte"; export const ChooseProfilePictureApp: App = { metadata: { - name: "Choose Profile Picture", + name: "%apps.FirstRun.ChooseProfilePicture._name%", author: "Izaak Kuipers", version: "1.0.0", icon: "AccountIcon", diff --git a/src/apps/components/firstrun/FirstRun.svelte b/src/apps/components/firstrun/FirstRun.svelte index 168adbbff..5c9f556fa 100755 --- a/src/apps/components/firstrun/FirstRun.svelte +++ b/src/apps/components/firstrun/FirstRun.svelte @@ -15,7 +15,7 @@ }); -
+
{#if Component} {:else} @@ -24,7 +24,7 @@
{/if}
-
+
{#each $currentPage?.actions.left as button} + diff --git a/src/apps/components/fsrenameitem/RenameItem.svelte b/src/apps/components/fsrenameitem/RenameItem.svelte index 8f0c6a75f..69b92b3d7 100755 --- a/src/apps/components/fsrenameitem/RenameItem.svelte +++ b/src/apps/components/fsrenameitem/RenameItem.svelte @@ -11,15 +11,15 @@
-

Rename file or folder

-

Enter a new name for the item:

+

%apps.FsRenameItem.title%

+

%apps.FsRenameItem.subtitle%

{#snippet rightContent()} - process.closeWindow()}>Cancel - process.rename()}>Rename + process.closeWindow()}>%general.cancel% + process.rename()}>%apps.FsRenameItem.rename% {/snippet} diff --git a/src/apps/components/globalloadindicator/runtime.ts b/src/apps/components/globalloadindicator/runtime.ts index 1c8a8c774..0967a4c5a 100755 --- a/src/apps/components/globalloadindicator/runtime.ts +++ b/src/apps/components/globalloadindicator/runtime.ts @@ -5,7 +5,7 @@ import type { AppProcessData } from "$types/apps/app"; import type { GlobalLoadIndicatorProgress } from "./types"; export class GlobalLoadIndicatorRuntime extends AppProcess implements IGlobalLoadIndicatorRuntime { - caption = Store("Just a moment..."); + caption = Store("%general.genericStatus%"); progress = Store(); //#region LIFECYCLE diff --git a/src/apps/components/iconeditdialog/IconEditDialog.svelte b/src/apps/components/iconeditdialog/IconEditDialog.svelte index 564345bbe..e14da634d 100644 --- a/src/apps/components/iconeditdialog/IconEditDialog.svelte +++ b/src/apps/components/iconeditdialog/IconEditDialog.svelte @@ -14,7 +14,7 @@
-

Change {iconName}

+

%title({iconName})%

{#if $type === "@fs"} @@ -28,13 +28,13 @@ {#snippet leftContent()} {#if process.defaultIcon} process.default} disabled={`${$type}::${$values[$type]}` === process.defaultIcon}> - Default + %default% {/if} {/snippet} {#snippet rightContent()} - process.closeWindow()}>Cancel - process.save()}>Save + process.closeWindow()}>%general.cancel% + process.save()}>%general.save% {/snippet}
diff --git a/src/apps/components/iconeditdialog/IconEditDialog.ts b/src/apps/components/iconeditdialog/IconEditDialog.ts index 76370a11d..b40ef4980 100644 --- a/src/apps/components/iconeditdialog/IconEditDialog.ts +++ b/src/apps/components/iconeditdialog/IconEditDialog.ts @@ -5,7 +5,7 @@ import { IconEditDialogRuntime } from "./runtime"; export const IconEditDialogApp: App = { metadata: { - name: "Change Icon", + name: "%apps.IconEditDialog._name%", version: "1.0.0", author: "Izaak Kuipers", icon: "ComponentIcon", diff --git a/src/apps/components/iconeditdialog/IconEditDialog/AppType.svelte b/src/apps/components/iconeditdialog/IconEditDialog/AppType.svelte index 2a4265c19..f3a4ebb65 100644 --- a/src/apps/components/iconeditdialog/IconEditDialog/AppType.svelte +++ b/src/apps/components/iconeditdialog/IconEditDialog/AppType.svelte @@ -7,11 +7,11 @@
-

App:

+

%appType.title%

diff --git a/src/apps/components/iconeditdialog/IconEditDialog/BuiltinType.svelte b/src/apps/components/iconeditdialog/IconEditDialog/BuiltinType.svelte index 8f3c29f78..6ce14258e 100644 --- a/src/apps/components/iconeditdialog/IconEditDialog/BuiltinType.svelte +++ b/src/apps/components/iconeditdialog/IconEditDialog/BuiltinType.svelte @@ -16,7 +16,7 @@
-

Icon ID:

+

%builtinType.title%

{#if $values[$type]} @@ -24,6 +24,6 @@ {/if} {$values[$type]}
- +
diff --git a/src/apps/components/iconeditdialog/IconEditDialog/FileType.svelte b/src/apps/components/iconeditdialog/IconEditDialog/FileType.svelte index 8c4c76b2c..1e9b9b548 100644 --- a/src/apps/components/iconeditdialog/IconEditDialog/FileType.svelte +++ b/src/apps/components/iconeditdialog/IconEditDialog/FileType.svelte @@ -9,7 +9,7 @@ async function browse() { const [path] = await Daemon!.files!.LoadSaveDialog({ - title: "Choose an icon to load", + title: "%apps.IconEditDialog.fileType.loadSaveTitle%", extensions: [".svg", ".png", ".jpg", ".bmp", ".gif", ".jpeg"], icon: UploadIcon, startDir: UserPaths.Pictures, @@ -20,9 +20,9 @@
-

File path:

+

%fileType.title%

- +
diff --git a/src/apps/components/iconeditdialog/IconEditDialog/ModeToggle.svelte b/src/apps/components/iconeditdialog/IconEditDialog/ModeToggle.svelte index be2354d5a..ae34c85a4 100644 --- a/src/apps/components/iconeditdialog/IconEditDialog/ModeToggle.svelte +++ b/src/apps/components/iconeditdialog/IconEditDialog/ModeToggle.svelte @@ -6,7 +6,7 @@
- - - + + +
diff --git a/src/apps/components/iconeditdialog/runtime.ts b/src/apps/components/iconeditdialog/runtime.ts index fa86d9ac9..18d2f7675 100644 --- a/src/apps/components/iconeditdialog/runtime.ts +++ b/src/apps/components/iconeditdialog/runtime.ts @@ -57,6 +57,10 @@ export class IconEditDialogRuntime extends AppProcess implements IIconEditDialog this.values.subscribe((v) => this.updateCurrentIcon(this.type(), v)); } + async render() { + this.getBody().setAttribute("data-prefix", "apps.IconEditDialog"); + } + //#endregion async updateCurrentIcon(type: string = this.type(), values: Record = this.values()) { diff --git a/src/apps/components/iconpicker/IconPicker.svelte b/src/apps/components/iconpicker/IconPicker.svelte index 9ca384dcb..ad34eee69 100755 --- a/src/apps/components/iconpicker/IconPicker.svelte +++ b/src/apps/components/iconpicker/IconPicker.svelte @@ -28,12 +28,12 @@ {#snippet leftContent()} ($selected = defaultIcon || "DefaultIcon")} disabled={$selected === defaultIcon}> - Reset + %reset% - process.selectRandom()}>Random + process.selectRandom()}>%random% {/snippet} {#snippet rightContent()} - process.cancel()}>Cancel - process.confirm()} disabled={!$selected}>Choose + process.cancel()}>%general.cancel% + process.confirm()} disabled={!$selected}>%choose% {/snippet} diff --git a/src/apps/components/iconpicker/IconPicker.ts b/src/apps/components/iconpicker/IconPicker.ts index 92a6ab13d..a1ccb29d5 100644 --- a/src/apps/components/iconpicker/IconPicker.ts +++ b/src/apps/components/iconpicker/IconPicker.ts @@ -5,7 +5,7 @@ import { IconPickerRuntime } from "./runtime"; export const IconPickerApp: App = { metadata: { - name: "Icon Picker", + name: "%apps.IconPicker._name%", version: "3.0.0", author: "Izaak Kuipers", icon: "IconLibraryIcon", diff --git a/src/apps/components/iconpicker/IconPicker/Header.svelte b/src/apps/components/iconpicker/IconPicker/Header.svelte index dcae05a1c..1b3c151f2 100755 --- a/src/apps/components/iconpicker/IconPicker/Header.svelte +++ b/src/apps/components/iconpicker/IconPicker/Header.svelte @@ -7,7 +7,7 @@
-

Pick an icon for {forWhat}

+

%title({forWhat})%

{forWhat} diff --git a/src/apps/components/iconpicker/runtime.ts b/src/apps/components/iconpicker/runtime.ts index dd204fe76..75215de27 100755 --- a/src/apps/components/iconpicker/runtime.ts +++ b/src/apps/components/iconpicker/runtime.ts @@ -41,6 +41,10 @@ export class IconPickerRuntime extends AppProcess implements IIconPickerRuntime this.groups = iconService.getGroupedIcons(); } + async render() { + this.getBody().setAttribute("data-prefix", "apps.IconPicker"); + } + //#endregion async confirm() { diff --git a/src/apps/components/iconpicker/store.ts b/src/apps/components/iconpicker/store.ts index eb442f0f6..ea4e4b445 100755 --- a/src/apps/components/iconpicker/store.ts +++ b/src/apps/components/iconpicker/store.ts @@ -1,10 +1,10 @@ export const ICON_GROUP_CAPTIONS = { - Branding: "ArcOS logos", - General: "General icons", - Apps: "Application icons", - Filesystem: "Filesystem-related icons", - Power: "Power icons", - Dialog: "Dialog icons", - Status: "Status indicators", - Mimetypes: "File mimetypes", + Branding: "%groups.Branding%", + General: "%groups.General%", + Apps: "%groups.Apps%", + Filesystem: "%groups.Filesystem%", + Power: "%groups.Power%", + Dialog: "%groups.Dialog%", + Status: "%groups.Status%", + Mimetypes: "%groups.Mimetypes%", }; diff --git a/src/apps/components/iteminfo/ItemInfo.ts b/src/apps/components/iteminfo/ItemInfo.ts index 47a7c4202..878ce28c5 100644 --- a/src/apps/components/iteminfo/ItemInfo.ts +++ b/src/apps/components/iteminfo/ItemInfo.ts @@ -5,7 +5,7 @@ import { ItemInfoRuntime } from "./runtime"; export const ItemInfoApp: App = { metadata: { - name: "Item Info", + name: "%apps.ItemInfo._name%", version: "1.0.0", author: "Izaak Kuipers", icon: "ComponentIcon", diff --git a/src/apps/components/iteminfo/ItemInfo/Actions.svelte b/src/apps/components/iteminfo/ItemInfo/Actions.svelte index cba5e0817..c071d68ee 100755 --- a/src/apps/components/iteminfo/ItemInfo/Actions.svelte +++ b/src/apps/components/iteminfo/ItemInfo/Actions.svelte @@ -14,10 +14,10 @@ process.spawnOverlayApp("ShortcutProperties", process.pid, $info.location.fullPath, process.shortcut())} > - Edit Shortcut... + %actions.editShortcut% {/if} - process.open()}>Open - process.closeWindow()}>Okay + process.open()}>%general.open% + process.closeWindow()}>%general.okay% {/snippet} diff --git a/src/apps/components/iteminfo/ItemInfo/Header.svelte b/src/apps/components/iteminfo/ItemInfo/Header.svelte index 56d9fac7c..62c84e479 100755 --- a/src/apps/components/iteminfo/ItemInfo/Header.svelte +++ b/src/apps/components/iteminfo/ItemInfo/Header.svelte @@ -21,10 +21,10 @@

{$info.name || $info.location.parent || $info.location.drive}

{#if $info.name && ($info.location.parent || $info.location.drive)} -

in {$info.location.parent || $info.location.drive}

+

%subtitle({$info.location.parent || $info.location.drive})%

{/if}
{#if $info.name} - + {/if}
diff --git a/src/apps/components/iteminfo/ItemInfo/Location.svelte b/src/apps/components/iteminfo/ItemInfo/Location.svelte index 062ba2a1e..b1a24d8fe 100755 --- a/src/apps/components/iteminfo/ItemInfo/Location.svelte +++ b/src/apps/components/iteminfo/ItemInfo/Location.svelte @@ -10,21 +10,21 @@ - + {$info.location.fullPath} - + {$info.location.extension ? "." + $info.location.extension : "-"} - + {$info.location.parent || "-"} - + {$info.location.drive || "-"} - + {$info.location.driveFs || "GFS"} diff --git a/src/apps/components/iteminfo/ItemInfo/Meta.svelte b/src/apps/components/iteminfo/ItemInfo/Meta.svelte index dda82f9d5..0646f27ba 100755 --- a/src/apps/components/iteminfo/ItemInfo/Meta.svelte +++ b/src/apps/components/iteminfo/ItemInfo/Meta.svelte @@ -24,21 +24,21 @@ - + {$info.name ? $info.meta.sort : "drive"} - + {$info.meta.mimetype || "-"} - + {$info.meta.size ? formatBytes($info.meta.size) : "-"} - + {created} - + {modified} diff --git a/src/apps/components/iteminfo/runtime.ts b/src/apps/components/iteminfo/runtime.ts index 8028e0015..99b85f658 100755 --- a/src/apps/components/iteminfo/runtime.ts +++ b/src/apps/components/iteminfo/runtime.ts @@ -29,6 +29,8 @@ export class ItemInfoRuntime extends AppProcess implements IItemInfoRuntime { } async render({ path, file }: RenderArgs) { + this.getBody().setAttribute("data-prefix", "apps.ItemInfo"); + file = file as FileEntry | FolderEntry; try { diff --git a/src/apps/components/messagecomposer/MessageComposer.ts b/src/apps/components/messagecomposer/MessageComposer.ts index a4b8d29db..5b1d70746 100644 --- a/src/apps/components/messagecomposer/MessageComposer.ts +++ b/src/apps/components/messagecomposer/MessageComposer.ts @@ -5,7 +5,7 @@ import { MessageComposerRuntime } from "./runtime"; export const MessageComposerApp: App = { metadata: { - name: "New Message", + name: "%apps.MessageComposer._name%", author: "Izaak Kuipers", version: "1.0.0", icon: "MessagingIcon", diff --git a/src/apps/components/messagecomposer/MessageComposer/ActionBar.svelte b/src/apps/components/messagecomposer/MessageComposer/ActionBar.svelte index 6beb47617..2438a1d5e 100755 --- a/src/apps/components/messagecomposer/MessageComposer/ActionBar.svelte +++ b/src/apps/components/messagecomposer/MessageComposer/ActionBar.svelte @@ -13,21 +13,25 @@ {#snippet leftContent()} - {formatBytes($body.length)} - {formatBytes($attachments.map((a) => a.data.size).reduce((a, b) => a + b, 0))} + {formatBytes($body.length)} + {formatBytes($attachments.map((a) => a.data.size).reduce((a, b) => a + b, 0))} {/snippet} {#snippet rightContent()} - process.addAttachment()} /> + process.addAttachment()} + /> process.discard()} - className="discard-button" - /> - process.send()}> - Send - + > + process.send()} + >%actionBar.send% {/snippet} diff --git a/src/apps/components/messagecomposer/MessageComposer/AttachmentBar.svelte b/src/apps/components/messagecomposer/MessageComposer/AttachmentBar.svelte index a544bf765..264416d36 100755 --- a/src/apps/components/messagecomposer/MessageComposer/AttachmentBar.svelte +++ b/src/apps/components/messagecomposer/MessageComposer/AttachmentBar.svelte @@ -17,10 +17,10 @@ ({formatBytes(attachment.data.size)})
diff --git a/src/apps/components/messagecomposer/MessageComposer/SubjectField.svelte b/src/apps/components/messagecomposer/MessageComposer/SubjectField.svelte index 836151f13..4712a3953 100755 --- a/src/apps/components/messagecomposer/MessageComposer/SubjectField.svelte +++ b/src/apps/components/messagecomposer/MessageComposer/SubjectField.svelte @@ -6,7 +6,7 @@
-

Subject:

+

%subjectField.subject%

diff --git a/src/apps/components/messagecomposer/MessageComposer/ToField.svelte b/src/apps/components/messagecomposer/MessageComposer/ToField.svelte index 81b3b5111..605d66d7f 100755 --- a/src/apps/components/messagecomposer/MessageComposer/ToField.svelte +++ b/src/apps/components/messagecomposer/MessageComposer/ToField.svelte @@ -27,11 +27,11 @@
-

To:

+

%toField.to%

{#each $recipients as recipient (recipient)} {/each} - +
diff --git a/src/apps/components/messagecomposer/runtime.ts b/src/apps/components/messagecomposer/runtime.ts index 39919cfe5..f8672a8bf 100755 --- a/src/apps/components/messagecomposer/runtime.ts +++ b/src/apps/components/messagecomposer/runtime.ts @@ -40,6 +40,10 @@ export class MessageComposerRuntime extends AppProcess implements IMessageCompos this.setSource(__SOURCE__); } + async render() { + this.getBody().setAttribute("data-prefix", "apps.MessageComposer"); + } + //#endregion //#region SENDING @@ -51,8 +55,8 @@ export class MessageComposerRuntime extends AppProcess implements IMessageCompos const prog = await Daemon?.files!.FileProgress( { type: "none", - caption: "Sending message", - subtitle: "Preparing...", + caption: "%apps.MessageComposer.sendProg.caption%", + subtitle: "%apps.MessageComposer.sendProg.subtitleInitial%", icon: "MessagingIcon", }, this.pid @@ -69,7 +73,7 @@ export class MessageComposerRuntime extends AppProcess implements IMessageCompos prog?.setType("size"); prog?.setMax(progress.max); prog?.setDone(progress.value); - prog?.updSub("Uploading..."); + prog?.updSub("%apps.MessageComposer.sendProg.subtitleUploading%"); } ); @@ -87,12 +91,12 @@ export class MessageComposerRuntime extends AppProcess implements IMessageCompos if (!this.isModified()) return this.closeWindow(); MessageBox( { - title: "Discard message?", - message: "Are you sure you want to discard this message? This cannot be undone.", + title: "%apps.MessageComposer.discardMessage.title%", + message: "%apps.MessageComposer.discardMessage.title%", buttons: [ - { caption: "Cancel", action: () => {} }, + { caption: "%general.cancel%", action: () => {} }, { - caption: "Discard", + caption: "%general.discard%", action: () => { this.closeWindow(); }, @@ -116,8 +120,8 @@ export class MessageComposerRuntime extends AppProcess implements IMessageCompos this.sending.set(false); MessageBox( { - title: "Failed to send message", - message: `The message couldn't be sent${errorMessage}`, +title: "%apps.MessageComposer.sendFailed.title%", + message: `%apps.MessageComposer.sendFailed.message% ${errorMessage}`, image: "WarningIcon", sound: "arcos.dialog.warning", buttons: [BTN_OKAY_SUG], @@ -135,7 +139,7 @@ export class MessageComposerRuntime extends AppProcess implements IMessageCompos const attachments: Attachment[] = []; const paths = await Daemon!.files!.LoadSaveDialog({ - title: "Choose one or more files to attach", + title: "%apps.MessageComposer.addAttachment.title%", icon: "UploadIcon", startDir: UserPaths.Documents, multiple: true, @@ -145,7 +149,7 @@ export class MessageComposerRuntime extends AppProcess implements IMessageCompos { max: 100, type: "none", - caption: "Just a moment...", + caption: "%general.genericStatus%", icon: "MemoryIcon", }, this.pid @@ -159,7 +163,11 @@ export class MessageComposerRuntime extends AppProcess implements IMessageCompos try { const contents = await Fs.readFile(path, (progress) => { prog.show(); - prog.updateCaption(progress.what ? `Reading ${progress.what}` : "Reading file..."); + prog.updateCaption( + progress.what + ? `%apps.MessageComposer.fileProgress.readingFileKnown(${progress.what})%` + : "%apps.MessageComposer.fileProgress.readingFileUnknown%" + ); prog.updSub(path); prog.setType("size"); prog.setDone(0); diff --git a/src/apps/components/multiupdategui/MultiUpdateGui.svelte b/src/apps/components/multiupdategui/MultiUpdateGui.svelte index 387e55133..2bffa266f 100755 --- a/src/apps/components/multiupdategui/MultiUpdateGui.svelte +++ b/src/apps/components/multiupdategui/MultiUpdateGui.svelte @@ -25,33 +25,33 @@

{#if $working} {#if $currentPackage} - Updating {$currentPackage.pkg.name} + %title.updating({$currentPackage.pkg.name})% {:else} - Just a moment + %title.loading% {/if} {:else if $done} - Finished updating + %title.done% {:else} - Ready to update + %title.ready% {/if}

{#if $working} {#if $currentPackage && $currentPackage.user} - By + %generic.by% {:else} - Loading... + %generic.loading% {/if} {:else if $done} {#if $errored.length} {$errored.length} {Plural("error", $errored.length)} occurred {:else} - All packages were updated. + %allPackagesUpdated% {/if} {:else} - Click Update to begin + %clickUpdate% {/if}

@@ -82,13 +82,13 @@

{#if item.type === "file"} - Writing file + %itemType.file% {:else if item.type === "mkdir"} - Creating directory + %itemType.mkdir% {:else if item.type === "registration"} - Registering + %itemType.registration% {:else} - Status + %itemType.generic% {/if}

{item.content}

@@ -100,10 +100,10 @@
{#if $showLog}%hideLog%{:else}%showLog%{/if} - + {#if $done}%finish%{:else}%startUpdate%{/if}
diff --git a/src/apps/components/multiupdategui/MultiUpdateGui.ts b/src/apps/components/multiupdategui/MultiUpdateGui.ts index dabfe4dae..7ebdf0d40 100644 --- a/src/apps/components/multiupdategui/MultiUpdateGui.ts +++ b/src/apps/components/multiupdategui/MultiUpdateGui.ts @@ -5,7 +5,7 @@ import { MultiUpdateGuiRuntime } from "./runtime"; export const MultiUpdateGuiApp: App = { metadata: { - name: "App Updater", + name: "%apps.MultiUpdateGui._name%", author: "Izaak Kuipers", version: "1.0.0", icon: "UpdateIcon", diff --git a/src/apps/components/multiupdategui/runtime.ts b/src/apps/components/multiupdategui/runtime.ts index b840c8bba..5adbf017a 100755 --- a/src/apps/components/multiupdategui/runtime.ts +++ b/src/apps/components/multiupdategui/runtime.ts @@ -52,6 +52,8 @@ export class MultiUpdateGuiRuntime extends AppProcess implements IMultiUpdateGui } async render() { + this.getBody().setAttribute("data-prefix", "apps.MultiUpdateGui"); + this.win = this.getWindow(); if (this.updates.length > 15) { @@ -95,7 +97,7 @@ export class MultiUpdateGuiRuntime extends AppProcess implements IMultiUpdateGui this.working.set(true); const elevated = await Daemon!.elevation!.manuallyElevate({ - what: `ArcOS needs your permission to update ${this.updates.length} ${Plural("app", this.updates.length)}.`, + what: `%apps.MultiUpdateGui.elevation(${this.updates.length}::${Plural("app", this.updates.length)})%`, title: this.app.data.metadata.name, description: this.app.data.metadata.author, image: "UpdateIcon", diff --git a/src/apps/core/bootscreen/bootScreen.ts b/src/apps/core/bootscreen/bootScreen.ts index 1853d8a90..b59856eb4 100644 --- a/src/apps/core/bootscreen/bootScreen.ts +++ b/src/apps/core/bootscreen/bootScreen.ts @@ -5,7 +5,7 @@ import { BootScreenRuntime } from "./runtime"; export const BootScreen: App = { metadata: { - name: "Boot App", + name: "%apps.bootScreen._name%", author: "Izaak Kuipers", version: "9.0.0", icon: "ComponentIcon", diff --git a/src/apps/core/bootscreen/runtime.ts b/src/apps/core/bootscreen/runtime.ts index 1684bec6f..04def8b1b 100755 --- a/src/apps/core/bootscreen/runtime.ts +++ b/src/apps/core/bootscreen/runtime.ts @@ -22,7 +22,7 @@ export class BootScreenRuntime extends AppProcess implements IBootScreenRuntime async begin() { this.Log("Initializing boot"); - this.status.set("Press a key or click to start"); + this.status.set("%apps.bootScreen.pressAnyKey%"); document.addEventListener("click", () => this.startBooting(), { once: true, @@ -43,14 +43,14 @@ export class BootScreenRuntime extends AppProcess implements IBootScreenRuntime this.progress.set(true); if (e?.key === "F8") { - this.status.set("Entering Safe Mode"); + this.status.set("%apps.bootScreen.safeMode%"); await Sleep(2000); State?.loadState("login", { safeMode: true }); } else if (e?.key === "F4") { this.status.set("Opening server selector"); State?.loadState("switchServer", { safeMode: true }); } else if (e?.key.toLowerCase() === "a") { - this.status.set("Starting ArcTerm"); + this.status.set("%apps.bootScreen.arcTerm%"); State?.loadState("arcterm"); } else { this.status.set(" "); diff --git a/src/apps/core/initialsetup/InitialSetup.svelte b/src/apps/core/initialsetup/InitialSetup.svelte index e5faa5193..a2c04b71a 100755 --- a/src/apps/core/initialsetup/InitialSetup.svelte +++ b/src/apps/core/initialsetup/InitialSetup.svelte @@ -26,7 +26,7 @@
-
+
{#if PageComponent} {/if} diff --git a/src/apps/core/initialsetup/InitialSetup/Page/CheckInbox.svelte b/src/apps/core/initialsetup/InitialSetup/Page/CheckInbox.svelte index 85bbdbd0b..2fc51e5d3 100755 --- a/src/apps/core/initialsetup/InitialSetup/Page/CheckInbox.svelte +++ b/src/apps/core/initialsetup/InitialSetup/Page/CheckInbox.svelte @@ -4,9 +4,7 @@
-

Check your inbox

-

You've got mail!

-

- We sent you a link to activate your account. Open it to continue. Be sure to check your spam if you can’t find it. -

+

%checkInbox.title%

+

%checkInbox.subtitle%

+

%checkInbox.message%

diff --git a/src/apps/core/initialsetup/InitialSetup/Page/Finish.svelte b/src/apps/core/initialsetup/InitialSetup/Page/Finish.svelte index 7cf2fa97a..d26fbb42c 100755 --- a/src/apps/core/initialsetup/InitialSetup/Page/Finish.svelte +++ b/src/apps/core/initialsetup/InitialSetup/Page/Finish.svelte @@ -4,9 +4,7 @@
-

All finished!

-

You're all set.

-

- Your account has been set up successfully. Click Finish to start using ArcOS. -

+

%finish.title%

+

%finish.subtitle%

+

%finish.message%

diff --git a/src/apps/core/initialsetup/InitialSetup/Page/FreshDeployment.svelte b/src/apps/core/initialsetup/InitialSetup/Page/FreshDeployment.svelte index 612870831..67a731530 100644 --- a/src/apps/core/initialsetup/InitialSetup/Page/FreshDeployment.svelte +++ b/src/apps/core/initialsetup/InitialSetup/Page/FreshDeployment.svelte @@ -5,19 +5,12 @@
-

Fresh deployment

-

- It appears this ReArc deployment is fresh. Before continuing, please make sure that that's supposed to be the case. If it is, - click Next. The user you're about to create is considered user #0, and will be set up as a God Admin by default. The - owner of this account is then automatically seen as the owner of the server. Also, please check the server configuration: -

+

%freshDeployment.title%

+

%freshDeployment.message%

    {#each Object.entries(process.server?.serverInfo!) as [key, value]}
  • {key}: {JSON.stringify(value)}
  • {/each}
-

- Note: disableRegistration is ignored for the first user. This configuration option will be retained once the - first user has been created. -

+

%freshDeployment.note%

diff --git a/src/apps/core/initialsetup/InitialSetup/Page/Identity.svelte b/src/apps/core/initialsetup/InitialSetup/Page/Identity.svelte index c32a7700c..2bb4ebdbf 100755 --- a/src/apps/core/initialsetup/InitialSetup/Page/Identity.svelte +++ b/src/apps/core/initialsetup/InitialSetup/Page/Identity.svelte @@ -121,9 +121,13 @@
-

Your ArcOS Identity

-

We'll use this information to create your ArcOS account.

+

%identity.title%

+

%identity.subtitle%

+
+

%identity.fields.displayName%

+ +

Username

{#if usernameTaken && emailTaken} -

Username and email address are both taken!

+

%identity.errors.usernameAndEmailTaken%

{:else if usernameTaken} -

Username is already taken!

+

%identity.errors.usernameTaken%

{:else if emailTaken} -

Email is already taken!

+

%identity.errors.emailTaken%

{/if} {#if emailInvalid && usernameInvalid} -

Username and email address are both invalid!

+

%identity.errors.usernameAndEmailInvalid%

{:else if usernameInvalid} -

Username is invalid!

+

%identity.errors.usernameInvalid%

{:else if emailInvalid} -

Email address is invalid!

+

%identity.errors.emailInvalid%

{/if} {#if passwordInvalid} -

- Password is {PasswordStrengthCaptions[passwordStrength]}! -

+

%identity.errors.password{passwordStrength}%

{/if}

diff --git a/src/apps/core/initialsetup/InitialSetup/Page/License.svelte b/src/apps/core/initialsetup/InitialSetup/Page/License.svelte index 9bc150097..5ffd1fa71 100755 --- a/src/apps/core/initialsetup/InitialSetup/Page/License.svelte +++ b/src/apps/core/initialsetup/InitialSetup/Page/License.svelte @@ -4,7 +4,7 @@

-

License Agreement

-

Scary stuff, I know

-

By using ArcOS, you agree to the GPLv3 license.

+

%license.title%

+

%license.subtitle%

+

%license.message%

diff --git a/src/apps/core/initialsetup/InitialSetup/Page/Welcome.svelte b/src/apps/core/initialsetup/InitialSetup/Page/Welcome.svelte index 33f442b4e..aebe89b30 100755 --- a/src/apps/core/initialsetup/InitialSetup/Page/Welcome.svelte +++ b/src/apps/core/initialsetup/InitialSetup/Page/Welcome.svelte @@ -4,10 +4,7 @@
-

Welcome to ArcOS!

-

We're happy to have you

-

- Let's get you an ArcOS account. Click Next to get started, or - Cancel to go back to the login screen. -

+

%welcome.title%

+

%welcome.subtitle%

+

%welcome.message%

diff --git a/src/apps/core/initialsetup/initialSetupWizard.ts b/src/apps/core/initialsetup/initialSetupWizard.ts index d36d78e89..393a59390 100644 --- a/src/apps/core/initialsetup/initialSetupWizard.ts +++ b/src/apps/core/initialsetup/initialSetupWizard.ts @@ -6,7 +6,7 @@ import { InitialSetupRuntime } from "./runtime"; export const InitialSetupWizard: App = { metadata: { - name: "Initial Setup Wizard", + name: "%apps.initialSetupWizard._name%", author: "Izaak Kuipers", version: "7.0.0", icon: WaveIcon, diff --git a/src/apps/core/initialsetup/runtime.ts b/src/apps/core/initialsetup/runtime.ts index be1770b08..b204ad82a 100755 --- a/src/apps/core/initialsetup/runtime.ts +++ b/src/apps/core/initialsetup/runtime.ts @@ -37,7 +37,7 @@ export class InitialSetupRuntime extends AppProcess implements IInitialSetupRunt public readonly pageButtons: PageButtons = [ { left: { - caption: "Cancel", + caption: "%general.cancel%", action: async () => { State?.loadState("login"); }, @@ -45,37 +45,37 @@ export class InitialSetupRuntime extends AppProcess implements IInitialSetupRunt }, previous: { disabled: () => true, - caption: "Previous", + caption: "%general.previous%", to: 0, }, next: { suggested: true, - caption: "Next", + caption: "%general.next%", to: 1, }, }, { left: { - caption: "View License", + caption: "%apps.initialSetupWizard.buttons.viewLicense%", action: () => this.viewLicense(), }, previous: { - caption: "Previous", + caption: "%general.previous%", to: 0, }, next: { - caption: "I agree", + caption: "%general.iAgree%", suggested: true, action: () => this.licenseConfirmation(), }, }, { previous: { - caption: "Previous", + caption: "%general.previous%", to: 1, }, next: { - caption: "Continue", + caption: "%general.continue%", disabled: () => !this.identityInfoValid(), action: () => this.createAccount(), suggested: true, @@ -85,10 +85,10 @@ export class InitialSetupRuntime extends AppProcess implements IInitialSetupRunt previous: { disabled: () => true, to: 3, - caption: "Previous", + caption: "%general.previous%", }, next: { - caption: "I clicked it", + caption: "%apps.initialSetupWizard.buttons.iClickedIt%", suggested: true, action: () => this.checkAccountActivation(), }, @@ -97,25 +97,25 @@ export class InitialSetupRuntime extends AppProcess implements IInitialSetupRunt previous: { disabled: () => true, to: 4, - caption: "Previous", + caption: "%general.previous%", }, next: { - caption: "Let's begin", + caption: "%apps.initialSetupWizard.buttons.letsBegin%", action: () => this.finish(), suggested: true, }, }, { left: { - caption: "Cancel", + caption: "%general.cancel%", disabled: () => true, }, previous: { - caption: "Previous", + caption: "%general.previous%", disabled: () => true, }, next: { - caption: "Server's all good", + caption: "%apps.initialSetupWizard.serverAllGood%", to: 0, suggested: true, }, @@ -178,18 +178,17 @@ export class InitialSetupRuntime extends AppProcess implements IInitialSetupRunt MessageBox( { - title: "Just making sure...", - message: - "By using ArcOS, you agree to the License Agreement. You may not violate any of the rules contained within this license. Continue?", + title: "%apps.initialSetupWizard.licenseConfirmation.title%", + message: "%apps.initialSetupWizard.licenseConfirmation.message%", buttons: [ { - caption: "Decline", + caption: "%general.decline%", action: () => { this.actionsDisabled.set(false); }, }, { - caption: "I agree", + caption: "%general.iAgree%", suggested: true, action: () => { this.pageNumber.set(this.pageNumber() + 1); @@ -209,19 +208,17 @@ export class InitialSetupRuntime extends AppProcess implements IInitialSetupRunt MessageBox( { image: SecurityMediumIcon, - title: "ArcOS License - GPLv3", - message: `By using ArcOS, you agree to the GPLv3 License contained within: ${htmlspecialchars( - ArcLicense() - )}`, + title: "%apps.initialSetupWizard.viewLicense.title%", + message: `%apps.initialSetupWizard.viewLicense.message%: ${htmlspecialchars(ArcLicense())}`, buttons: [ { - caption: "Decline", + caption: "%general.decline%", action: () => { State?.loadState("licenseDeclined"); }, }, { - caption: "I agree", + caption: "%general.iAgree%", action: () => { this.actionsDisabled.set(false); }, @@ -249,12 +246,12 @@ export class InitialSetupRuntime extends AppProcess implements IInitialSetupRunt MessageBox( { image: WarningIcon, - title: "You made a typo!", - message: "The passwords you entered don't match. Please re-enter them, and then try again.", + title: "%apps.initialSetupWizard.createAccount.passwordMismatch.title%", + message: "%apps.initialSetupWizard.createAccount.passwordMismatch.message%", sound: "arcos.dialog.warning", buttons: [ { - caption: "Okay", + caption: "%general.okay%", suggested: true, action: () => { this.actionsDisabled.set(false); @@ -317,12 +314,11 @@ export class InitialSetupRuntime extends AppProcess implements IInitialSetupRunt MessageBox( { image: ErrorIcon, - title: "Something went wrong", - message: - "An error occurred while creating your account. We might be experiencing some technical difficulties, please try again later.", + title: "%apps.initialSetupWizard.createAccount.genericError.title%", + message: "%apps.initialSetupWizard.createAccount.genericError.message%", buttons: [ { - caption: "Okay", + caption: "%generic.okay%", suggested: true, action: () => { this.actionsDisabled.set(false); @@ -348,11 +344,11 @@ export class InitialSetupRuntime extends AppProcess implements IInitialSetupRunt if (!tokenResult.success) { MessageBox( { - title: "Did you click the link?", - message: `Our systems tell me that your account hasn't been activated yet. Are you sure you clicked the link? If you did, and you're still seeing this, please contact support.

Details: ${tokenResult.errorMessage ?? "Unknown error"}`, + title: "%apps.initialSetupWizard.checkAccountActivationError.title%", + message: "%apps.initialSetupWizard.checkAccountActivationError.message%", buttons: [ { - caption: "Okay", + caption: "%generic.okay%", action: () => { this.actionsDisabled.set(false); }, diff --git a/src/apps/core/loginapp/ErrorMessage.svelte b/src/apps/core/loginapp/ErrorMessage.svelte index 449efb091..30c821139 100755 --- a/src/apps/core/loginapp/ErrorMessage.svelte +++ b/src/apps/core/loginapp/ErrorMessage.svelte @@ -7,4 +7,4 @@

{@html $errorMessage}

- + diff --git a/src/apps/core/loginapp/Loading.svelte b/src/apps/core/loginapp/Loading.svelte index 942f33afe..84d903d4f 100755 --- a/src/apps/core/loginapp/Loading.svelte +++ b/src/apps/core/loginapp/Loading.svelte @@ -7,5 +7,5 @@

- {@html $loadingStatus} + {$loadingStatus}

diff --git a/src/apps/core/loginapp/LoginForm.svelte b/src/apps/core/loginapp/LoginForm.svelte index afe7e0e0d..a6b7fd7e8 100755 --- a/src/apps/core/loginapp/LoginForm.svelte +++ b/src/apps/core/loginapp/LoginForm.svelte @@ -17,9 +17,9 @@ {#if $persistence} - + {:else if !$serverInfo?.disableRegistration} - + {/if} diff --git a/src/apps/core/loginapp/loginApp.ts b/src/apps/core/loginapp/loginApp.ts index 2ca7a12de..8b6aacd9e 100644 --- a/src/apps/core/loginapp/loginApp.ts +++ b/src/apps/core/loginapp/loginApp.ts @@ -5,7 +5,7 @@ import { LoginAppRuntime } from "./runtime"; export const LoginApp: App = { metadata: { - name: "LogonUI", + name: "%apps.loginApp._name%", author: "Izaak Kuipers", version: "9.0.0", icon: "PasswordIcon", diff --git a/src/apps/user/filemanager/runtime.ts b/src/apps/user/filemanager/runtime.ts index 8b93b7704..3b8944b62 100755 --- a/src/apps/user/filemanager/runtime.ts +++ b/src/apps/user/filemanager/runtime.ts @@ -41,12 +41,12 @@ export class FileManagerRuntime extends AppProcess implements IFileManagerRuntim directoryListing = Store(); virtualLocations: Record = { my_arcos: { - name: "My ArcOS", + name: "%virtualLocations.my_arcos%", icon: "computer", component: MyArcOs as any, }, recycle_bin: { - name: "Recycle Bin", + name: "%virtualLocations.recycle_bin%", icon: "trash-2", component: TrashCan as any, }, diff --git a/src/lang/en.json b/src/lang/en.json new file mode 100644 index 000000000..5b014791f --- /dev/null +++ b/src/lang/en.json @@ -0,0 +1,649 @@ +{ + "apps": { + "bootScreen": { + "_name": "Boot screen", + "pressAnyKey": "Press any key or click to start", + "safeMode": "Entering Safe Mode", + "arcTerm": "Starting ArcTerm" + }, + "initialSetupWizard": { + "_name": "Initial Setup Wizard", + "buttons": { + "viewLicense": "View License", + "iAgree": "I agree", + "iClickedIt": "I clicked it", + "letsBegin": "Let's begin", + "serverAllGood": "Server's all good" + }, + "licenseConfirmation": { + "title": "Just making sure...", + "message": "By using ArcOS, you agree to the License Agreement. You may not violate any of the rules contained within this license. Continue?" + }, + "viewLicense": { + "title": "ArcOS License - GPLv3", + "message": "By using ArcOS, you agree to the GPLv3 License contained within" + }, + "createAccount": { + "passwordMismatch": { + "title": "You made a typo!", + "message": "The passwords you entered don't match. Please re-enter them, and then try again." + }, + "genericError": { + "title": "Something went wrong", + "message": "An error occured while creating your account. We might be experiencing some technical difficulties, please try again later." + } + }, + "checkAccountActivationError": { + "title": "Did you click the link?", + "message": "Our systems tell me that your account hasn't been activated yet. Are you sure you clicked the link? If you did, and you're still seeing this, please contact support." + }, + "page": { + "checkInbox": { + "title": "Check your inbox", + "subtitle": "You've got mail!", + "message": "We sent you a link to activate your account. Open it to continue. Be sure to check your spam if you can’t find it." + }, + "finish": { + "title": "All finished!", + "subtitle": "You're all set.", + "message": "Your account has been set up successfully. Click Finish to start using ArcOS." + }, + "freshDeployment": { + "title": "Fresh deployment", + "message": "It appears this ReArc deployment is fresh. Before continuing, please make sure that that's supposed to be the case. If it is, click Next. The user you're about to create is considered user #0, and will be set up as a God Admin by default. The owner of this account is then automatically seen as the owner of the server. Also, please check the server configuration:", + "note": "Note: disableRegistration is ignored for the first user. This configuration option will be retained once the first user has been created." + }, + "identity": { + "title": "Your ArcOS Identity", + "subtitle": "We'll use this information to create your ArcOS account.", + "fields": { + "displayName": "Display Name", + "username": "Username", + "emailAddress": "Email address *", + "password": "Password", + "passwordConfirm": "Confirm" + }, + "errors": { + "usernameAndEmailTaken": "Username and email address are both taken!", + "usernameTaken": "Username is already taken!", + "emailTaken": "Email is already taken!", + "emailAndUsernameInvalid": "Username and email address are both invalid!", + "usernameInvalid": "Username is invalid!", + "emailInvalid": "Email address is invalid!", + "passwordtooWeak": "Password is too weak!", + "passwordweak": "Password is weak!", + "passwordmedium": "Password is medium!", + "passwordstrong": "Password is strong!" + }, + "disclaimer": "* You will receive an email with a link to activate your account. Your display name, username and password can be changed later on. To change your email, contact an administrator." + }, + "license": { + "title": "License Agreement", + "subtitle": "Scary stuff, I know", + "message": "By using ArcOS, you agree to the GPLv3 license." + }, + "welcome": { + "title": "Welcome", + "subtitle": "We're happy to have you", + "message": "Let's get you an ArcOS account. Click Next to get started, or Cancel to go back to the login screen." + } + } + }, + "loginApp": { + "_name": "LogonUI", + "welcomeString": { + "night": "Hi, go to sleep", + "morning": "Good morning", + "afternoon": "Good afternoon", + "evening": "Good evening" + }, + "errors": { + "noDaemon": "Failed to start user daemon", + "noUserInfo": "Failed to request user info", + "totpInvalid": "You didn't enter a valid 2FA code!", + "credentialIncorrect": "Username or password incorrect." + }, + "startDaemon": { + "savingToken": "Saving token", + "loadingSettings": "Loading your settings", + "request2fa": "Requesting 2FA", + "startingFilesystem": "Starting filesystem", + "startingSync": "Starting synchronization", + "profileCustomization": "Reading profile customization", + "notifyLoginActivity": "Notifying login activity", + "startServiceHost": { + "initial": "Starting service host", + "specific": "Starting {{0}}" + }, + "loadingApps": { + "initial": "Loading apps...", + "specific": "Loaded {{0}}" + }, + "checkingAssoc": "Checking associations", + "globalDispatch": "Connecting global dispatch", + "welcome": "Welcome to ArcOS", + "driveNotifierWatcher": "Starting drive notifier watcher", + "shareManagement": "Starting share management", + "adminBootstrapper": "Activating admin bootstrapper", + "statusRefresh": "Starting status refresh", + "letsGo": "Let's go!", + "startingWorkspaces": "Starting workspaces", + "autoload": "Spawning autorun" + }, + "exit": { + "logoff": "Goodbye, ", + "shutdown": "Shutting down...", + "restart": "Restarting..." + }, + "loginForm": { + "switchUser": "Switch user", + "noAccount": "No account?", + "username": "Username", + "password": "Password" + } + }, + "AcceleratorOverview": { + "header": { + "title": "Keyboard shortcuts", + "subtitle": "Get more work done faster with these handy shortcuts!" + } + }, + "AppInfo": { + "_name": "App Info", + "noTargetApp": { + "title": "App not found", + "message": "AppInfo couldn't find any information about \"{{0}}\" Is it installed?" + }, + "killAll": { + "what": "ArcOS needs your permission to kill all instances of an app" + }, + "header": { + "isDisabled": "{{0}} is disabled!", + "deleteApp": "Delete app", + "launch": "Launch" + }, + "indepthInfo": { + "size": "Size", + "minSize": "Minimal Size", + "maxSize": "Maximal Size", + "controls": "Controls", + "initialPosition": "Initial Position", + "origin": "Origin", + "core": "Core", + "hidden": "Hidden", + "centered": "Centered", + "cornerOfScreen": "Corner of screen" + }, + "processInfo": { + "processes": "Processes", + "instances": "{{0}} instance(s)", + "firstPid": "First PID" + }, + "thirdPartyInfo": { + "thirdParty": "Third-party", + "signatures": "Signatures", + "hasProcess": "Has process", + "workingDirectory": "Working directory", + "entryPoint": "Entry point" + }, + "internalInfo": { + "loadTime": "Load time", + "systemVersion": "System version", + "minimalAssembly": "Minimal assembly", + "codePath": "Code path" + }, + "actions": { + "killAll": "Kill all" + } + }, + "AppInstaller": { + "noDistrib": { + "title": "Can't install package", + "message": "The Distribution Service isn't running anymore. Please restart ArcOS to fix this problem." + }, + "noEnableThirdParty": { + "title": "Can't install app", + "message": "Third-party apps aren't enabled on your account. Please enable third-party apps in the Settings app to install this app.", + "takeMeThere": "Take me there" + }, + "rollback": "Rolling back changes...", + "header": { + "title": { + "installed": "Package installed!", + "installing": "Installing package...", + "failed": "Installation failed" + }, + "subtitle": { + "completed": "Click Open now to launch the app", + "installing": "{{0}} by {{1}}", + "generic": "{{0}} - {{1}}" + } + }, + "actions": { + "revert": "Revert", + "openNow": "Open now", + "done": "Done", + "install": "Install" + }, + "readyToInstall": { + "title": "Ready to install", + "message": "Click Install to install this package." + }, + "logType": { + "file": "Writing file", + "mkdir": "Creating directory", + "registration": "Registering", + "generic": "Status" + } + }, + "AppPreInstall": { + "noEnableThirdParty": { + "title": "Can't install app", + "message": "Third-party apps aren't enabled on your account. Please enable third-party apps in the Settings app to install this app.", + "takeMeThere": "Take me there" + }, + "readingPackage": "Reading ArcOS package", + "errors": { + "noContents": "The package contents could not be read", + "noMeta": "The package metadata could not be read", + "missingFiles": "Package is corrupt; missing package or app metadata.", + "appIdMalformed": "The application ID is malformed: it contains periods or dashes. If you're the creator of the app, be sure to use the suggested format for application IDs.", + "fsError": "Filesystem error" + }, + "fail": { + "title": "Failed to open package", + "messagePartial": "ArcOS failed to open the specified package." + }, + "elevation": { + "what": "ArcOS wants to install an application", + "description": "{{0}} - {{1}}" + }, + "header": { + "title": "Do you want to install this package?" + }, + "info": { + "author": "Author", + "version": "Version" + }, + "installPackage": "Install package" + }, + "ArcFindProc": { + "fsSupplier": { + "shortcut": "Shortcut - {{0}}" + }, + "appSupplier": { + "description": "By {{0}}" + }, + "powerOptions": { + "shutdown": { + "caption": "Shut down", + "description": "Leave the desktop and turn off ArcOS" + }, + "restart": { + "caption": "Restart", + "description": "Leave the desktop and restart ArcOS" + }, + "logoff": { + "caption": "Log off", + "description": "Leave the desktop and log out of ArcOS" + } + } + }, + "contextMenu": { + "_name": "Context Menu", + "system": { + "windowTitlebar": { + "appInfo": "App Info", + "processInfo": "Process Info", + "windowSnapping": "Window snapping", + "snappingLeft": "Left", + "snappingRight": "Right", + "snappingTop": "Top", + "snappingBottom": "Bottom", + "snappingTopLeft": "Top Left", + "snappingTopRight": "Top Right", + "snappingBottomLeft": "Bottom Left", + "snappingBottomRight": "Bottom Right", + "moveWorkspace": "Move to workspace", + "moveWorkspaceLeft": "Left workspace", + "moveWorkspaceRight": "Right workspace" + } + } + }, + "DriveInfo": { + "_name": "Drive Info", + "quota": { + "used": "Used ({{0}}%)", + "free": "Free ({{0}}%)" + }, + "legend": { + "system": "System ({{0}}%)", + "trash": "Trash ({{0}}%)", + "home": "Home ({{0}}%)", + "apps": "Apps ({{0}}%)" + }, + "advanced": { + "id": "ID", + "mountpoint": "Mountpoint", + "label": "Label", + "identifiesAs": "Identifies As", + "flags": "Flags", + "busy": "Busy", + "fixed": "Fixed", + "readonly": "Readonly", + "removable": "Removable" + }, + "actions": { + "simple": "Simple", + "advanced": "Advanced" + } + }, + "ExitApp": { + "_name": "Exit", + "header": { + "title": "Exit ArcOS", + "message": "What's your escape route?" + }, + "exitActions": { + "restart": "Restart", + "shutdown": "Shutdown", + "logoff": "Log off" + } + }, + "FirstRun": { + "_name": "First Run", + "finishingUp": "Finishing up...", + "creatingShortcut": "Creating shortcut for {{0}}", + "pages": { + "welcome": { + "_name": "Welcome", + "subtitle": "Let's customize your account.", + "later": "Later", + "next": "Next" + }, + "style": { + "_name": "What's your style?", + "subtitle": "Pick which style suits you best. You can always change it in the Settings app.", + "goBack": "Go back", + "next": "Next" + }, + "profilePicture": { + "_name": "Choose a profile picture", + "subtitle": "Do you want to upload or pick a profile picture?", + "upload": "Upload...", + "choose": "Choose", + "next": "Next" + }, + "thirdParty": { + "_name": "Enable third-party apps?", + "subtitle": "Do you want to be able to install apps made by other ArcOS users? You can always turn it off later if you change your mind.", + "goBack": "Go back", + "notNow": "Not now", + "enable": "Enable" + }, + "finish": { + "_name": "You're all set!", + "subtitle": "Enjoy using ArcOS! If you have any questions or concerns, feel free to talk about them in the Discord.", + "finish": "Finish" + } + }, + "ChooseProfilePicture": { + "_name": "Choose Profile Picture", + "title": "Choose profile picture", + "subtitle": "What do you want to be?" + }, + "themes": { + "darkMode": "Dark mode", + "lightMode": "Light mode" + } + }, + "FsNewFile": { + "title": "New file", + "subtitle": "Think of a wonderful name for this new file:", + "create": "Create" + }, + "FsNewFolder": { + "title": "New folder", + "subtitle": "Think of a wonderful name for this new folder:", + "create": "Create" + }, + "FsProgress": { + "quantity": "{{0}} / {{1}} done", + "size": "{{0}} / {{1}} done" + }, + "FsProgressFail": { + "subtitle": "This file operation encountered an error" + }, + "FsRenameItem": { + "title": "Rename file or folder", + "subtitle": "Enter a new name for the item:", + "rename": "Rename" + }, + "IconEditDialog": { + "_name": "Change Icon", + "title": "Change {{0}}", + "modeToggle": { + "app": "App", + "fs": "File", + "builtin": "Built-in" + }, + "fileType": { + "loadSaveTitle": "Choose an icon to load", + "title": "File path:", + "buttonTitle": "Choose file" + }, + "appType": { + "title": "App:", + "option": "{{0}} by {{1}}" + }, + "builtinType": { + "title": "Icon ID:", + "buttonTitle": "Choose icon" + }, + "default": "Default" + }, + "IconPicker": { + "_name": "Icon Picker", + "groups": { + "Branding": "ArcOS logos", + "General": "General icons", + "Apps": "Application icons", + "Filesystem": "Filesystem-related icons", + "Power": "Power icons", + "Dialog": "Dialog icons", + "Status": "Status indicators", + "Mimetypes": "File mimetypes" + }, + "random": "Random", + "choose": "Choose", + "reset": "Reset", + "title": "Pick an icon for {{0}}" + }, + "ItemInfo": { + "_name": "Item Info", + "subtitle": "in {{0}}", + "renameItem": "Rename item", + "location": { + "fullPath": "Full Path", + "extension": "Extension", + "parent": "Parent", + "drive": "Drive", + "filesystem": "Filesystem" + }, + "meta": { + "sort": "Sort", + "type": "Type", + "size": "Size", + "created": "Created", + "modified": "Modified" + }, + "actions": { + "editShortcut": "Edit shortcut..." + } + }, + "MessageComposer": { + "_name": "New Message", + "sendProg": { + "caption": "Sending message", + "subtitleInitial": "Preparing", + "subtitleUploading": "Uploading" + }, + "discardMessage": { + "title": "Discard message?", + "message": "Are you sure you want to discard this message? This cannot be undone." + }, + "sendFailed": { + "title": "Failed to send message", + "message": "ArcOS failed to send the message! It might be too large, or none of the recipients exist. Please check the recipients or try shrinking it down, and then resend it. If it still doesn't work, contact an ArcOS administrator." + }, + "addAttachment": { + "title": "Choose one or more files to attach", + "fileProgress": { + "caption": "Just a moment...", + "readingFileKnown": "Reading {{0}}", + "readingFileUnknown": "Readon file..." + } + }, + "actionBar": { + "body": "Body", + "attachments": "Attachments", + "addAttachment": "Add attachment", + "discardMessage": "Discard message", + "send": "Send" + }, + "attachmentBar": { + "removeAttachment": "Remove attachment" + }, + "subjectField": { + "subject": "Subject:" + }, + "toField": { + "to": "To:", + "removeRecipient": "Remove {{0}}", + "enterUsername": "Enter username" + } + }, + "MultiUpdateGui": { + "_name": "App Updater", + "elevation": "ArcOS needs your permission to update {{0}} {{1}}.", + "title": { + "updating": "Updating {{0}}", + "loading": "Just a moment", + "done": "Finished updating", + "ready": "Ready to update" + }, + "allPackagesUpdated": "All packages were updated.", + "clickUpdate": "Click Update to begin", + "itemType": { + "file": "Writing file", + "mkdir": "Creating directory", + "registration": "Registering", + "generic": "Status" + }, + "showLog": "Show log", + "hideLog": "Hide log", + "startUpdate": "Update", + "finish": "Finish" + } + }, + "general": { + "betaPill": "BETA", + "cancel": "Cancel", + "okay": "Okay", + "open": "Open", + "previous": "Previous", + "next": "Next", + "continue": "Continue", + "decline": "Decline", + "iAgree": "I agree", + "unknown": "Unknown", + "noAuthor": "No author", + "enable": "Enable", + "disable": "Disable", + "minimize": "Minimize", + "maximize": "Maximize", + "close": "Close", + "yes": "Yes", + "no": "No", + "none": "None", + "confirm": "Confirm", + "ArcOSTeam": "The ArcOS Team", + "genericStatus": "Just a moment...", + "save": "Save", + "by": "By", + "loading": "Loading...", + "discard": "Discard" + }, + "appOrigins": { + "builtin": "Built-in", + "userApps": "Third-party", + "injected": "Other", + "administrative": "Administrative" + }, + "virtualLocations": { + "my_arcos": "My ArcOS", + "recycle_bin": "Recycle Bin" + }, + "userPaths": { + "Home": "Home folder", + "Documents": "Documents", + "Pictures": "Pictures", + "Downloads": "Downloads", + "Wallpapers": "Wallpapers", + "Desktop": "Desktop", + "Music": "Music", + "Applications": "Applications", + "Trashcan": "Recycle Bin", + "Root": "Your Drive", + "System": "System Folder", + "Migrations": "Migration Files", + "Configuration": "Configuration", + "AppShortcuts": "Application Shortcuts", + "AppRepository": "Application Repository" + }, + "associations": { + "tpa": "Third-party app file", + "tpab": "Third-party app binary", + "json": "JSON file", + "pdf": "PDF document", + "svg": "SVG image", + "zip": "Archive", + "txt": "Text file", + "arctermConf": "ArcTerm configuration", + "arcterm": "ArcTerm script (unsupported)", + "arctheme": "ArcOS theme", + "md": "Markdown document", + "html": "HTML document", + "htm": "HTML document", + "js": "JS script", + "dTs": "Type definitions", + "ts": "TS script", + "mjs": "JS module", + "xml": "XML file", + "arcpl": "ArcOS playlist", + "arclnk": "Shortcut", + "arc": "ArcOS package", + "msg": "ArcOS message", + "css": "CSS file", + "mig": "Migration status", + "lock": "Migration lockfile", + "RegisteredVersion": "Version registration", + "exe": "Windows executable (unsupported)", + "msi": "Windows installer (unsupported)", + "com": "MS-DOS executable (unsupported)", + "img": "Floppy image (unsupported)", + "iso": "CD-ROM image (unsupported)", + "osl": "OriginOS script (unsupported)", + "wasm": "WebAssembly (unsupported", + "bin": "Binary (unsupported)", + "mp3": "Audio file", + "opus": "Audio file", + "wav": "Audio file", + "m4a": "Audio file", + "flac": "Audio file", + "imageFile": "Image file", + "audioFile": "Audio file", + "videoFile": "Video file", + "shortcut": "Shortcut" + } +} diff --git a/src/lang/nl.json b/src/lang/nl.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/src/lang/nl.json @@ -0,0 +1 @@ +{} diff --git a/src/ts/apps/store.ts b/src/ts/apps/store.ts index 34e0511bb..3151993d3 100755 --- a/src/ts/apps/store.ts +++ b/src/ts/apps/store.ts @@ -74,8 +74,8 @@ export const AdminAppImportPathAbsolutes = import.meta.glob([ export const appShortcuts: [number, AppKeyCombinations][] = []; export const AppOrigins: Record = { - builtin: "Built-in", - userApps: "Third-party", - injected: "Other", - aefs: "Administrative", + builtin: "%appOrigins.builtin%", + userApps: "%appOrigins.userApps%", + injected: "%appOrigins.injected%", + aefs: "%appOrigins.administrative%", }; diff --git a/src/ts/env.ts b/src/ts/env.ts index eb8906d12..a47f12945 100755 --- a/src/ts/env.ts +++ b/src/ts/env.ts @@ -11,6 +11,7 @@ import type { IServerConnector, IServerManager } from "$interfaces/modules/IServ import type { ISoundbus } from "$interfaces/modules/ISoundbus"; import type { ISystemDispatch } from "$interfaces/modules/ISystemDispatch"; import packageJson from "../../package.json"; +import type { I18n as II18n } from "./kernel/mods/i18n"; export const ArcOSVersion = packageJson.version as `${number}.${number}.${number}`; export const BETA = true; @@ -25,6 +26,7 @@ export let SysDispatch: ISystemDispatch; export let SoundBus: ISoundbus; export let State: IStateHandler; export let BugHunt: IBugHunt; +export let I18n: II18n; export function SetCurrentKernel(kernel: IWaveKernel) { if (Kernel) throw new Error("Tried to reassign CurrentKernel"); @@ -46,6 +48,7 @@ export function SetKernelExports() { SysDispatch = getKMod("dispatch"); SoundBus = getKMod("soundbus"); BugHunt = getKMod("bughunt"); + I18n = getKMod("i18n"); } export function getKMod(id: string, dontCrash = false): T { diff --git a/src/ts/kernel/init.ts b/src/ts/kernel/init.ts index df61a1021..72c101545 100755 --- a/src/ts/kernel/init.ts +++ b/src/ts/kernel/init.ts @@ -1,7 +1,7 @@ import type { IStateHandler } from "$interfaces/IStateHandler"; import type { IServerManager } from "$interfaces/modules/IServerManager"; import { __Console__ } from "$ts/console"; -import { ArcOSVersion, Env, Fs, getKMod, Kernel, SetCurrentStateHandler, Stack } from "$ts/env"; +import { ArcOSVersion, Env, Fs, getKMod, I18n, Kernel, SetCurrentStateHandler, Stack } from "$ts/env"; import { MemoryFilesystemDrive } from "$ts/kernel/mods/fs/drives/temp"; import { ArcBuild } from "$ts/metadata/build"; import { ArcMode } from "$ts/metadata/mode"; @@ -45,6 +45,8 @@ export class InitProcess extends Process { __Console__.timeEnd("** Init jumpstart"); this.name = "InitProcess"; + I18n.startObserver(Stack.renderer?.target!); + if (ArcMode() === "nightly") this.nightly(); this.setSource(__SOURCE__); diff --git a/src/ts/kernel/mods/i18n/index.ts b/src/ts/kernel/mods/i18n/index.ts new file mode 100644 index 000000000..cdfca0e10 --- /dev/null +++ b/src/ts/kernel/mods/i18n/index.ts @@ -0,0 +1,146 @@ +import type { IWaveKernel } from "$interfaces/IWaveKernel"; +import type { ISystemDispatch } from "$interfaces/modules/ISystemDispatch"; +import EnglishLanguage from "$lang/en.json"; +import DutchLanguage from "$lang/nl.json"; +import { getKMod } from "$ts/env"; +import { KernelModule } from "$ts/kernel/module"; +import { getJsonHierarchy } from "$ts/util/hierarchy"; + +export class I18n extends KernelModule { + REGEX = /%(?[\w.=\-]+)(?:\((?(.*?))\)|)%/gm; + language: string = "nl"; + dispatch: ISystemDispatch; + observer: MutationObserver | null = null; + TARGET?: HTMLDivElement; + + // Attributes to translate + TRANSLATABLE_ATTRIBUTES = ["placeholder", "title", "alt", "aria-label", "data-tooltip"]; + + LANGUAGES: Record = { + en: EnglishLanguage, + nl: DutchLanguage, + }; + + constructor(kernel: IWaveKernel, id: string) { + super(kernel, id); + this.dispatch = getKMod("dispatch"); + } + + async _init(): Promise { + this.startObserver(document.querySelector("#appRenderer")!); + } + + translateString(str: string, prefix?: string): string | undefined { + const results = [...str.matchAll(this.REGEX)].map(({ groups, [0]: key }) => ({ + id: groups?.id, + inlays: groups?.inlays ? groups.inlays.split("::") : [], + key, + })); + + if (!results.length) return undefined; + + let resultString = str; + + for (const result of results) { + const fullId = prefix ? `${prefix}.${result.id}` : result.id; + + const value = + getJsonHierarchy(this.LANGUAGES[this.language], fullId ?? "") || + getJsonHierarchy(this.LANGUAGES[this.language], result.id ?? ""); + + if (!result.id || !value) { + resultString = resultString.replace(result.key, "??"); + continue; + } + + let partialResultString = value as string; + + if (result.inlays) { + for (let i = 0; i < result.inlays.length; i++) { + partialResultString = partialResultString.replace( + `{{${i}}}`, + this.translateString(result.inlays[i]) || result.inlays[i] + ); + } + } + + resultString = resultString.replace(result.key, partialResultString); + } + + return resultString; + } + + replaceInNode(node: Node): void { + if (node.nodeType === node.TEXT_NODE) { + const str = node.textContent ?? ""; + const prefix = node.parentElement?.closest("[data-prefix]")?.getAttribute("data-prefix") ?? undefined; + const resultString = this.translateString(str, prefix); + + if (!resultString) return; + + if (node.parentElement) { + node.parentElement.setAttribute("data-i18n-original", str); + node.textContent = resultString; + + let raf: number; + + const x = () => { + if (node.isConnected) { + if (node.textContent !== resultString) { + this.replaceInNode(node); + } + + raf = requestAnimationFrame(x); + } else { + cancelAnimationFrame(raf); + } + }; + + raf = requestAnimationFrame(x); + } + } else if (node.nodeType === node.ELEMENT_NODE) { + const el = node as HTMLElement; + const prefix = el.closest("[data-prefix]")?.getAttribute("data-prefix") ?? undefined; + + for (const attr of this.TRANSLATABLE_ATTRIBUTES) { + const raw = el.getAttribute(attr); + if (!raw) continue; + + const resultString = this.translateString(raw, prefix); + if (!resultString) continue; + + el.setAttribute(`data-i18n-original-${attr}`, raw); + el.setAttribute(attr, resultString); + } + + Array.from(node.childNodes).forEach((n) => this.replaceInNode(n)); + } + } + + startObserver(target: HTMLDivElement) { + if (!target) throw new Error(`I18n: target does not exist`); + + this.TARGET = target; + this.observer = new MutationObserver((mutations) => { + for (const m of mutations) { + m.addedNodes.forEach((n) => this.replaceInNode(n)); + } + }); + + this.observer.observe(this.TARGET, { + childList: true, + subtree: true, + attributes: true, + characterData: true, + characterDataOldValue: true, + attributeOldValue: true, + }); + + this.replaceInNode(this.TARGET); + } + + stopObserver() { + this.observer?.disconnect(); + this.observer = null; + } +} diff --git a/src/ts/kernel/module/store.ts b/src/ts/kernel/module/store.ts index c3af08ba7..38b64e23b 100755 --- a/src/ts/kernel/module/store.ts +++ b/src/ts/kernel/module/store.ts @@ -3,12 +3,14 @@ import { SystemDispatch } from "$ts/kernel/mods/dispatch"; import { Filesystem } from "$ts/kernel/mods/fs"; import { SoundBus } from "$ts/kernel/mods/soundbus"; import { Environment } from "../mods/env"; +import { I18n } from "../mods/i18n"; import { ServerManager } from "../mods/server"; import { ProcessHandler } from "../mods/stack"; export const KernelModules: Record = { env: Environment, dispatch: SystemDispatch, + i18n: I18n, soundbus: SoundBus, stack: ProcessHandler, server: ServerManager, diff --git a/src/ts/servicehost/services/FileAssocSvc/store/audio.ts b/src/ts/servicehost/services/FileAssocSvc/store/audio.ts index 4fcef1cd7..d84acf19a 100644 --- a/src/ts/servicehost/services/FileAssocSvc/store/audio.ts +++ b/src/ts/servicehost/services/FileAssocSvc/store/audio.ts @@ -2,23 +2,23 @@ import type { FileDefinition } from "$types/system/assoc"; export const AudioFileDefinitions: Record = { ".mp3": { - friendlyName: "Audio file", + friendlyName: "%associations.audioFile%", icon: "AudioMimeIcon", }, ".opus": { - friendlyName: "Audio file", + friendlyName: "%associations.audioFile%", icon: "AudioMimeIcon", }, ".wav": { - friendlyName: "Audio file", + friendlyName: "%associations.audioFile%", icon: "AudioMimeIcon", }, ".m4a": { - friendlyName: "Audio file", + friendlyName: "%associations.audioFile%", icon: "AudioMimeIcon", }, ".flac": { - friendlyName: "Audio file", + friendlyName: "%associations.audioFile%", icon: "AudioMimeIcon", }, }; diff --git a/src/ts/servicehost/services/FileAssocSvc/store/image.ts b/src/ts/servicehost/services/FileAssocSvc/store/image.ts index 44315817a..743817cc8 100644 --- a/src/ts/servicehost/services/FileAssocSvc/store/image.ts +++ b/src/ts/servicehost/services/FileAssocSvc/store/image.ts @@ -2,39 +2,39 @@ import type { FileDefinition } from "$types/system/assoc"; export const ImageFileDefinitions: Record = { ".png": { - friendlyName: "Image file", + friendlyName: "%associations.imageFile%", icon: "ImageMimeIcon", }, ".jpg": { - friendlyName: "Image file", + friendlyName: "%associations.imageFile%", icon: "ImageMimeIcon", }, ".gif": { - friendlyName: "Image file", + friendlyName: "%associations.imageFile%", icon: "ImageMimeIcon", }, ".webp": { - friendlyName: "Image file", + friendlyName: "%associations.imageFile%", icon: "ImageMimeIcon", }, ".ico": { - friendlyName: "Image file", + friendlyName: "%associations.imageFile%", icon: "ImageMimeIcon", }, ".bmp": { - friendlyName: "Image file", + friendlyName: "%associations.imageFile%", icon: "ImageMimeIcon", }, ".tif": { - friendlyName: "Image file", + friendlyName: "%associations.imageFile%", icon: "ImageMimeIcon", }, ".tiff": { - friendlyName: "Image file", + friendlyName: "%associations.imageFile%", icon: "ImageMimeIcon", }, ".jpeg": { - friendlyName: "Image file", + friendlyName: "%associations.imageFile%", icon: "ImageMimeIcon", }, }; diff --git a/src/ts/servicehost/services/FileAssocSvc/store/index.ts b/src/ts/servicehost/services/FileAssocSvc/store/index.ts index df461778c..26cad6907 100644 --- a/src/ts/servicehost/services/FileAssocSvc/store/index.ts +++ b/src/ts/servicehost/services/FileAssocSvc/store/index.ts @@ -5,139 +5,139 @@ import { VideoFileDefinitions } from "./video"; export const DefaultFileDefinitions: Record = { ".tpa": { - friendlyName: "Third-party app file", + friendlyName: "%associations.tpa%", icon: "ArcAppMimeIcon", }, ".tpab": { - friendlyName: "Third-party app binary", + friendlyName: "%associations.tpab%", icon: "ArcAppMimeIcon", }, ".json": { - friendlyName: "JSON file", + friendlyName: "%associations.json%", icon: "JsonMimeIcon", }, ".pdf": { - friendlyName: "PDF document", + friendlyName: "%associations.pdf%", icon: "PdfMimeIcon", }, ".svg": { - friendlyName: "SVG image", + friendlyName: "%associations.svg%", icon: "SvgMimeIcon", }, ".zip": { - friendlyName: "Archive", + friendlyName: "%associations.zip%", icon: "CompressMimeIcon", }, ".txt": { - friendlyName: "Text file", + friendlyName: "%associations.txt%", icon: "TextMimeIcon", }, "arcterm.conf": { - friendlyName: "ArcTerm configuration", + friendlyName: "%associations.arctermConf%", icon: "TextMimeIcon", }, ".arcterm": { - friendlyName: "ArcTerm script (unsupported)", + friendlyName: "%associations.arcterm%", icon: "TextMimeIcon", }, ".arctheme": { - friendlyName: "ArcOS theme", + friendlyName: "%associations.arctheme%", icon: "TextMimeIcon", }, ".md": { - friendlyName: "Markdown document", + friendlyName: "%associations.md%", icon: "TextMimeIcon", }, ".html": { - friendlyName: "HTML document", + friendlyName: "%associations.html%", icon: "XmlMimeIcon", }, ".htm": { - friendlyName: "HTML document", + friendlyName: "%associations.htm%", icon: "XmlMimeIcon", }, ".js": { - friendlyName: "JS script", + friendlyName: "%associations.js%", icon: "JavascriptMimeIcon", }, ".d.ts": { - friendlyName: "Type definitions", + friendlyName: "%associations.dTs%", icon: "JavascriptMimeIcon", }, ".ts": { - friendlyName: "TS script", + friendlyName: "%associations.ts%", icon: "JavascriptMimeIcon", }, ".mjs": { - friendlyName: "JS module", + friendlyName: "%associations.js%", icon: "JavascriptMimeIcon", }, ".xml": { - friendlyName: "XML file", + friendlyName: "%associations.xml%", icon: "XmlMimeIcon", }, ".arcpl": { - friendlyName: "ArcOS playlist", + friendlyName: "%associations.arcpl%", icon: "PlaylistMimeIcon", }, ".arclnk": { - friendlyName: "Shortcut", + friendlyName: "%associations.shortcut%", icon: "ShortcutMimeIcon", }, ".arc": { - friendlyName: "ArcOS package", + friendlyName: "%associations.arc%", icon: "ArcAppMimeIcon", }, ".msg": { - friendlyName: "ArcOS message", + friendlyName: "%associations.msg%", icon: "MessagingIcon", }, ".css": { - friendlyName: "CSS file", + friendlyName: "%associations.css%", icon: "DefaultMimeIcon", }, ".mig": { - friendlyName: "Migration status", + friendlyName: "%associations.mig%", icon: "ComponentIcon", }, ".lock": { - friendlyName: "Migration lockfile", + friendlyName: "%associations.lock%", icon: "ComponentIcon", }, RegisteredVersion: { - friendlyName: "Version registration", + friendlyName: "%associations.RegisteredVersion%", icon: "ComponentIcon", }, ".exe": { - friendlyName: "Windows executable (unsupported)", + friendlyName: "%associations.exe%", icon: "UnknownFileIcon", }, ".msi": { - friendlyName: "Windows installer (unsupported)", + friendlyName: "%associations.msi%", icon: "UnknownFileIcon", }, ".com": { - friendlyName: "MS-DOS executable (unsupported)", + friendlyName: "%associations.com%", icon: "UnknownFileIcon", }, ".img": { - friendlyName: "Floppy image (unsupported)", + friendlyName: "%associations.img%", icon: "UnknownFileIcon", }, ".iso": { - friendlyName: "CD-ROM image (unsupported)", + friendlyName: "%associations.iso%", icon: "UnknownFileIcon", }, ".osl": { - friendlyName: "OriginOS script (unsupported)", + friendlyName: "%associations.osl%", icon: "UnknownFileIcon", }, ".wasm": { - friendlyName: "WebAssembly (unsupported", + friendlyName: "%associations.wasm%", icon: "UnknownFileIcon", }, ".bin": { - friendlyName: "Binary (unsupported)", + friendlyName: "%associations.bin%", icon: "UnknownFileIcon", }, ".atc": { diff --git a/src/ts/servicehost/services/FileAssocSvc/store/video.ts b/src/ts/servicehost/services/FileAssocSvc/store/video.ts index e10cacff7..c994d2f78 100644 --- a/src/ts/servicehost/services/FileAssocSvc/store/video.ts +++ b/src/ts/servicehost/services/FileAssocSvc/store/video.ts @@ -2,19 +2,19 @@ import type { FileDefinition } from "$types/system/assoc"; export const VideoFileDefinitions: Record = { ".mp4": { - friendlyName: "Video file", + friendlyName: "%associations.videoFile%", icon: "VideoMimeIcon", }, ".mkv": { - friendlyName: "Video file", + friendlyName: "%associations.videoFile%", icon: "VideoMimeIcon", }, ".mov": { - friendlyName: "Video file", + friendlyName: "%associations.videoFile%", icon: "VideoMimeIcon", }, ".avi": { - friendlyName: "Video file", + friendlyName: "%associations.videoFile%", icon: "VideoMimeIcon", }, }; diff --git a/src/ts/user/store.ts b/src/ts/user/store.ts index 9ed9fbf74..39dc80916 100644 --- a/src/ts/user/store.ts +++ b/src/ts/user/store.ts @@ -354,21 +354,21 @@ export const SystemFolders = [ ]; export const UserPathCaptions: Record = { - Home: "Home folder", - Documents: "Documents", - Pictures: "Pictures", - Downloads: "Downloads", - Wallpapers: "Wallpapers", - Desktop: "Desktop", - Music: "Music", - Applications: "Applications", - Trashcan: "Recycle Bin", - Root: "Your Drive", - System: "System Folder", - Migrations: "Migration Files", - Configuration: "Configuration", - AppShortcuts: "Application Shortcuts", - AppRepository: "Application Repository", + Home: "%userPaths.Home%", + Documents: "%userPaths.Documents%", + Pictures: "%userPaths.Pictures%", + Downloads: "%userPaths.Downloads%", + Wallpapers: "%userPaths.Wallpapers%", + Desktop: "%userPaths.Desktop%", + Music: "%userPaths.Music%", + Applications: "%userPaths.Applications%", + Trashcan: "%userPaths.Trashcan%", + Root: "%userPaths.Root%", + System: "%userPaths.System%", + Migrations: "%userPaths.Migrations%", + Configuration: "%userPaths.Configuration%", + AppShortcuts: "%userPaths.AppShortcuts%", + AppRepository: "%userPaths.AppRepository%", Libraries: "System Libraries", StartMenu: "Start Menu", }; diff --git a/src/ts/writable.ts b/src/ts/writable.ts index 7e16d7798..de9973cf8 100755 --- a/src/ts/writable.ts +++ b/src/ts/writable.ts @@ -1,9 +1,25 @@ import type { ReadableStore } from "$types/shared/writable"; import { get, writable } from "svelte/store"; +import { getKMod } from "./env"; +import { I18n } from "./kernel/mods/i18n"; export function Store(initial?: T): ReadableStore { + function translateValue(v: any) { + if (typeof v !== "string") return v; + + const i18n = getKMod("i18n", true); + + return i18n?.translateString(v) || v; + } + const store = writable(initial); - const obj = { ...store, get: () => get(store) }; + const obj = { + ...store, + get: () => get(store), + set: (v: any) => store.set(translateValue(v)), + update: (v: (v: any) => any) => store.set(translateValue(v(get(store)))), + }; + const fn = () => obj.get(); fn.get = obj.get; diff --git a/tsconfig.app.json b/tsconfig.app.json index cb056d40e..d947e35ad 100755 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -23,6 +23,7 @@ "$types/*": ["./src/types/*"], "$lib/*": ["./src/lib/*"], "$ts/*": ["./src/ts/*"], + "$lang/*": ["./src/lang/*"], "$interfaces/*": ["./src/interfaces/*"] }, "esModuleInterop": true diff --git a/vite.config.ts b/vite.config.ts index 42b29dfa0..d237ebbd3 100755 --- a/vite.config.ts +++ b/vite.config.ts @@ -29,6 +29,7 @@ export default defineConfig({ $ts: resolve(__dirname, "./src/ts"), $lib: resolve(__dirname, "./src/lib"), $interfaces: resolve(__dirname, "./src/interfaces"), + $lang: resolve(__dirname, "./src/lang"), }, }, build: {