From 9193f8bb80bcc69c57d6c14a0fe1b138d5988645 Mon Sep 17 00:00:00 2001 From: 4gray Date: Tue, 24 Feb 2026 21:14:34 +0100 Subject: [PATCH] feat(workspace): add i18n support for dashboard widgets and update translations --- apps/web/src/assets/i18n/de.json | 43 ++++++++++++++++++ apps/web/src/assets/i18n/en.json | 45 +++++++++++++++++++ apps/web/src/assets/i18n/ru.json | 43 ++++++++++++++++++ .../lib/dashboard-widget-host.component.html | 6 ++- .../lib/dashboard-widget-host.component.ts | 2 + .../continue-watching-widget.component.html | 41 ++++++++++++----- .../continue-watching-widget.component.ts | 9 +++- .../global-favorites-widget.component.html | 35 +++++++++++---- .../global-favorites-widget.component.ts | 4 ++ .../recent-sources-widget.component.html | 45 +++++++++++++++---- .../recent-sources-widget.component.ts | 3 +- .../recently-watched-widget.component.html | 35 +++++++++++---- .../recently-watched-widget.component.ts | 4 ++ .../source-stats-widget.component.html | 13 +++--- .../widgets/source-stats-widget.component.ts | 3 +- 15 files changed, 285 insertions(+), 46 deletions(-) diff --git a/apps/web/src/assets/i18n/de.json b/apps/web/src/assets/i18n/de.json index 899af8e6f..6e6c7a7b4 100644 --- a/apps/web/src/assets/i18n/de.json +++ b/apps/web/src/assets/i18n/de.json @@ -428,5 +428,48 @@ "DOWN_CHANNEL": "Sender abwärts", "HEADER": "IPTVNator Fernbedienung", "FOOTER": "IPTVNator Fernbedienung" + }, + "WORKSPACE": { + "FAVORITES_ONLY": "Nur Favoriten", + "SORT_DATE_DESC": "Datum (Neueste zuerst)", + "SORT_DATE_ASC": "Datum (Älteste zuerst)", + "SORT_NAME_ASC": "Name A-Z", + "SORT_NAME_DESC": "Name Z-A", + "SORT_LABEL": "Sortierung: ", + "DASHBOARD": { + "UNSUPPORTED_WIDGET": "Nicht unterstütztes Widget", + "UNSUPPORTED_WIDGET_DESC": "Dieser Widget-Typ ist in diesem Build nicht verfügbar.", + "CONTINUE_WATCHING": "Weiter schauen", + "FOCUS": "Fokus", + "UNTITLED_SOURCE": "Unbenannte Quelle", + "OPEN_SOURCE": "Quelle öffnen", + "MANAGE_SOURCES": "Quellen verwalten", + "NO_ACTIVE_SOURCE": "Noch keine aktive Quelle ausgewählt.", + "ADD_SOURCE": "Quelle hinzufügen", + "RECENTLY_USED": "Zuletzt verwendet", + "GLOBAL_FAVORITES": "Globale Favoriten", + "LIBRARY": "Bibliothek", + "FILTER_ITEMS": "Elemente filtern", + "ALL": "Alle", + "CHANNELS": "Sender", + "VOD": "VOD", + "SERIES": "Serien", + "LIST_VIEW": "Listenansicht", + "GRID_VIEW": "Gitteransicht", + "NO_FAVORITES": "Keine Favoriten für diesen Filter gefunden.", + "RECENT_SOURCES": "Letzte Quellen", + "MANAGE_ALL": "Alle verwalten", + "NO_SOURCES": "Noch keine Quellen verfügbar.", + "OPEN_SOURCES": "Quellen öffnen", + "RECENTLY_WATCHED": "Zuletzt angesehen", + "ACTIVITY": "Aktivität", + "NO_HISTORY": "Keine Elemente für diesen Filter gefunden.", + "SOURCE_STATS": "Quellen-Statistiken", + "OVERVIEW": "Übersicht", + "TOTAL": "Gesamt", + "XTREAM": "Xtream", + "STALKER": "Stalker", + "M3U": "M3U" + } } } diff --git a/apps/web/src/assets/i18n/en.json b/apps/web/src/assets/i18n/en.json index 0624c671b..f6e456317 100644 --- a/apps/web/src/assets/i18n/en.json +++ b/apps/web/src/assets/i18n/en.json @@ -142,6 +142,8 @@ }, "SETTINGS": { "TITLE": "Settings", + "GENERAL": "General", + "GENERAL_SUBTITLE": "Change the configuration of the application", "SUBTITLE": "Change the configuration of the application", "DESCRIPTION": "Please adapt the options from the list below and confirm with the save changes button", "EPG_URL_LABEL": "EPG Url (*.xml or *.xml.gz)", @@ -490,5 +492,48 @@ "DOWN_CHANNEL": "Channel Down", "HEADER": "IPTVNator Remote Control", "FOOTER": "IPTVNator Remote Control" + }, + "WORKSPACE": { + "FAVORITES_ONLY": "Favorites only", + "SORT_DATE_DESC": "Date Added (Latest First)", + "SORT_DATE_ASC": "Date Added (Oldest First)", + "SORT_NAME_ASC": "Name A-Z", + "SORT_NAME_DESC": "Name Z-A", + "SORT_LABEL": "Sort: ", + "DASHBOARD": { + "UNSUPPORTED_WIDGET": "Unsupported widget", + "UNSUPPORTED_WIDGET_DESC": "This widget type is not available in this build.", + "CONTINUE_WATCHING": "Continue Watching", + "FOCUS": "Focus", + "UNTITLED_SOURCE": "Untitled source", + "OPEN_SOURCE": "Open Source", + "MANAGE_SOURCES": "Manage Sources", + "NO_ACTIVE_SOURCE": "No active source selected yet.", + "ADD_SOURCE": "Add Source", + "RECENTLY_USED": "Recently used", + "GLOBAL_FAVORITES": "Global Favorites", + "LIBRARY": "Library", + "FILTER_ITEMS": "Filter items", + "ALL": "All", + "CHANNELS": "Channels", + "VOD": "VOD", + "SERIES": "Series", + "LIST_VIEW": "List view", + "GRID_VIEW": "Grid view", + "NO_FAVORITES": "No favorites found for this filter.", + "RECENT_SOURCES": "Recent Sources", + "MANAGE_ALL": "Manage all", + "NO_SOURCES": "No sources available yet.", + "OPEN_SOURCES": "Open Sources", + "RECENTLY_WATCHED": "Recently Watched", + "ACTIVITY": "Activity", + "NO_HISTORY": "No items found for this filter.", + "SOURCE_STATS": "Source Statistics", + "OVERVIEW": "Overview", + "TOTAL": "Total", + "XTREAM": "Xtream", + "STALKER": "Stalker", + "M3U": "M3U" + } } } diff --git a/apps/web/src/assets/i18n/ru.json b/apps/web/src/assets/i18n/ru.json index 0fb0cb522..4fb240123 100644 --- a/apps/web/src/assets/i18n/ru.json +++ b/apps/web/src/assets/i18n/ru.json @@ -404,5 +404,48 @@ "DOWN_CHANNEL": "Канал вниз", "HEADER": "IPTVNator Дистанционное управление", "FOOTER": "IPTVNator Дистанционное управление" + }, + "WORKSPACE": { + "FAVORITES_ONLY": "Только избранное", + "SORT_DATE_DESC": "Дата добавления (Сначала новые)", + "SORT_DATE_ASC": "Дата добавления (Сначала старые)", + "SORT_NAME_ASC": "Имя (А-Я)", + "SORT_NAME_DESC": "Имя (Я-А)", + "SORT_LABEL": "Сортировка: ", + "DASHBOARD": { + "UNSUPPORTED_WIDGET": "Неподдерживаемый виджет", + "UNSUPPORTED_WIDGET_DESC": "Этот тип виджета недоступен в данной сборке.", + "CONTINUE_WATCHING": "Продолжить просмотр", + "FOCUS": "Фокус", + "UNTITLED_SOURCE": "Источник без названия", + "OPEN_SOURCE": "Открыть источник", + "MANAGE_SOURCES": "Управление источниками", + "NO_ACTIVE_SOURCE": "Активный источник пока не выбран.", + "ADD_SOURCE": "Добавить источник", + "RECENTLY_USED": "Недавно использованные", + "GLOBAL_FAVORITES": "Все фавориты", + "LIBRARY": "Библиотека", + "FILTER_ITEMS": "Фильтровать", + "ALL": "Все", + "CHANNELS": "Каналы", + "VOD": "Видео", + "SERIES": "Сериалы", + "LIST_VIEW": "В виде списка", + "GRID_VIEW": "В виде сетки", + "NO_FAVORITES": "Для этого фильтра нет избранного.", + "RECENT_SOURCES": "Недавние источники", + "MANAGE_ALL": "Управлять всем", + "NO_SOURCES": "Источники пока недоступны.", + "OPEN_SOURCES": "Открыть источники", + "RECENTLY_WATCHED": "Недавно просмотренные", + "ACTIVITY": "Активность", + "NO_HISTORY": "Для этого фильтра элементы не найдены.", + "SOURCE_STATS": "Статистика источников", + "OVERVIEW": "Обзор", + "TOTAL": "Всего", + "XTREAM": "Xtream", + "STALKER": "Stalker", + "M3U": "M3U" + } } } diff --git a/libs/workspace/dashboard/ui/src/lib/dashboard-widget-host.component.html b/libs/workspace/dashboard/ui/src/lib/dashboard-widget-host.component.html index afa84de4f..b8072af43 100644 --- a/libs/workspace/dashboard/ui/src/lib/dashboard-widget-host.component.html +++ b/libs/workspace/dashboard/ui/src/lib/dashboard-widget-host.component.html @@ -16,8 +16,10 @@ } @default {
-

Unsupported widget

-

This widget type is not available in this build.

+

{{ 'WORKSPACE.DASHBOARD.UNSUPPORTED_WIDGET' | translate }}

+

+ {{ 'WORKSPACE.DASHBOARD.UNSUPPORTED_WIDGET_DESC' | translate }} +

} } diff --git a/libs/workspace/dashboard/ui/src/lib/dashboard-widget-host.component.ts b/libs/workspace/dashboard/ui/src/lib/dashboard-widget-host.component.ts index 031b35184..ac0e388fc 100644 --- a/libs/workspace/dashboard/ui/src/lib/dashboard-widget-host.component.ts +++ b/libs/workspace/dashboard/ui/src/lib/dashboard-widget-host.component.ts @@ -1,4 +1,5 @@ import { Component, input } from '@angular/core'; +import { TranslatePipe } from '@ngx-translate/core'; import { DashboardWidgetConfig } from 'workspace-dashboard-data-access'; import { ContinueWatchingWidgetComponent } from './widgets/continue-watching-widget.component'; import { GlobalFavoritesWidgetComponent } from './widgets/global-favorites-widget.component'; @@ -14,6 +15,7 @@ import { SourceStatsWidgetComponent } from './widgets/source-stats-widget.compon RecentSourcesWidgetComponent, RecentlyWatchedWidgetComponent, SourceStatsWidgetComponent, + TranslatePipe, ], templateUrl: './dashboard-widget-host.component.html', styleUrl: './dashboard-widget-host.component.scss', diff --git a/libs/workspace/dashboard/ui/src/lib/widgets/continue-watching-widget.component.html b/libs/workspace/dashboard/ui/src/lib/widgets/continue-watching-widget.component.html index efcc2f4d2..7b35ddcd8 100644 --- a/libs/workspace/dashboard/ui/src/lib/widgets/continue-watching-widget.component.html +++ b/libs/workspace/dashboard/ui/src/lib/widgets/continue-watching-widget.component.html @@ -1,42 +1,63 @@ - + @if (data.activePlaylist(); as active) { } @else { } @if (data.quickRecent().length > 0) {
-

Recently used

+

+ {{ 'WORKSPACE.DASHBOARD.RECENTLY_USED' | translate }} +

@for (item of data.quickRecent(); track item._id) { - - {{ item.title || item.filename || 'Untitled source' }} + + {{ + item.title || + item.filename || + ('WORKSPACE.DASHBOARD.UNTITLED_SOURCE' + | translate) + }} {{ data.getPlaylistProvider(item) }} diff --git a/libs/workspace/dashboard/ui/src/lib/widgets/continue-watching-widget.component.ts b/libs/workspace/dashboard/ui/src/lib/widgets/continue-watching-widget.component.ts index 656cb690e..d4fad50d9 100644 --- a/libs/workspace/dashboard/ui/src/lib/widgets/continue-watching-widget.component.ts +++ b/libs/workspace/dashboard/ui/src/lib/widgets/continue-watching-widget.component.ts @@ -2,12 +2,19 @@ import { Component, inject } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIcon } from '@angular/material/icon'; import { RouterLink } from '@angular/router'; +import { TranslatePipe } from '@ngx-translate/core'; import { DashboardDataService } from 'workspace-dashboard-data-access'; import { DashboardWidgetShellComponent } from './dashboard-widget-shell.component'; @Component({ selector: 'app-continue-watching-widget', - imports: [MatButtonModule, MatIcon, RouterLink, DashboardWidgetShellComponent], + imports: [ + MatButtonModule, + MatIcon, + RouterLink, + DashboardWidgetShellComponent, + TranslatePipe, + ], templateUrl: './continue-watching-widget.component.html', styleUrl: './continue-watching-widget.component.scss', }) diff --git a/libs/workspace/dashboard/ui/src/lib/widgets/global-favorites-widget.component.html b/libs/workspace/dashboard/ui/src/lib/widgets/global-favorites-widget.component.html index 26d07d867..616daa189 100644 --- a/libs/workspace/dashboard/ui/src/lib/widgets/global-favorites-widget.component.html +++ b/libs/workspace/dashboard/ui/src/lib/widgets/global-favorites-widget.component.html @@ -1,10 +1,13 @@ - + @@ -14,7 +17,9 @@ selectedKind() === 'all' ? 'check' : '' }} All ({{ counts().all }}){{ 'WORKSPACE.DASHBOARD.ALL' | translate }} ({{ + counts().all + }}) @@ -50,10 +61,16 @@ aria-label="Select global favorites view mode" (change)="onViewModeChange($event.value)" > - + view_list - + grid_view @@ -61,7 +78,7 @@ diff --git a/libs/workspace/dashboard/ui/src/lib/widgets/global-favorites-widget.component.ts b/libs/workspace/dashboard/ui/src/lib/widgets/global-favorites-widget.component.ts index bfa1de360..d5203fbb2 100644 --- a/libs/workspace/dashboard/ui/src/lib/widgets/global-favorites-widget.component.ts +++ b/libs/workspace/dashboard/ui/src/lib/widgets/global-favorites-widget.component.ts @@ -3,6 +3,8 @@ import { MatButtonModule } from '@angular/material/button'; import { MatButtonToggleModule } from '@angular/material/button-toggle'; import { MatIcon } from '@angular/material/icon'; import { MatMenuModule } from '@angular/material/menu'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { TranslatePipe } from '@ngx-translate/core'; import { DashboardContentKind, DashboardDataService, @@ -30,8 +32,10 @@ const PAGE_SIZE = 20; MatButtonToggleModule, MatMenuModule, MatIcon, + MatTooltipModule, DashboardActivityItemsComponent, DashboardWidgetShellComponent, + TranslatePipe, ], templateUrl: './global-favorites-widget.component.html', styleUrl: './global-favorites-widget.component.scss', diff --git a/libs/workspace/dashboard/ui/src/lib/widgets/recent-sources-widget.component.html b/libs/workspace/dashboard/ui/src/lib/widgets/recent-sources-widget.component.html index 71607f26d..763b9ef40 100644 --- a/libs/workspace/dashboard/ui/src/lib/widgets/recent-sources-widget.component.html +++ b/libs/workspace/dashboard/ui/src/lib/widgets/recent-sources-widget.component.html @@ -1,5 +1,10 @@ - - Manage all + + {{ + 'WORKSPACE.DASHBOARD.MANAGE_ALL' | translate + }} @if (data.recentPlaylists().length > 0) {
@for (item of data.recentPlaylists(); track item._id) { @@ -9,20 +14,44 @@ {{ data.getPlaylistProvider(item) }} @if (item.count) { - {{ item.count }} channels + {{ item.count }} + {{ + 'WORKSPACE.DASHBOARD.CHANNELS' | translate + }} }
-

- {{ item.title || item.filename || 'Untitled source' }} +

+ {{ + item.title || + item.filename || + ('WORKSPACE.DASHBOARD.UNTITLED_SOURCE' + | translate) + }}

-

{{ data.formatTimestamp(item.updateDate || item.importDate) }}

+

+ {{ + data.formatTimestamp( + item.updateDate || item.importDate + ) + }} +

}
} @else { }
diff --git a/libs/workspace/dashboard/ui/src/lib/widgets/recent-sources-widget.component.ts b/libs/workspace/dashboard/ui/src/lib/widgets/recent-sources-widget.component.ts index 6212a185a..c11b5b368 100644 --- a/libs/workspace/dashboard/ui/src/lib/widgets/recent-sources-widget.component.ts +++ b/libs/workspace/dashboard/ui/src/lib/widgets/recent-sources-widget.component.ts @@ -1,11 +1,12 @@ import { Component, inject } from '@angular/core'; import { RouterLink } from '@angular/router'; +import { TranslatePipe } from '@ngx-translate/core'; import { DashboardDataService } from 'workspace-dashboard-data-access'; import { DashboardWidgetShellComponent } from './dashboard-widget-shell.component'; @Component({ selector: 'app-recent-sources-widget', - imports: [RouterLink, DashboardWidgetShellComponent], + imports: [RouterLink, DashboardWidgetShellComponent, TranslatePipe], templateUrl: './recent-sources-widget.component.html', styleUrl: './recent-sources-widget.component.scss', }) diff --git a/libs/workspace/dashboard/ui/src/lib/widgets/recently-watched-widget.component.html b/libs/workspace/dashboard/ui/src/lib/widgets/recently-watched-widget.component.html index a55d803b1..c9fd5f005 100644 --- a/libs/workspace/dashboard/ui/src/lib/widgets/recently-watched-widget.component.html +++ b/libs/workspace/dashboard/ui/src/lib/widgets/recently-watched-widget.component.html @@ -1,10 +1,13 @@ - + @@ -14,7 +17,9 @@ selectedKind() === 'all' ? 'check' : '' }} All ({{ counts().all }}){{ 'WORKSPACE.DASHBOARD.ALL' | translate }} ({{ + counts().all + }}) @@ -50,10 +61,16 @@ aria-label="Select recently watched view mode" (change)="onViewModeChange($event.value)" > - + view_list - + grid_view @@ -61,7 +78,7 @@ diff --git a/libs/workspace/dashboard/ui/src/lib/widgets/recently-watched-widget.component.ts b/libs/workspace/dashboard/ui/src/lib/widgets/recently-watched-widget.component.ts index 5507ad49c..71edb2e21 100644 --- a/libs/workspace/dashboard/ui/src/lib/widgets/recently-watched-widget.component.ts +++ b/libs/workspace/dashboard/ui/src/lib/widgets/recently-watched-widget.component.ts @@ -3,6 +3,8 @@ import { MatButtonModule } from '@angular/material/button'; import { MatButtonToggleModule } from '@angular/material/button-toggle'; import { MatIcon } from '@angular/material/icon'; import { MatMenuModule } from '@angular/material/menu'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { TranslatePipe } from '@ngx-translate/core'; import { DashboardContentKind, DashboardDataService, @@ -30,8 +32,10 @@ const PAGE_SIZE = 20; MatButtonToggleModule, MatMenuModule, MatIcon, + MatTooltipModule, DashboardActivityItemsComponent, DashboardWidgetShellComponent, + TranslatePipe, ], templateUrl: './recently-watched-widget.component.html', styleUrl: './recently-watched-widget.component.scss', diff --git a/libs/workspace/dashboard/ui/src/lib/widgets/source-stats-widget.component.html b/libs/workspace/dashboard/ui/src/lib/widgets/source-stats-widget.component.html index 3231b81d7..23300cab8 100644 --- a/libs/workspace/dashboard/ui/src/lib/widgets/source-stats-widget.component.html +++ b/libs/workspace/dashboard/ui/src/lib/widgets/source-stats-widget.component.html @@ -1,19 +1,22 @@ - +
-

Total

+

{{ 'WORKSPACE.DASHBOARD.TOTAL' | translate }}

{{ data.stats().total }}

-

Xtream

+

{{ 'WORKSPACE.DASHBOARD.XTREAM' | translate }}

{{ data.stats().xtream }}

-

Stalker

+

{{ 'WORKSPACE.DASHBOARD.STALKER' | translate }}

{{ data.stats().stalker }}

-

M3U

+

{{ 'WORKSPACE.DASHBOARD.M3U' | translate }}

{{ data.stats().m3u }}

diff --git a/libs/workspace/dashboard/ui/src/lib/widgets/source-stats-widget.component.ts b/libs/workspace/dashboard/ui/src/lib/widgets/source-stats-widget.component.ts index b4b2f9508..98a7fbadb 100644 --- a/libs/workspace/dashboard/ui/src/lib/widgets/source-stats-widget.component.ts +++ b/libs/workspace/dashboard/ui/src/lib/widgets/source-stats-widget.component.ts @@ -1,10 +1,11 @@ import { Component, inject } from '@angular/core'; +import { TranslatePipe } from '@ngx-translate/core'; import { DashboardDataService } from 'workspace-dashboard-data-access'; import { DashboardWidgetShellComponent } from './dashboard-widget-shell.component'; @Component({ selector: 'app-source-stats-widget', - imports: [DashboardWidgetShellComponent], + imports: [DashboardWidgetShellComponent, TranslatePipe], templateUrl: './source-stats-widget.component.html', styleUrl: './source-stats-widget.component.scss', })