diff --git a/core/src/db/model/package.rs b/core/src/db/model/package.rs index 76869511a..70c33a360 100644 --- a/core/src/db/model/package.rs +++ b/core/src/db/model/package.rs @@ -14,7 +14,7 @@ use crate::net::host::Hosts; use crate::net::service_interface::ServiceInterface; use crate::prelude::*; use crate::progress::FullProgress; -use crate::s9pk::manifest::Manifest; +use crate::s9pk::manifest::{LocaleString, Manifest}; use crate::status::StatusInfo; use crate::util::DataUrl; use crate::util::serde::{Pem, is_partial_of}; @@ -417,7 +417,7 @@ impl Map for CurrentDependencies { #[serde(rename_all = "camelCase")] #[model = "Model"] pub struct CurrentDependencyInfo { - pub title: Option, + pub title: Option, pub icon: Option>, #[serde(flatten)] pub kind: CurrentDependencyKind, diff --git a/core/src/registry/package/index.rs b/core/src/registry/package/index.rs index 4a53c1c13..64b83d5e4 100644 --- a/core/src/registry/package/index.rs +++ b/core/src/registry/package/index.rs @@ -290,12 +290,12 @@ impl Model { let metadata = self.as_metadata_mut(); metadata .as_alerts_mut() - .mutate(|a| Ok(a.localize_for(locale))); + .mutate(|a| Ok(a.localize_for(locale)))?; metadata .as_dependency_metadata_mut() .as_entries_mut()? .into_iter() - .try_for_each(|(_, d)| d.mutate(|d| Ok(d.localize_for(locale)))); + .try_for_each(|(_, d)| d.mutate(|d| Ok(d.localize_for(locale))))?; metadata .as_description_mut() .mutate(|d| Ok(d.localize_for(locale)))?; diff --git a/core/src/service/effects/dependency.rs b/core/src/service/effects/dependency.rs index ffd38ddac..43f4297b9 100644 --- a/core/src/service/effects/dependency.rs +++ b/core/src/service/effects/dependency.rs @@ -228,7 +228,7 @@ pub async fn set_dependencies( .s9pk .dependency_metadata(&dep_id) .await? - .map(|m| m.title.localized()), + .map(|m| m.title), icon: context .seed .persistent_container @@ -345,7 +345,7 @@ pub async fn check_dependencies( .collect(); results.push(CheckDependenciesResult { package_id, - title, + title: title.map(|t| t.localized()), installed_version: None, satisfies: BTreeSet::new(), is_running: false, @@ -371,7 +371,7 @@ pub async fn check_dependencies( .collect(); results.push(CheckDependenciesResult { package_id, - title, + title: title.map(|t| t.localized()), installed_version, satisfies, is_running, diff --git a/sdk/base/lib/osBindings/CurrentDependencyInfo.ts b/sdk/base/lib/osBindings/CurrentDependencyInfo.ts index e56e2be7a..aaabea857 100644 --- a/sdk/base/lib/osBindings/CurrentDependencyInfo.ts +++ b/sdk/base/lib/osBindings/CurrentDependencyInfo.ts @@ -1,8 +1,9 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { DataUrl } from "./DataUrl" +import type { LocaleString } from "./LocaleString" export type CurrentDependencyInfo = { - title: string | null + title: LocaleString | null icon: DataUrl | null versionRange: string } & ({ kind: "exists" } | { kind: "running"; healthChecks: string[] }) diff --git a/sdk/package/lib/version/VersionInfo.ts b/sdk/package/lib/version/VersionInfo.ts index 0d6bd1c62..35d6c0544 100644 --- a/sdk/package/lib/version/VersionInfo.ts +++ b/sdk/package/lib/version/VersionInfo.ts @@ -7,7 +7,7 @@ export type VersionOptions = { /** The exver-compliant version number */ version: Version & ValidateExVer /** The release notes for this version */ - releaseNotes: string + releaseNotes: T.LocaleString /** Data migrations for this version */ migrations: { /** diff --git a/web/projects/marketplace/src/pages/list/categories/categories.component.html b/web/projects/marketplace/src/pages/list/categories/categories.component.html index cdb2b2657..2bb5017cd 100644 --- a/web/projects/marketplace/src/pages/list/categories/categories.component.html +++ b/web/projects/marketplace/src/pages/list/categories/categories.component.html @@ -8,7 +8,7 @@ {{ - cat.key === 'ai' ? (cat.key | uppercase) : (cat.value.name | titlecase) + cat.key === 'ai' ? (cat.key | uppercase) : (cat.value.name | localize | titlecase) }} diff --git a/web/projects/marketplace/src/pages/list/categories/categories.module.ts b/web/projects/marketplace/src/pages/list/categories/categories.module.ts index 15a779c8a..2d67d8aca 100644 --- a/web/projects/marketplace/src/pages/list/categories/categories.module.ts +++ b/web/projects/marketplace/src/pages/list/categories/categories.module.ts @@ -2,12 +2,13 @@ import { TuiIcon, TuiAppearance } from '@taiga-ui/core' import { CommonModule } from '@angular/common' import { NgModule } from '@angular/core' import { TuiSkeleton } from '@taiga-ui/kit' +import { LocalizePipe } from '@start9labs/shared' import { CategoriesComponent } from './categories.component' import { RouterModule } from '@angular/router' @NgModule({ - imports: [RouterModule, CommonModule, TuiAppearance, TuiIcon, TuiSkeleton], + imports: [RouterModule, CommonModule, TuiAppearance, TuiIcon, TuiSkeleton, LocalizePipe], declarations: [CategoriesComponent], exports: [CategoriesComponent], }) diff --git a/web/projects/marketplace/src/pages/show/dependencies/dependency-item.component.ts b/web/projects/marketplace/src/pages/show/dependencies/dependency-item.component.ts index 60e4302d7..216dc8d7c 100644 --- a/web/projects/marketplace/src/pages/show/dependencies/dependency-item.component.ts +++ b/web/projects/marketplace/src/pages/show/dependencies/dependency-item.component.ts @@ -1,7 +1,7 @@ import { KeyValue } from '@angular/common' -import { ChangeDetectionStrategy, Component, Input } from '@angular/core' +import { ChangeDetectionStrategy, Component, inject, Input } from '@angular/core' import { RouterModule } from '@angular/router' -import { ExverPipesModule, i18nPipe } from '@start9labs/shared' +import { ExverPipesModule, i18nPipe, i18nService } from '@start9labs/shared' import { T } from '@start9labs/start-sdk' import { TuiAvatar, TuiLineClamp } from '@taiga-ui/kit' import { MarketplacePkgBase } from '../../../types' @@ -97,6 +97,8 @@ import { MarketplacePkgBase } from '../../../types' imports: [RouterModule, TuiAvatar, ExverPipesModule, TuiLineClamp, i18nPipe], }) export class MarketplaceDepItemComponent { + private readonly i18nService = inject(i18nService) + @Input({ required: true }) pkg!: MarketplacePkgBase @@ -109,6 +111,7 @@ export class MarketplaceDepItemComponent { } getTitle(key: string): string { - return this.pkg.dependencyMetadata[key]?.title || key + const title = this.pkg.dependencyMetadata[key]?.title + return title ? this.i18nService.localize(title) : key } } diff --git a/web/projects/marketplace/src/pages/show/hero.component.ts b/web/projects/marketplace/src/pages/show/hero.component.ts index 488773d0f..8579ddaa2 100644 --- a/web/projects/marketplace/src/pages/show/hero.component.ts +++ b/web/projects/marketplace/src/pages/show/hero.component.ts @@ -1,5 +1,6 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core' import { SharedPipesModule, TickerComponent } from '@start9labs/shared' +import { T } from '@start9labs/start-sdk' @Component({ selector: 'marketplace-package-hero', @@ -125,7 +126,7 @@ export class MarketplacePackageHeroComponent { id: string title: string version: string - description: { short: string } + description: { short: T.LocaleString } icon: string } diff --git a/web/projects/marketplace/src/pages/show/release-notes.component.ts b/web/projects/marketplace/src/pages/show/release-notes.component.ts index a8d69552d..15a8a7a61 100644 --- a/web/projects/marketplace/src/pages/show/release-notes.component.ts +++ b/web/projects/marketplace/src/pages/show/release-notes.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectionStrategy, Component, input } from '@angular/core' -import { MarkdownPipe } from '@start9labs/shared' +import { LocalizePipe, MarkdownPipe } from '@start9labs/shared' import { NgDompurifyPipe } from '@taiga-ui/dompurify' import { MarketplacePkgBase } from '../../types' @@ -9,7 +9,7 @@ import { MarketplacePkgBase } from '../../types'

New in {{ pkg().version }}

-

+

`, @@ -21,7 +21,7 @@ import { MarketplacePkgBase } from '../../types' } `, changeDetection: ChangeDetectionStrategy.OnPush, - imports: [NgDompurifyPipe, MarkdownPipe], + imports: [NgDompurifyPipe, MarkdownPipe, LocalizePipe], }) export class MarketplaceReleaseNotesComponent { readonly pkg = input.required() diff --git a/web/projects/shared/src/i18n/i18n.service.ts b/web/projects/shared/src/i18n/i18n.service.ts index 28f1bc86a..375e941bb 100644 --- a/web/projects/shared/src/i18n/i18n.service.ts +++ b/web/projects/shared/src/i18n/i18n.service.ts @@ -1,6 +1,7 @@ import { inject, Injectable, signal } from '@angular/core' import { TuiLanguageName, TuiLanguageSwitcherService } from '@taiga-ui/i18n' import { I18N, I18N_LOADER, I18N_STORAGE } from './i18n.providers' +import { T } from '@start9labs/start-sdk' export const languages = ['en_US', 'es_ES', 'de_DE', 'fr_FR', 'pl_PL'] as const export type Languages = (typeof languages)[number] @@ -37,6 +38,14 @@ export class i18nService extends TuiLanguageSwitcherService { ) } + localize(string: T.LocaleString): string { + if (typeof string === 'string') return string + + return ( + string[this.lang] ?? string['en_US'] ?? Object.values(string)[0] ?? '' + ) + } + setLang(language: Languages = 'en_US'): void { const tuiLang = LANGUAGE_TO_TUI[language] const current = this.language diff --git a/web/projects/shared/src/i18n/localize.pipe.ts b/web/projects/shared/src/i18n/localize.pipe.ts new file mode 100644 index 000000000..e9f439ec0 --- /dev/null +++ b/web/projects/shared/src/i18n/localize.pipe.ts @@ -0,0 +1,16 @@ +import { inject, Injectable, Pipe, PipeTransform } from '@angular/core' +import { i18nService } from './i18n.service' +import { T } from '@start9labs/start-sdk' + +@Pipe({ + name: 'localize', + pure: false, +}) +@Injectable({ providedIn: 'root' }) +export class LocalizePipe implements PipeTransform { + private readonly i18nService = inject(i18nService) + + transform(string: T.LocaleString): string { + return this.i18nService.localize(string) + } +} diff --git a/web/projects/shared/src/public-api.ts b/web/projects/shared/src/public-api.ts index 3269be9aa..c3c683984 100644 --- a/web/projects/shared/src/public-api.ts +++ b/web/projects/shared/src/public-api.ts @@ -18,6 +18,7 @@ export * from './directives/safe-links.directive' export * from './i18n/i18n.pipe' export * from './i18n/i18n.providers' export * from './i18n/i18n.service' +export * from './i18n/localize.pipe' export * from './pipes/exver/exver.module' export * from './pipes/exver/exver.pipe' diff --git a/web/projects/ui/src/app/routes/portal/routes/marketplace/components/controls.component.ts b/web/projects/ui/src/app/routes/portal/routes/marketplace/components/controls.component.ts index f8732b32b..142ef126d 100644 --- a/web/projects/ui/src/app/routes/portal/routes/marketplace/components/controls.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/marketplace/components/controls.component.ts @@ -14,6 +14,7 @@ import { Exver, ExverPipesModule, i18nPipe, + i18nService, LoadingService, sameUrl, } from '@start9labs/shared' @@ -122,6 +123,7 @@ export class MarketplaceControlsComponent { private readonly router = inject(Router) private readonly marketplace = inject(MarketplaceService) private readonly api = inject(ApiService) + private readonly i18n = inject(i18nService) readonly pkg = input.required>() @@ -148,7 +150,7 @@ export class MarketplaceControlsComponent { const originalUrl = localPkg?.registry || null if (!localPkg) { - if (await this.alerts.alertInstall(this.pkg().alerts.install || '')) { + if (await this.alerts.alertInstall(this.i18n.localize(this.pkg().alerts.install || ''))) { this.installOrUpload(currentUrl) } return diff --git a/web/projects/ui/src/app/routes/portal/routes/updates/item.component.ts b/web/projects/ui/src/app/routes/portal/routes/updates/item.component.ts index c8348945a..0a87d2faf 100644 --- a/web/projects/ui/src/app/routes/portal/routes/updates/item.component.ts +++ b/web/projects/ui/src/app/routes/portal/routes/updates/item.component.ts @@ -12,6 +12,7 @@ import { DialogService, i18nKey, i18nPipe, + LocalizePipe, MarkdownPipe, SafeLinksDirective, } from '@start9labs/shared' @@ -138,7 +139,7 @@ import UpdatesComponent from './updates.component'

@@ -237,6 +238,7 @@ import UpdatesComponent from './updates.component' TuiProgressCircle, TuiTitle, TuiFade, + LocalizePipe, MarkdownPipe, NgDompurifyPipe, SafeLinksDirective,