Skip to content

Commit 73c54ab

Browse files
committed
feat(i18n): enhance i18n and refactor UI text handling
- add popup status translations - refactor text updates using map/helper - improve settings & popup localization
1 parent dfb76c4 commit 73c54ab

6 files changed

Lines changed: 138 additions & 78 deletions

File tree

src/i18n/locales/ar.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,16 @@ const ar: LocaleStrings = {
1414
minsUnit: 'دقائق',
1515
reason_count: 'السبب: تجاوز عدد الفيديوهات',
1616
reason_time: 'السبب: انتهاء الوقت المسموح',
17-
reason_bypass: 'السبب: محاولة تحايل (Bypass)',
17+
reason_bypass: 'السبب: محاولة تحايل',
18+
checking: 'جاري التحقق...',
19+
refreshPage: '○ حدّث الصفحة',
20+
youtubeShorts: '● يوتيوب شورتس',
21+
unsupportedSite: '○ موقع غير مدعوم',
1822
},
1923
dashboard: {
2024
title: 'مركز قيادة ليمترا',
2125
sidebarGeneral: 'الإعدادات العامة',
22-
sidebarAI: 'المساعد الذكي (AI)',
26+
sidebarAI: 'المساعد الذكي',
2327
sidebarAnalytics: 'الإحصائيات',
2428
comingSoon: 'قريباً',
2529
languageLabel: 'اللغة',
@@ -33,7 +37,7 @@ const ar: LocaleStrings = {
3337
groupCustomization: 'تخصيص الواجهة',
3438
descLanguage: 'اختر لغة الواجهة المفضلة لك. التغييرات تطبق فوراً.',
3539
groupSecurity: 'الحماية والتتبع',
36-
themeLabel: 'المظهر (Theme)',
40+
themeLabel: 'المظهر',
3741
descTheme: 'اختر نظام الألوان المفضل لك.',
3842
themeLight: 'فاتح',
3943
themeDark: 'داكن',
@@ -43,6 +47,10 @@ const ar: LocaleStrings = {
4347
sidebarAbout: 'حول',
4448
aboutTitle: 'حول ليمترا',
4549
aboutDesc: 'مبنية لحماية انتباهك وتركيزك.',
50+
btnSponsor: 'رعاية ودعم المشروع',
51+
descTracking: 'حدد كيف تقوم الإضافة باحتساب وقتك.',
52+
footerCopyright: '© 2026 ليمترا. جميع الحقوق محفوظة.',
53+
footerPrivacy: 'سياسة الخصوصية · الشروط',
4654
},
4755
tones: {
4856
random: 'عشوائي',

src/i18n/locales/en.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ const en: LocaleStrings = {
1515
reason_count: 'Triggered by: Video Counter',
1616
reason_time: 'Triggered by: Time Limit',
1717
reason_bypass: 'Triggered by: Anti-Bypass System',
18+
checking: 'Checking...',
19+
refreshPage: '○ Refresh Page',
20+
youtubeShorts: '● YouTube Shorts',
21+
unsupportedSite: '○ Unsupported Site',
1822
},
1923
dashboard: {
2024
title: 'Limitra Command Center',
@@ -44,6 +48,10 @@ const en: LocaleStrings = {
4448
sidebarAbout: 'About',
4549
aboutTitle: 'About Limitra',
4650
aboutDesc: 'Built to protect your attention.',
51+
btnSponsor: 'Sponsor & Support',
52+
descTracking: 'Determine how Limitra counts your usage.',
53+
footerCopyright: '© 2026 Limitra. All rights reserved.',
54+
footerPrivacy: 'Privacy Policy · Terms',
4755
},
4856
tones: {
4957
random: 'Random',

src/i18n/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ export interface LocaleStrings {
1515
reason_count: string;
1616
reason_time: string;
1717
reason_bypass: string;
18+
checking: string;
19+
refreshPage: string;
20+
youtubeShorts: string;
21+
unsupportedSite: string;
1822
};
1923
dashboard: {
2024
title: string;
@@ -43,6 +47,10 @@ export interface LocaleStrings {
4347
sidebarAbout: string;
4448
aboutTitle: string;
4549
aboutDesc: string;
50+
btnSponsor: string;
51+
descTracking: string;
52+
footerCopyright: string;
53+
footerPrivacy: string;
4654
};
4755
tones: {
4856
random: string;

src/ui/popup/index.ts

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,29 @@ let currentConsumedTimeMs = 0;
1717
let clickCount = 0;
1818
let spamResetTimer: number | null = null;
1919

20+
function setText(id: string, value: string) {
21+
const el = document.getElementById(id);
22+
if (el) el.textContent = value;
23+
}
24+
2025
function updateUITexts() {
2126
const t = i18n.t;
22-
(document.getElementById('app-title') as HTMLElement).textContent = t.popup.title;
23-
(document.getElementById('lbl-max-videos') as HTMLElement).textContent = t.popup.maxVideos;
24-
(document.getElementById('lbl-time-limit') as HTMLElement).textContent = t.popup.timeLimit;
2527

26-
const lblUsageVideos = document.getElementById('lbl-usage-videos');
27-
if (lblUsageVideos) lblUsageVideos.textContent = t.popup.videosUnit;
28-
const lblUsageTime = document.getElementById('lbl-usage-time');
29-
if (lblUsageTime) lblUsageTime.textContent = t.popup.timeTitle;
28+
const map: Record<string, string> = {
29+
'app-title': t.popup.title,
30+
'lbl-max-videos': t.popup.maxVideos,
31+
'lbl-time-limit': t.popup.timeLimit,
32+
'lbl-usage-videos': t.popup.videosUnit,
33+
'lbl-usage-time': t.popup.timeTitle,
34+
'site-badge': t.popup.checking,
35+
};
36+
37+
Object.entries(map).forEach(([id, value]) => setText(id, value));
3038

3139
btnSave.textContent = t.popup.saveBtn;
40+
3241
document.body.dir = i18n.language === 'ar' ? 'rtl' : 'ltr';
42+
3343
updateProgressBar();
3444
}
3545

@@ -40,6 +50,7 @@ function updateProgressBar() {
4050
const timeLimit = Number(timeInput.value) || 0;
4151
const timeSpentMins = Math.floor(currentConsumedTimeMs / (60 * 1000));
4252
const t = i18n.t;
53+
4354
const containerVideos = document.getElementById('track-container-videos') as HTMLElement;
4455
const containerTime = document.getElementById('track-container-time') as HTMLElement;
4556
const usageSection = document.getElementById('usage-section') as HTMLElement;
@@ -57,8 +68,7 @@ function updateProgressBar() {
5768
let pCount = limit > 0 ? (currentConsumedCount / limit) * 100 : 0;
5869
if (pCount > 100) pCount = 100;
5970

60-
(document.getElementById('stats-videos') as HTMLElement).textContent =
61-
`${currentConsumedCount} / ${limit} ${t.popup.videosUnit}`;
71+
setText('stats-videos', `${currentConsumedCount} / ${limit} ${t.popup.videosUnit}`);
6272

6373
const fillV = document.getElementById('fill-videos') as HTMLElement;
6474
fillV.setAttribute('style', `--progress: ${pCount}%`);
@@ -73,8 +83,7 @@ function updateProgressBar() {
7383
let pTime = timeLimit > 0 ? (timeSpentMins / timeLimit) * 100 : 0;
7484
if (pTime > 100) pTime = 100;
7585

76-
(document.getElementById('stats-time') as HTMLElement).textContent =
77-
`${timeSpentMins} / ${timeLimit} ${t.popup.minsUnit}`;
86+
setText('stats-time', `${timeSpentMins} / ${timeLimit} ${t.popup.minsUnit}`);
7887

7988
const fillT = document.getElementById('fill-time') as HTMLElement;
8089
fillT.setAttribute('style', `--progress: ${pTime}%`);
@@ -118,19 +127,20 @@ async function init() {
118127
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
119128
const currentTab = tabs[0];
120129
const currentUrl = currentTab?.url || '';
130+
const t = i18n.t;
121131

122132
if (currentUrl.includes('youtube.com') && currentUrl.includes('/shorts/')) {
123133
chrome.tabs.sendMessage(currentTab.id!, { action: AppAction.PING }, (response) => {
124134
if (chrome.runtime.lastError || !response || response.status !== 'ALIVE') {
125-
siteBadge.textContent = '○ Refresh Page';
135+
siteBadge.textContent = t.popup.refreshPage;
126136
setBadgeState(siteBadge, 'warning');
127137
} else {
128-
siteBadge.textContent = '● YouTube Shorts';
138+
siteBadge.textContent = t.popup.youtubeShorts;
129139
setBadgeState(siteBadge, 'success');
130140
}
131141
});
132142
} else {
133-
siteBadge.textContent = '○ Unsupported Site';
143+
siteBadge.textContent = t.popup.unsupportedSite;
134144
setBadgeState(siteBadge, 'muted');
135145
}
136146
});
@@ -161,9 +171,7 @@ function updateActionIcon(isSystemDark: boolean) {
161171
: '../../assets/manifest/32x32-light.png';
162172

163173
if (chrome.action) {
164-
void chrome.action
165-
.setIcon({ path: iconPath })
166-
.catch((err) => console.error('[Limitra] Background Icon Error:', err));
174+
void chrome.action.setIcon({ path: iconPath }).catch(console.error);
167175
}
168176
}
169177

@@ -176,19 +184,20 @@ systemThemeQuery.addEventListener('change', (event) => {
176184

177185
btnSave.onclick = async () => {
178186
clickCount++;
187+
179188
if (spamResetTimer) clearTimeout(spamResetTimer);
180-
spamResetTimer = window.setTimeout(() => {
181-
clickCount = 0;
182-
}, 2000);
189+
spamResetTimer = window.setTimeout(() => (clickCount = 0), 2000);
183190

184191
if (clickCount >= 10) {
185192
btnSave.textContent = i18n.t.popup.stopSpamming;
186193
btnSave.disabled = true;
194+
187195
setTimeout(() => {
188196
btnSave.textContent = i18n.t.popup.saveBtn;
189197
btnSave.disabled = false;
190198
clickCount = 0;
191199
}, 3000);
200+
192201
return;
193202
}
194203

@@ -203,6 +212,7 @@ btnSave.onclick = async () => {
203212
updateProgressBar();
204213

205214
btnSave.textContent = i18n.t.popup.savedMsg;
215+
206216
setTimeout(() => {
207217
if (!btnSave.disabled) btnSave.textContent = i18n.t.popup.saveBtn;
208218
}, 1500);
@@ -215,25 +225,35 @@ btnSettings.onclick = () => {
215225
themeToggleBtn.onclick = async () => {
216226
const current = await storage.getTheme();
217227
const isSystemDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
228+
218229
const next =
219230
current === 'auto' ? (isSystemDark ? 'light' : 'dark') : current === 'dark' ? 'light' : 'dark';
231+
220232
await storage.setTheme(next);
221233
applyTheme(next);
222234
};
223235

224236
chrome.storage.onChanged.addListener((changes) => {
225237
let needsUpdate = false;
238+
226239
if (changes['limitra_count']) {
227240
currentConsumedCount = Number(changes['limitra_count'].newValue) || 0;
228241
needsUpdate = true;
229242
}
243+
230244
if (changes['limitra_time_spent']) {
231245
currentConsumedTimeMs = Number(changes['limitra_time_spent'].newValue) || 0;
232246
needsUpdate = true;
233247
}
248+
234249
if (changes['limitra_theme']) {
235250
applyTheme(changes['limitra_theme'].newValue as string);
236251
}
252+
253+
if (changes['limitra_language']) {
254+
updateUITexts();
255+
}
256+
237257
if (needsUpdate) updateProgressBar();
238258
});
239259

src/ui/settings/index.html

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ <h5 class="setting-title" id="lbl-dash-language">Language</h5>
7373
<div class="setting-control">
7474
<select id="dash-language" class="brutal-select">
7575
<option value="en">English</option>
76-
<option value="ar">Arabic</option>
76+
<option value="ar">العربيّة</option>
7777
</select>
7878
</div>
7979
</div>
@@ -187,7 +187,7 @@ <h2 id="title-about" class="about-title">About Limitra</h2>
187187
d="M12 21s-6-4.35-9-7.5C-1 9 2 3 7.5 3c2.04 0 3.5 1.5 4.5 3 1-1.5 2.46-3 4.5-3C22 3 25 9 21 13.5 18 16.65 12 21 12 21z"
188188
/>
189189
</svg>
190-
Sponsor &amp; Support
190+
<span id="btn-sponsor-text">Sponsor &amp; Support</span>
191191
</a>
192192

193193
<div class="about-socials">
@@ -266,7 +266,9 @@ <h2 id="title-about" class="about-title">About Limitra</h2>
266266
</div>
267267
</div>
268268

269-
<p class="about-footer-text">© 2026 Limitra. All rights reserved.</p>
269+
<p id="footer-copyright" class="about-footer-text">
270+
© 2026 Limitra. All rights reserved.
271+
</p>
270272

271273
<div class="footer-links">
272274
<a
@@ -278,7 +280,7 @@ <h2 id="title-about" class="about-title">About Limitra</h2>
278280
<svg class="footer-icon" viewBox="0 0 24 24">
279281
<path d="M12 2l7 4v6c0 5-3.5 9-7 10-3.5-1-7-5-7-10V6l7-4z" />
280282
</svg>
281-
Privacy Policy · Terms
283+
<span id="footer-privacy">Privacy Policy · Terms</span>
282284
<span class="arrow"></span>
283285
</a>
284286
</div>

0 commit comments

Comments
 (0)