diff --git a/apps/web/src/assets/i18n/ar.json b/apps/web/src/assets/i18n/ar.json
index 4ecacc5a4..76e26f667 100644
--- a/apps/web/src/assets/i18n/ar.json
+++ b/apps/web/src/assets/i18n/ar.json
@@ -853,6 +853,11 @@
"CONTINUE_WATCHING": "متابعة المشاهدة",
"LIVE_FAVORITES": "يُبث الآن على المفضلة لديك",
"LIVE_RECENT": "متابعة البث المباشر",
+ "REMAINING_SECONDS": "متبقٍ {{seconds}} ث",
+ "REMAINING_MINUTES": "متبقٍ {{minutes}} د",
+ "REMAINING_HOURS": "متبقٍ {{hours}} س",
+ "REMAINING_HOURS_MINUTES": "متبقٍ {{hours}} س {{minutes}} د",
+ "PERCENT_WATCHED": "تمت مشاهدة {{value}}%",
"UNTITLED_SOURCE": "مصدر بدون عنوان",
"GLOBAL_FAVORITES": "المفضلة العامة",
"REMOVE_FROM_HISTORY": "إزالة من السجل",
diff --git a/apps/web/src/assets/i18n/ary.json b/apps/web/src/assets/i18n/ary.json
index facc7890c..fedc92b2c 100644
--- a/apps/web/src/assets/i18n/ary.json
+++ b/apps/web/src/assets/i18n/ary.json
@@ -853,6 +853,11 @@
"CONTINUE_WATCHING": "كمل المشاهدة",
"LIVE_FAVORITES": "مباشر دابا على المفضلة ديالك",
"LIVE_RECENT": "كمل مع المباشر",
+ "REMAINING_SECONDS": "باقي {{seconds}} ث",
+ "REMAINING_MINUTES": "باقي {{minutes}} د",
+ "REMAINING_HOURS": "باقي {{hours}} س",
+ "REMAINING_HOURS_MINUTES": "باقي {{hours}} س {{minutes}} د",
+ "PERCENT_WATCHED": "تشاهد {{value}}%",
"UNTITLED_SOURCE": "مصدر بلا عنوان",
"GLOBAL_FAVORITES": "المفضلة العامة",
"REMOVE_FROM_HISTORY": "حيد من السجل",
diff --git a/apps/web/src/assets/i18n/by.json b/apps/web/src/assets/i18n/by.json
index 4654352c0..e10779332 100644
--- a/apps/web/src/assets/i18n/by.json
+++ b/apps/web/src/assets/i18n/by.json
@@ -853,6 +853,11 @@
"CONTINUE_WATCHING": "Працягнуць прагляд",
"LIVE_FAVORITES": "Цяпер у эфіры на выбраных",
"LIVE_RECENT": "Працягнуць прагляд эфіру",
+ "REMAINING_SECONDS": "Засталося {{seconds}} с",
+ "REMAINING_MINUTES": "Засталося {{minutes}} хв",
+ "REMAINING_HOURS": "Засталося {{hours}} г",
+ "REMAINING_HOURS_MINUTES": "Засталося {{hours}} г {{minutes}} хв",
+ "PERCENT_WATCHED": "Прагледжана {{value}}%",
"UNTITLED_SOURCE": "Крыніца без назвы",
"GLOBAL_FAVORITES": "Усе фаварыты",
"REMOVE_FROM_HISTORY": "Выдаліць з гісторыі",
diff --git a/apps/web/src/assets/i18n/de.json b/apps/web/src/assets/i18n/de.json
index aa51b7a9a..4de9bb902 100644
--- a/apps/web/src/assets/i18n/de.json
+++ b/apps/web/src/assets/i18n/de.json
@@ -853,6 +853,11 @@
"CONTINUE_WATCHING": "Weiter schauen",
"LIVE_FAVORITES": "Jetzt live auf Ihren Favoriten",
"LIVE_RECENT": "Mit Live-TV weiterschauen",
+ "REMAINING_SECONDS": "Noch {{seconds}} s",
+ "REMAINING_MINUTES": "Noch {{minutes}} Min.",
+ "REMAINING_HOURS": "Noch {{hours}} Std.",
+ "REMAINING_HOURS_MINUTES": "Noch {{hours}} Std. {{minutes}} Min.",
+ "PERCENT_WATCHED": "{{value}}% angesehen",
"UNTITLED_SOURCE": "Unbenannte Quelle",
"GLOBAL_FAVORITES": "Globale Favoriten",
"REMOVE_FROM_HISTORY": "Aus Verlauf entfernen",
diff --git a/apps/web/src/assets/i18n/el.json b/apps/web/src/assets/i18n/el.json
index 53ced9c68..02df9c93a 100644
--- a/apps/web/src/assets/i18n/el.json
+++ b/apps/web/src/assets/i18n/el.json
@@ -853,6 +853,11 @@
"CONTINUE_WATCHING": "Συνέχιση παρακολούθησης",
"LIVE_FAVORITES": "Ζωντανά τώρα στα αγαπημένα σας",
"LIVE_RECENT": "Συνέχιση με ζωντανή TV",
+ "REMAINING_SECONDS": "Απομένουν {{seconds}} δ",
+ "REMAINING_MINUTES": "Απομένουν {{minutes}} λ",
+ "REMAINING_HOURS": "Απομένουν {{hours}} ώ",
+ "REMAINING_HOURS_MINUTES": "Απομένουν {{hours}} ώ {{minutes}} λ",
+ "PERCENT_WATCHED": "Προβλήθηκε {{value}}%",
"UNTITLED_SOURCE": "Πηγή χωρίς τίτλο",
"GLOBAL_FAVORITES": "Βασικά Αγαπημένα",
"REMOVE_FROM_HISTORY": "Αφαίρεση από το ιστορικό",
diff --git a/apps/web/src/assets/i18n/en.json b/apps/web/src/assets/i18n/en.json
index efde90ae8..3f1ab3fb5 100644
--- a/apps/web/src/assets/i18n/en.json
+++ b/apps/web/src/assets/i18n/en.json
@@ -853,6 +853,11 @@
"CONTINUE_WATCHING": "Continue Watching",
"LIVE_FAVORITES": "Live now on your favorites",
"LIVE_RECENT": "Continue with live TV",
+ "REMAINING_SECONDS": "{{seconds}}s left",
+ "REMAINING_MINUTES": "{{minutes}}m left",
+ "REMAINING_HOURS": "{{hours}}h left",
+ "REMAINING_HOURS_MINUTES": "{{hours}}h {{minutes}}m left",
+ "PERCENT_WATCHED": "{{value}}% watched",
"UNTITLED_SOURCE": "Untitled source",
"GLOBAL_FAVORITES": "Global Favorites",
"REMOVE_FROM_HISTORY": "Remove from history",
diff --git a/apps/web/src/assets/i18n/es.json b/apps/web/src/assets/i18n/es.json
index 637be0594..915a13d36 100644
--- a/apps/web/src/assets/i18n/es.json
+++ b/apps/web/src/assets/i18n/es.json
@@ -853,6 +853,11 @@
"CONTINUE_WATCHING": "Continuar viendo",
"LIVE_FAVORITES": "Ahora en vivo en tus favoritos",
"LIVE_RECENT": "Continuar con TV en vivo",
+ "REMAINING_SECONDS": "Quedan {{seconds}} s",
+ "REMAINING_MINUTES": "Quedan {{minutes}} min",
+ "REMAINING_HOURS": "Quedan {{hours}} h",
+ "REMAINING_HOURS_MINUTES": "Quedan {{hours}} h {{minutes}} min",
+ "PERCENT_WATCHED": "{{value}}% visto",
"UNTITLED_SOURCE": "Fuente sin título",
"GLOBAL_FAVORITES": "Favoritos globales",
"REMOVE_FROM_HISTORY": "Quitar del historial",
diff --git a/apps/web/src/assets/i18n/fr.json b/apps/web/src/assets/i18n/fr.json
index 161c3c106..d725f6ba9 100644
--- a/apps/web/src/assets/i18n/fr.json
+++ b/apps/web/src/assets/i18n/fr.json
@@ -853,6 +853,11 @@
"CONTINUE_WATCHING": "Reprendre la lecture",
"LIVE_FAVORITES": "En direct sur vos favoris",
"LIVE_RECENT": "Reprendre la TV en direct",
+ "REMAINING_SECONDS": "Il reste {{seconds}} s",
+ "REMAINING_MINUTES": "Il reste {{minutes}} min",
+ "REMAINING_HOURS": "Il reste {{hours}} h",
+ "REMAINING_HOURS_MINUTES": "Il reste {{hours}} h {{minutes}} min",
+ "PERCENT_WATCHED": "{{value}}% regardé",
"UNTITLED_SOURCE": "Source sans titre",
"GLOBAL_FAVORITES": "Favoris globaux",
"REMOVE_FROM_HISTORY": "Retirer de l'historique",
diff --git a/apps/web/src/assets/i18n/it.json b/apps/web/src/assets/i18n/it.json
index d527787ab..8495b34cb 100644
--- a/apps/web/src/assets/i18n/it.json
+++ b/apps/web/src/assets/i18n/it.json
@@ -853,6 +853,11 @@
"CONTINUE_WATCHING": "Continua a guardare",
"LIVE_FAVORITES": "Live ora sui tuoi preferiti",
"LIVE_RECENT": "Continua con la TV live",
+ "REMAINING_SECONDS": "Mancano {{seconds}} s",
+ "REMAINING_MINUTES": "Mancano {{minutes}} min",
+ "REMAINING_HOURS": "Mancano {{hours}} h",
+ "REMAINING_HOURS_MINUTES": "Mancano {{hours}} h {{minutes}} min",
+ "PERCENT_WATCHED": "{{value}}% visto",
"UNTITLED_SOURCE": "Sorgente senza titolo",
"GLOBAL_FAVORITES": "Preferiti globali",
"REMOVE_FROM_HISTORY": "Rimuovi dalla cronologia",
diff --git a/apps/web/src/assets/i18n/ja.json b/apps/web/src/assets/i18n/ja.json
index 44817f13f..d0ce7368a 100644
--- a/apps/web/src/assets/i18n/ja.json
+++ b/apps/web/src/assets/i18n/ja.json
@@ -853,6 +853,11 @@
"CONTINUE_WATCHING": "視聴を再開",
"LIVE_FAVORITES": "お気に入りで現在放送中",
"LIVE_RECENT": "ライブTVの視聴を再開",
+ "REMAINING_SECONDS": "残り{{seconds}}秒",
+ "REMAINING_MINUTES": "残り{{minutes}}分",
+ "REMAINING_HOURS": "残り{{hours}}時間",
+ "REMAINING_HOURS_MINUTES": "残り{{hours}}時間{{minutes}}分",
+ "PERCENT_WATCHED": "{{value}}% 視聴済み",
"UNTITLED_SOURCE": "無題のソース",
"GLOBAL_FAVORITES": "グローバルお気に入り",
"REMOVE_FROM_HISTORY": "履歴から削除",
diff --git a/apps/web/src/assets/i18n/ko.json b/apps/web/src/assets/i18n/ko.json
index 3e5849dc4..bb72c1867 100644
--- a/apps/web/src/assets/i18n/ko.json
+++ b/apps/web/src/assets/i18n/ko.json
@@ -853,6 +853,11 @@
"CONTINUE_WATCHING": "이어서 시청",
"LIVE_FAVORITES": "즐겨찾기에서 지금 라이브 중인 채널입니다",
"LIVE_RECENT": "라이브 TV 이어서 시청",
+ "REMAINING_SECONDS": "{{seconds}}초 남음",
+ "REMAINING_MINUTES": "{{minutes}}분 남음",
+ "REMAINING_HOURS": "{{hours}}시간 남음",
+ "REMAINING_HOURS_MINUTES": "{{hours}}시간 {{minutes}}분 남음",
+ "PERCENT_WATCHED": "{{value}}% 시청함",
"UNTITLED_SOURCE": "제목 없는 소스",
"GLOBAL_FAVORITES": "전체 즐겨찾기",
"REMOVE_FROM_HISTORY": "기록에서 제거",
diff --git a/apps/web/src/assets/i18n/nl.json b/apps/web/src/assets/i18n/nl.json
index f11ced48f..1130411fe 100644
--- a/apps/web/src/assets/i18n/nl.json
+++ b/apps/web/src/assets/i18n/nl.json
@@ -853,6 +853,11 @@
"CONTINUE_WATCHING": "Verder kijken",
"LIVE_FAVORITES": "Nu live op je favorieten",
"LIVE_RECENT": "Verder kijken met live tv",
+ "REMAINING_SECONDS": "Nog {{seconds}} sec",
+ "REMAINING_MINUTES": "Nog {{minutes}} min",
+ "REMAINING_HOURS": "Nog {{hours}} u",
+ "REMAINING_HOURS_MINUTES": "Nog {{hours}} u {{minutes}} min",
+ "PERCENT_WATCHED": "{{value}}% bekeken",
"UNTITLED_SOURCE": "Naamloze bron",
"GLOBAL_FAVORITES": "Algemene favorieten",
"REMOVE_FROM_HISTORY": "Verwijderen uit geschiedenis",
diff --git a/apps/web/src/assets/i18n/pl.json b/apps/web/src/assets/i18n/pl.json
index 82a01f75d..e472e8d85 100644
--- a/apps/web/src/assets/i18n/pl.json
+++ b/apps/web/src/assets/i18n/pl.json
@@ -853,6 +853,11 @@
"CONTINUE_WATCHING": "Kontynuuj oglądanie",
"LIVE_FAVORITES": "Na żywo w Twoich ulubionych",
"LIVE_RECENT": "Kontynuuj oglądanie na żywo",
+ "REMAINING_SECONDS": "Pozostało {{seconds}} s",
+ "REMAINING_MINUTES": "Pozostało {{minutes}} min",
+ "REMAINING_HOURS": "Pozostało {{hours}} godz.",
+ "REMAINING_HOURS_MINUTES": "Pozostało {{hours}} godz. {{minutes}} min",
+ "PERCENT_WATCHED": "Obejrzano {{value}}%",
"UNTITLED_SOURCE": "Źródło bez tytułu",
"GLOBAL_FAVORITES": "Globalne ulubione",
"REMOVE_FROM_HISTORY": "Usuń z historii",
diff --git a/apps/web/src/assets/i18n/pt.json b/apps/web/src/assets/i18n/pt.json
index 4715f2cc8..62fd8bd5f 100644
--- a/apps/web/src/assets/i18n/pt.json
+++ b/apps/web/src/assets/i18n/pt.json
@@ -853,6 +853,11 @@
"CONTINUE_WATCHING": "Continuar assistindo",
"LIVE_FAVORITES": "Ao vivo agora nos seus favoritos",
"LIVE_RECENT": "Continuar com TV ao vivo",
+ "REMAINING_SECONDS": "Restam {{seconds}} s",
+ "REMAINING_MINUTES": "Restam {{minutes}} min",
+ "REMAINING_HOURS": "Restam {{hours}} h",
+ "REMAINING_HOURS_MINUTES": "Restam {{hours}} h {{minutes}} min",
+ "PERCENT_WATCHED": "{{value}}% assistido",
"UNTITLED_SOURCE": "Fonte sem título",
"GLOBAL_FAVORITES": "Favoritos globais",
"REMOVE_FROM_HISTORY": "Remover do histórico",
diff --git a/apps/web/src/assets/i18n/ru.json b/apps/web/src/assets/i18n/ru.json
index d51948a34..d601f5a73 100644
--- a/apps/web/src/assets/i18n/ru.json
+++ b/apps/web/src/assets/i18n/ru.json
@@ -853,6 +853,11 @@
"CONTINUE_WATCHING": "Продолжить просмотр",
"LIVE_FAVORITES": "Сейчас в эфире на избранных каналах",
"LIVE_RECENT": "Продолжить просмотр прямого эфира",
+ "REMAINING_SECONDS": "Осталось {{seconds}} с",
+ "REMAINING_MINUTES": "Осталось {{minutes}} мин",
+ "REMAINING_HOURS": "Осталось {{hours}} ч",
+ "REMAINING_HOURS_MINUTES": "Осталось {{hours}} ч {{minutes}} мин",
+ "PERCENT_WATCHED": "Просмотрено {{value}}%",
"UNTITLED_SOURCE": "Источник без названия",
"GLOBAL_FAVORITES": "Глобальные избранные",
"REMOVE_FROM_HISTORY": "Удалить из истории",
diff --git a/apps/web/src/assets/i18n/tr.json b/apps/web/src/assets/i18n/tr.json
index aa1a0354a..8e14010d4 100644
--- a/apps/web/src/assets/i18n/tr.json
+++ b/apps/web/src/assets/i18n/tr.json
@@ -853,6 +853,11 @@
"CONTINUE_WATCHING": "İzlemeye Devam Et",
"LIVE_FAVORITES": "Favorilerinizde şu an canlı",
"LIVE_RECENT": "Canlı TV ile devam et",
+ "REMAINING_SECONDS": "{{seconds}} sn kaldı",
+ "REMAINING_MINUTES": "{{minutes}} dk kaldı",
+ "REMAINING_HOURS": "{{hours}} sa kaldı",
+ "REMAINING_HOURS_MINUTES": "{{hours}} sa {{minutes}} dk kaldı",
+ "PERCENT_WATCHED": "{{value}}% izlendi",
"UNTITLED_SOURCE": "Adsız kaynak",
"GLOBAL_FAVORITES": "Tüm Favoriler",
"REMOVE_FROM_HISTORY": "Geçmişten kaldır",
diff --git a/apps/web/src/assets/i18n/zh.json b/apps/web/src/assets/i18n/zh.json
index 2d2f6c20b..0fdc1f1f5 100644
--- a/apps/web/src/assets/i18n/zh.json
+++ b/apps/web/src/assets/i18n/zh.json
@@ -853,6 +853,11 @@
"CONTINUE_WATCHING": "继续观看",
"LIVE_FAVORITES": "收藏频道正在播放",
"LIVE_RECENT": "继续观看直播",
+ "REMAINING_SECONDS": "剩余 {{seconds}} 秒",
+ "REMAINING_MINUTES": "剩余 {{minutes}} 分钟",
+ "REMAINING_HOURS": "剩余 {{hours}} 小时",
+ "REMAINING_HOURS_MINUTES": "剩余 {{hours}} 小时 {{minutes}} 分钟",
+ "PERCENT_WATCHED": "已观看 {{value}}%",
"UNTITLED_SOURCE": "未命名源",
"GLOBAL_FAVORITES": "全局收藏",
"REMOVE_FROM_HISTORY": "从历史记录中移除",
diff --git a/apps/web/src/assets/i18n/zhtw.json b/apps/web/src/assets/i18n/zhtw.json
index ae4e893a1..ca6c6a89d 100644
--- a/apps/web/src/assets/i18n/zhtw.json
+++ b/apps/web/src/assets/i18n/zhtw.json
@@ -853,6 +853,11 @@
"CONTINUE_WATCHING": "繼續觀看",
"LIVE_FAVORITES": "我的最愛正在直播",
"LIVE_RECENT": "繼續觀看直播電視",
+ "REMAINING_SECONDS": "剩餘 {{seconds}} 秒",
+ "REMAINING_MINUTES": "剩餘 {{minutes}} 分鐘",
+ "REMAINING_HOURS": "剩餘 {{hours}} 小時",
+ "REMAINING_HOURS_MINUTES": "剩餘 {{hours}} 小時 {{minutes}} 分鐘",
+ "PERCENT_WATCHED": "已觀看 {{value}}%",
"UNTITLED_SOURCE": "未命名的來源",
"GLOBAL_FAVORITES": "全域我的最愛",
"REMOVE_FROM_HISTORY": "從歷史記錄中移除",
diff --git a/libs/workspace/dashboard/feature/src/lib/rails/workspace-dashboard-rails.component.html b/libs/workspace/dashboard/feature/src/lib/rails/workspace-dashboard-rails.component.html
index 86d9db6bf..1ed8e2679 100644
--- a/libs/workspace/dashboard/feature/src/lib/rails/workspace-dashboard-rails.component.html
+++ b/libs/workspace/dashboard/feature/src/lib/rails/workspace-dashboard-rails.component.html
@@ -85,12 +85,18 @@
@if (item.remainingLabel) {
- {{ item.remainingLabel }}
+ {{
+ item.remainingLabel.key
+ | translate: item.remainingLabel.params
+ }}
·
}
- {{ item.watchProgress }}% watched
+ {{
+ 'WORKSPACE.DASHBOARD.PERCENT_WATCHED'
+ | translate: { value: item.watchProgress }
+ }}
}
diff --git a/libs/workspace/dashboard/feature/src/lib/rails/workspace-dashboard-rails.component.spec.ts b/libs/workspace/dashboard/feature/src/lib/rails/workspace-dashboard-rails.component.spec.ts
index befac9136..66d2dc2fa 100644
--- a/libs/workspace/dashboard/feature/src/lib/rails/workspace-dashboard-rails.component.spec.ts
+++ b/libs/workspace/dashboard/feature/src/lib/rails/workspace-dashboard-rails.component.spec.ts
@@ -349,40 +349,55 @@ describe('playback-position helpers', () => {
).toBeNull();
});
- it('formats sub-minute, minute, and hour-spanning remainders', () => {
+ it('returns translation keys and params for sub-minute, minute, and hour-spanning remainders', () => {
expect(
formatRemainingLabel({
positionSeconds: 5970,
durationSeconds: 6000,
})
- ).toBe('30s left');
+ ).toEqual({
+ key: 'WORKSPACE.DASHBOARD.REMAINING_SECONDS',
+ params: { seconds: 30 },
+ });
expect(
formatRemainingLabel({
positionSeconds: 0,
durationSeconds: 1800,
})
- ).toBe('30m left');
+ ).toEqual({
+ key: 'WORKSPACE.DASHBOARD.REMAINING_MINUTES',
+ params: { minutes: 30 },
+ });
expect(
formatRemainingLabel({
positionSeconds: 0,
durationSeconds: 3600,
})
- ).toBe('1h left');
+ ).toEqual({
+ key: 'WORKSPACE.DASHBOARD.REMAINING_HOURS',
+ params: { hours: 1 },
+ });
expect(
formatRemainingLabel({
positionSeconds: 600,
durationSeconds: 6840,
})
- ).toBe('1h 44m left');
+ ).toEqual({
+ key: 'WORKSPACE.DASHBOARD.REMAINING_HOURS_MINUTES',
+ params: { hours: 1, minutes: 44 },
+ });
});
- it('clamps below zero — a completed program reads as "0s left" not negative', () => {
+ it('clamps below zero to the translated zero-seconds state', () => {
expect(
formatRemainingLabel({
positionSeconds: 7000,
durationSeconds: 6000,
})
- ).toBe('0s left');
+ ).toEqual({
+ key: 'WORKSPACE.DASHBOARD.REMAINING_SECONDS',
+ params: { seconds: 0 },
+ });
});
});
});
diff --git a/libs/workspace/dashboard/feature/src/lib/rails/workspace-dashboard-rails.component.ts b/libs/workspace/dashboard/feature/src/lib/rails/workspace-dashboard-rails.component.ts
index be70d17be..2165d4a8d 100644
--- a/libs/workspace/dashboard/feature/src/lib/rails/workspace-dashboard-rails.component.ts
+++ b/libs/workspace/dashboard/feature/src/lib/rails/workspace-dashboard-rails.component.ts
@@ -76,8 +76,12 @@ interface DashboardHeroModel {
readonly title: string;
/** 0–100 watched, when a resume position is known. */
readonly watchProgress?: number | null;
- /** "1h 04m left" — pre-formatted for direct rendering. */
- readonly remainingLabel?: string | null;
+ readonly remainingLabel?: DashboardRemainingLabel | null;
+}
+
+export interface DashboardRemainingLabel {
+ readonly key: string;
+ readonly params: Record;
}
export type DashboardHeroBackdropSource = 'backdrop' | 'poster' | 'fallback';
@@ -334,7 +338,7 @@ export function playbackProgressPercent(
export function formatRemainingLabel(
position: { positionSeconds: number; durationSeconds?: number } | null
-): string | null {
+): DashboardRemainingLabel | null {
if (
!position ||
position.durationSeconds == null ||
@@ -347,18 +351,30 @@ export function formatRemainingLabel(
Math.round(position.durationSeconds - position.positionSeconds)
);
if (remaining < 60) {
- return `${remaining}s left`;
+ return {
+ key: 'WORKSPACE.DASHBOARD.REMAINING_SECONDS',
+ params: { seconds: remaining },
+ };
}
const totalMinutes = Math.round(remaining / 60);
if (totalMinutes < 60) {
- return `${totalMinutes}m left`;
+ return {
+ key: 'WORKSPACE.DASHBOARD.REMAINING_MINUTES',
+ params: { minutes: totalMinutes },
+ };
}
const hours = Math.floor(totalMinutes / 60);
const minutes = totalMinutes % 60;
if (minutes === 0) {
- return `${hours}h left`;
+ return {
+ key: 'WORKSPACE.DASHBOARD.REMAINING_HOURS',
+ params: { hours },
+ };
}
- return `${hours}h ${minutes.toString().padStart(2, '0')}m left`;
+ return {
+ key: 'WORKSPACE.DASHBOARD.REMAINING_HOURS_MINUTES',
+ params: { hours, minutes },
+ };
}
function isXtreamAccountPlaylist(
@@ -401,6 +417,10 @@ export class WorkspaceDashboardRailsComponent {
private readonly snackBar = inject(MatSnackBar);
private readonly store = inject(Store);
private readonly translate = inject(TranslateService);
+ private readonly languageTick = toSignal(
+ this.translate.onLangChange.pipe(startWith(null)),
+ { initialValue: null }
+ );
private readonly shellActions = inject(WORKSPACE_SHELL_ACTIONS);
private readonly epgService = inject(EpgService);
@@ -447,12 +467,13 @@ export class WorkspaceDashboardRailsComponent {
// (movies/series) get one rail; live channels get their own. Two card
// formats — one rail each — so users can scan each surface at a glance
// instead of decoding a mixed grid of fallback posters and channel logos.
- readonly continueWatchingCards = computed(() =>
- this.data
+ readonly continueWatchingCards = computed(() => {
+ this.languageTick();
+ return this.data
.globalRecentVodItems()
.slice(0, RAIL_ITEM_LIMIT)
- .map((item) => this.toRecentCard(item))
- );
+ .map((item) => this.toRecentCard(item));
+ });
// Live rail source — favorited live channels take precedence so the EPG
// enrichment lands on the channels the user actually cares about. When