Skip to content

Commit c8be55a

Browse files
committed
feat(i18n): localize analytics dashboard and dynamic platform selector
- add translation keys for analytics stats and time units in AR/EN locales - replace hardcoded time formats in AnalyticsLoader with dynamic i18n values - implement dynamic translation mapping for the analytics platform dropdown - update README project structure
1 parent a885dd5 commit c8be55a

6 files changed

Lines changed: 56 additions & 16 deletions

File tree

README.md

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -152,34 +152,28 @@ Configure your limits, tracking mode, block condition, block duration, theme, an
152152

153153
```text
154154
src/
155-
├── adapters/ # Chrome-specific implementations (Composition Layer)
156-
│ └── chrome/
157-
│ ├── alarm-manager.ts
158-
│ ├── connection-manager.ts
159-
│ ├── message-bus.ts
160-
│ ├── storage-driver.ts
161-
│ └── tab-manager.ts
155+
├── adapters/ # Environment-specific implementations (Composition Layer)
156+
│ ├── browser/ # Browser-agnostic web APIs (e.g., IndexedDB)
157+
│ └── chrome/ # Chrome-specific extension APIs
162158
├── app/
163159
│ └── orchestrator.ts # Wires core components, owns the content-side block flow
164160
├── core/
161+
│ ├── analytics/ # Continuous tracking, aggregators, formatters, and reports
165162
│ ├── interfaces/ # Abstract contracts (Ports) - no platform dependencies
166-
│ │ ├── alarm-manager.ts
167-
│ │ ├── connection-manager.ts
168-
│ │ ├── message-bus.ts
169-
│ │ ├── platform-adapter.ts
170-
│ │ └── tab-manager.ts
171163
│ ├── storage/ # Persistence layer - settings, stats, sessions, security
164+
│ ├── subscription/ # Centralized policy engine (plans, capabilities, limits)
172165
│ ├── background-orchestrator.ts # Environment-agnostic background logic
173166
│ ├── limiter.ts # Pure counting and limit enforcement logic
174167
│ ├── session.ts # Heartbeat, activity tracking, and unload handling
175168
│ ├── tracker.ts # Delegates URL observation to the active adapter
176169
│ └── messenger.ts # Typed message bus wrapper
177170
├── platforms/
178-
│ ├── youtube/index.ts # YouTube Shorts + Watch adapter (with hot-swap support)
179-
│ └── generic/index.ts # Fallback adapter
171+
│ ├── instagram/ # Instagram specific parsers and enforcers
172+
│ ├── youtube/ # YouTube specific parsers and enforcers
173+
│ └── generic/ # Fallback adapter
180174
├── ui/
181175
│ ├── popup/ # Extension popup UI
182-
│ ├── settings/ # Command Center dashboard
176+
│ ├── settings/ # Command Center dashboard & analytics
183177
│ ├── overlay/ # Blocking screen (renderer, controller, persistence)
184178
│ └── components/ # Reusable UI components (tooltip, modal, custom-select)
185179
├── i18n/ # Internationalization (types, singleton, locale files)

src/i18n/locales/ar.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,12 @@ const ar: LocaleStrings = {
7878
condAnd: 'مرن (و)',
7979
descCondOr: 'تفعيل الحظر بمجرد انتهاء أي عداد (الوقت أو الفيديوهات).',
8080
descCondAnd: 'تفعيل الحظر فقط عند انتهاء كلا العدادين معاً.',
81+
analyticsTitle: 'إحصائيات المنصة',
82+
statToday: 'استخدام اليوم',
83+
statSessions: 'جلسات اليوم',
84+
statTrend: 'مقارنة بالأمس',
85+
hoursShort: 'س',
86+
minutesShort: 'د',
8187
},
8288
tones: {
8389
random: 'عشوائي',

src/i18n/locales/en.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ const en: LocaleStrings = {
8080
condAnd: 'Flexible (AND)',
8181
descCondOr: 'Block as soon as ANY limit (Time or Videos) is reached.',
8282
descCondAnd: 'Block ONLY when BOTH limits (Time and Videos) are reached.',
83+
analyticsTitle: 'Platform Analytics',
84+
statToday: "Today's Usage",
85+
statSessions: 'Sessions Today',
86+
statTrend: 'Vs Yesterday',
87+
hoursShort: 'h',
88+
minutesShort: 'm',
8389
},
8490
tones: {
8591
random: 'Random',

src/i18n/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ export interface LocaleStrings {
7777
condAnd: string;
7878
descCondOr: string;
7979
descCondAnd: string;
80+
analyticsTitle: string;
81+
statToday: string;
82+
statSessions: string;
83+
statTrend: string;
84+
hoursShort: string;
85+
minutesShort: string;
8086
};
8187
tones: {
8288
random: string;

src/ui/settings/analytics-loader.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { StorageFacade } from '../../core/storage/index';
22
import { PlatformId } from '../../types';
3+
import { i18n } from '../../i18n/index';
34

45
export class AnalyticsLoader {
56
constructor(private storage: StorageFacade) {}
@@ -14,7 +15,10 @@ export class AnalyticsLoader {
1415
return;
1516
}
1617

17-
const translations = { hours: 'h', minutes: 'm' };
18+
const translations = {
19+
hours: i18n.t.dashboard.hoursShort,
20+
minutes: i18n.t.dashboard.minutesShort,
21+
};
1822

1923
const report = await this.storage.analyticsService.getDashboardReport(platformId, translations);
2024

src/ui/settings/index.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ function updateUITexts() {
9494
'desc-dash-reset': t.dashboard.resetDesc,
9595
'btn-reset-settings': t.dashboard.resetBtn,
9696

97+
// Analytics
98+
'group-analytics-title': t.dashboard.analyticsTitle,
99+
'lbl-stat-today': t.dashboard.statToday,
100+
'lbl-stat-sessions': t.dashboard.statSessions,
101+
'lbl-stat-trend': t.dashboard.statTrend,
102+
97103
// CTA
98104
'btn-sponsor-text': t.dashboard.btnSponsor,
99105

@@ -107,6 +113,24 @@ function updateUITexts() {
107113
if (el) el.textContent = value;
108114
});
109115

116+
const platformMap: Record<string, string> = {
117+
youtube_shorts: t.popup.youtubeShorts,
118+
youtube_watch: t.popup.youtubeWatch,
119+
instagram_reels: t.popup.instagramReels,
120+
instagram_feed: t.popup.instagramFeed,
121+
};
122+
123+
const platformSelect = document.getElementById(
124+
'analytics-platform-selector',
125+
) as HTMLSelectElement;
126+
if (platformSelect) {
127+
Array.from(platformSelect.options).forEach((option) => {
128+
if (platformMap[option.value]) {
129+
option.textContent = platformMap[option.value];
130+
}
131+
});
132+
}
133+
110134
document.body.dir = i18n.language === 'ar' ? 'rtl' : 'ltr';
111135
}
112136

0 commit comments

Comments
 (0)