cdnhost 2.5.9 → 2.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdnhost",
3
- "version": "2.5.9",
3
+ "version": "2.6.1",
4
4
  "description": "cdnhost",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/wbs.png ADDED
Binary file
package/wbs2.png ADDED
Binary file
package/ws_cdn.js CHANGED
@@ -1,8 +1,9 @@
1
1
 
2
2
  document.addEventListener('DOMContentLoaded', function() {
3
-
3
+ if (window.FHLWidgetInitialized) return;
4
+ window.FHLWidgetInitialized = true;
5
+
4
6
  const languages = [ { code: 'ko', name: '한국어' }, { code: 'en', name: 'English' }, { code: 'es', name: 'Español' }, { code: 'hi', name: 'हिन्दी' }, { code: 'ar', name: 'العربية' }, { code: 'de', name: 'Deutsch' }, { code: 'fr', name: 'Français' }, { code: 'pt', name: 'Português' }, { code: 'bn', name: 'বাংলা' }, { code: 'ja', name: '日本語' }, { code: 'ru', name: 'Русский' }, { code: 'zh', name: '简体中文' }, { code: 'th', name: 'ไทย' }, { code: 'vi', name: 'Tiếng Việt' }, { code: 'id', name: 'Indonesia' }, { code: 'tr', name: 'Türkçe' }, { code: 'ur', name: 'اردو' }];
5
-
6
7
  let fullApiItemsData = null;
7
8
  let currentDisplayItems = [];
8
9
  let currentPage = 1;
@@ -24,7 +25,6 @@
24
25
  const languageBtn = document.getElementById('fhl-language-btn');
25
26
  const languagePanel = document.getElementById('fhl-language-panel');
26
27
 
27
- // ✨ [수정됨] `fhl-fixed-question` -> `fhl-fixed-image`로 되돌리고, 올바른 ID를 참조합니다.
28
28
  const fixedImageBtn = document.getElementById('fhl-fixed-image');
29
29
  const fixedChatBtn = document.getElementById('fhl-fixed-chat');
30
30
  const fixedAdBtn = document.getElementById('fhl-fixed-ad');
@@ -92,14 +92,7 @@
92
92
  });
93
93
  };
94
94
 
95
- const showToast = (message) => {
96
- clearTimeout(toastTimeout);
97
- toastElement.textContent = message;
98
- toastElement.classList.add('visible');
99
- toastTimeout = setTimeout(() => {
100
- toastElement.classList.remove('visible');
101
- }, 2500);
102
- };
95
+ const showToast = (message) => { clearTimeout(toastTimeout); toastElement.textContent = message; toastElement.classList.add('visible'); toastTimeout = setTimeout(() => { toastElement.classList.remove('visible'); }, 2500); };
103
96
 
104
97
  const applyLanguage = (langCode) => {
105
98
  if (typeof translations === 'undefined') {
@@ -115,7 +108,7 @@
115
108
  searchClose.setAttribute('aria-label', t.closeSearch);
116
109
  languageBtn.setAttribute('aria-label', t.changeLanguage);
117
110
  searchAddBtn.setAttribute('aria-label', 'Copy Widget Code');
118
- fixedImageBtn.setAttribute('aria-label', t.images); // ✨ [수정됨]
111
+ fixedImageBtn.setAttribute('aria-label', t.images);
119
112
  fixedChatBtn.setAttribute('aria-label', t.characterChat);
120
113
  fixedAdBtn.setAttribute('aria-label', t.ads);
121
114
 
@@ -156,6 +149,10 @@
156
149
  }
157
150
  };
158
151
 
152
+
153
+
154
+
155
+
159
156
  const createIconItem = (item, type = 'default') => {
160
157
  const link = document.createElement('a');
161
158
 
@@ -180,11 +177,7 @@
180
177
 
181
178
  if (type === 'search') {
182
179
  link.addEventListener('mousedown', () => {
183
- fetch('update_view_count.php', {
184
- method: 'POST',
185
- headers: { 'Content-Type': 'application/json' },
186
- body: JSON.stringify({ url: item.url })
187
- }).catch(console.error);
180
+ fetch('https://isai.kr/update_view_count.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ url: item.url }) }).catch(console.error);
188
181
  });
189
182
  link.target = '_blank';
190
183
  }
@@ -277,49 +270,17 @@
277
270
  return link;
278
271
  };
279
272
 
280
- const appendNextPage = () => {
281
- if (isLoadingNextPage) return;
282
- isLoadingNextPage = true;
283
- const startIndex = (currentPage - 1) * ITEMS_PER_PAGE;
284
- const endIndex = currentPage * ITEMS_PER_PAGE;
285
- if (startIndex >= currentDisplayItems.length) {
286
- isLoadingNextPage = false;
287
- return;
288
- }
289
- const itemsToAppend = currentDisplayItems.slice(startIndex, endIndex);
290
- itemsToAppend.forEach(item => {
291
- searchResultsContainer.appendChild(createIconItem(item, 'search'));
292
- });
293
- currentPage++;
294
- isLoadingNextPage = false;
295
- };
296
273
 
297
- const renderInitialResults = (items) => {
298
- searchResultsContainer.innerHTML = '';
299
- currentPage = 1;
300
- currentDisplayItems = items;
301
- appendNextPage();
302
- };
303
274
 
304
- const openSearch = async () => {
305
- widgetContainer.classList.add('search-mode');
306
- widgetWrapper.classList.add('search-mode');
307
- searchInput.focus();
308
- if (fullApiItemsData === null) {
309
- try {
310
- const response = await fetch('appapi2.php');
311
- if (!response.ok) throw new Error('API 응답 오류');
312
- fullApiItemsData = await response.json();
313
- } catch (error) {
314
- console.error('전체 앱 목록 로드 실패:', error);
315
- searchResultsContainer.innerHTML = '<p>목록 로드 실패</p>';
316
- fullApiItemsData = [];
317
- return;
318
- }
319
- }
320
- renderInitialResults(fullApiItemsData);
321
- };
322
275
 
276
+
277
+
278
+
279
+
280
+ const appendNextPage = () => { if (isLoadingNextPage) return; isLoadingNextPage = true; const startIndex = (currentPage - 1) * ITEMS_PER_PAGE; const endIndex = currentPage * ITEMS_PER_PAGE; if (startIndex >= currentDisplayItems.length) { isLoadingNextPage = false; return; } const itemsToAppend = currentDisplayItems.slice(startIndex, endIndex); itemsToAppend.forEach(item => { searchResultsContainer.appendChild(createIconItem(item, 'search')); }); currentPage++; isLoadingNextPage = false; };
281
+ const renderInitialResults = (items) => { searchResultsContainer.innerHTML = ''; currentPage = 1; currentDisplayItems = items; appendNextPage(); };
282
+ const openSearch = async () => { widgetContainer.classList.add('search-mode'); widgetWrapper.classList.add('search-mode'); searchInput.focus(); if (fullApiItemsData === null) { try { const response = await fetch('https://isai.kr/appapi2.php'); if (!response.ok) throw new Error('API 응답 오류'); fullApiItemsData = await response.json(); } catch (error) { console.error('전체 앱 목록 로드 실패:', error); searchResultsContainer.innerHTML = '<p>목록 로드 실패</p>'; fullApiItemsData = []; return; } } renderInitialResults(fullApiItemsData); };
283
+
323
284
  const initialRender = () => {
324
285
  const t = translations[currentLang] || translations['en'];
325
286
  const initialItemsData = [
@@ -333,165 +294,72 @@
333
294
  { name: t.psychology, url: 'https://simpong.oduc.kr/', img: 'http://cdn.jsdelivr.net/npm/cdnhost@2.2.0/_simpong.png' },
334
295
  { name: t.ads, url: 'https://gig.snapp.im/', icon: 'ph-currency-circle-dollar' },
335
296
  ];
336
-
337
297
  listContainer.innerHTML = '';
338
298
  const storedItems = shortcutManager.get();
339
299
  let combinedItems = [...initialItemsData];
340
300
  combinedItems.splice(2, 0, ...storedItems);
341
-
342
- combinedItems.forEach(item => {
343
- const isLocal = storedItems.some(stored => stored.url === item.url);
344
- const itemType = isLocal && !item.icon && !item.img ? 'local' : 'default';
345
- listContainer.appendChild(createIconItem(item, itemType));
346
- });
347
-
301
+ combinedItems.forEach(item => { const isLocal = storedItems.some(stored => stored.url === item.url); const itemType = isLocal && !item.icon && !item.img ? 'local' : 'default'; listContainer.appendChild(createIconItem(item, itemType)); });
348
302
  checkScrollability();
349
303
  updateAllWidgetLinks();
350
304
  };
351
305
 
352
- const updateScrollIndicator = () => {
353
- const scrollLeft = listContainer.scrollLeft;
354
- const maxScrollLeft = listContainer.scrollWidth - listContainer.clientWidth;
355
- if (maxScrollLeft <= 0) return;
356
- const scrollFraction = scrollLeft / maxScrollLeft;
357
- const trackWidth = indicatorTrack.clientWidth;
358
- const indicatorWidth = scrollIndicator.clientWidth;
359
- const maxIndicatorLeft = trackWidth - indicatorWidth;
360
- const indicatorLeft = scrollFraction * maxIndicatorLeft;
361
- scrollIndicator.style.transform = `translateY(-50%) translateX(${indicatorLeft}px)`;
362
- };
363
-
364
- const checkScrollability = () => {
365
- const isScrollable = listContainer.scrollWidth > listContainer.clientWidth;
366
- widgetContainer.classList.toggle('scrollable', isScrollable);
367
- if (isScrollable) updateScrollIndicator();
368
- };
369
-
306
+ const updateScrollIndicator = () => { const scrollLeft = listContainer.scrollLeft; const maxScrollLeft = listContainer.scrollWidth - listContainer.clientWidth; if (maxScrollLeft <= 0) return; const scrollFraction = scrollLeft / maxScrollLeft; const trackWidth = indicatorTrack.clientWidth; const indicatorWidth = scrollIndicator.clientWidth; const maxIndicatorLeft = trackWidth - indicatorWidth; const indicatorLeft = scrollFraction * maxIndicatorLeft; scrollIndicator.style.transform = `translateY(-50%) translateX(${indicatorLeft}px)`; };
307
+ const checkScrollability = () => { const isScrollable = listContainer.scrollWidth > listContainer.clientWidth; widgetContainer.classList.toggle('scrollable', isScrollable); if (isScrollable) updateScrollIndicator(); };
370
308
  let isDragging = false;
371
- const handleDragMove = (e) => {
372
- if (!isDragging) return;
373
- e.preventDefault();
374
- const trackRect = indicatorTrack.getBoundingClientRect();
375
- const maxScrollLeft = listContainer.scrollWidth - listContainer.clientWidth;
376
- let positionRatio = (e.clientX - trackRect.left) / trackRect.width;
377
- positionRatio = Math.max(0, Math.min(1, positionRatio));
378
- listContainer.scrollLeft = positionRatio * maxScrollLeft;
379
- };
380
- const handleDragEnd = () => {
381
- if (!isDragging) return;
382
- isDragging = false;
383
- document.removeEventListener('mousemove', handleDragMove);
384
- document.removeEventListener('mouseup', handleDragEnd);
385
- };
386
-
387
- indicatorTrack.addEventListener('mousedown', (e) => {
388
- isDragging = true;
389
- handleDragMove(e);
390
- document.addEventListener('mousemove', handleDragMove);
391
- document.addEventListener('mouseup', handleDragEnd);
392
- });
393
-
394
- const closeSearch = () => {
395
- widgetContainer.classList.remove('search-mode');
396
- widgetWrapper.classList.remove('search-mode');
397
- searchInput.value = '';
398
- searchResultsContainer.innerHTML = '';
399
- };
400
-
309
+ const handleDragMove = (e) => { if (!isDragging) return; e.preventDefault(); const trackRect = indicatorTrack.getBoundingClientRect(); const maxScrollLeft = listContainer.scrollWidth - listContainer.clientWidth; let positionRatio = (e.clientX - trackRect.left) / trackRect.width; positionRatio = Math.max(0, Math.min(1, positionRatio)); listContainer.scrollLeft = positionRatio * maxScrollLeft; };
310
+ const handleDragEnd = () => { if (!isDragging) return; isDragging = false; document.removeEventListener('mousemove', handleDragMove); document.removeEventListener('mouseup', handleDragEnd); };
311
+ indicatorTrack.addEventListener('mousedown', (e) => { isDragging = true; handleDragMove(e); document.addEventListener('mousemove', handleDragMove); document.addEventListener('mouseup', handleDragEnd); });
312
+ const closeSearch = () => { widgetContainer.classList.remove('search-mode'); widgetWrapper.classList.remove('search-mode'); searchInput.value = ''; searchResultsContainer.innerHTML = ''; };
401
313
  let hideTimeout = null;
402
314
  widgetContainer.addEventListener('mouseenter', () => { clearTimeout(hideTimeout); });
403
315
  widgetContainer.addEventListener('mouseleave', () => { hideTimeout = setTimeout(() => { widgetContainer.classList.add('hidden'); }, 6000); });
404
-
405
- document.addEventListener('click', (e) => {
406
- if (!languagePanel.contains(e.target) && !languageBtn.contains(e.target)) {
407
- languagePanel.classList.remove('visible');
408
- }
409
- if (!widgetContainer.classList.contains('hidden') || widgetContainer.contains(e.target)) {
410
- return;
411
- }
412
- widgetContainer.classList.remove('hidden');
413
- clearTimeout(hideTimeout);
414
- hideTimeout = setTimeout(() => {
415
- widgetContainer.classList.add('hidden');
416
- }, 6000);
417
- });
418
-
316
+ document.addEventListener('click', (e) => { if (!languagePanel.contains(e.target) && !languageBtn.contains(e.target)) { languagePanel.classList.remove('visible'); } if (!widgetContainer.classList.contains('hidden') || widgetContainer.contains(e.target)) { return; } widgetContainer.classList.remove('hidden'); clearTimeout(hideTimeout); hideTimeout = setTimeout(() => { widgetContainer.classList.add('hidden'); }, 6000); });
419
317
  listContainer.addEventListener('scroll', updateScrollIndicator);
420
318
  window.addEventListener('resize', checkScrollability);
421
-
422
319
  searchTrigger.addEventListener('click', openSearch);
423
-
424
- languageBtn.addEventListener('click', (e) => {
425
- e.stopPropagation();
426
- languagePanel.classList.toggle('visible');
427
- });
428
-
429
- searchInput.addEventListener('input', function() {
430
- if (!fullApiItemsData) return;
431
- const searchTerm = this.value.toLowerCase().trim();
432
- const filteredItems = fullApiItemsData.filter(item => item.name.toLowerCase().includes(searchTerm));
433
- renderInitialResults(filteredItems);
434
- });
435
-
436
- searchResultsContainer.addEventListener('scroll', () => {
437
- const isAtBottom = searchResultsContainer.scrollTop + searchResultsContainer.clientHeight >= searchResultsContainer.scrollHeight - 10;
438
- if (isAtBottom) {
439
- appendNextPage();
440
- }
441
- });
442
-
320
+ languageBtn.addEventListener('click', (e) => { e.stopPropagation(); languagePanel.classList.toggle('visible'); });
321
+ searchInput.addEventListener('input', function() { if (!fullApiItemsData) return; const searchTerm = this.value.toLowerCase().trim(); const filteredItems = fullApiItemsData.filter(item => item.name.toLowerCase().includes(searchTerm)); renderInitialResults(filteredItems); });
322
+ searchResultsContainer.addEventListener('scroll', () => { const isAtBottom = searchResultsContainer.scrollTop + searchResultsContainer.clientHeight >= searchResultsContainer.scrollHeight - 10; if (isAtBottom) { appendNextPage(); } });
443
323
  searchClose.addEventListener('click', closeSearch);
324
+ document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && widgetWrapper.classList.contains('search-mode')) { closeSearch(); } });
444
325
 
445
- document.addEventListener('keydown', (e) => {
446
- if (e.key === 'Escape' && widgetWrapper.classList.contains('search-mode')) {
447
- closeSearch();
326
+ const addCurrentPageToWidget = async (e) => {
327
+ e.preventDefault();
328
+ const t = translations[currentLang] || translations['en'];
329
+ const baseUrl = getProcessedUrl(window.location.href);
330
+ let iconName = document.title || t.currentPage;
331
+ try {
332
+ const response = await fetch(`https://isai.kr/get_title.php?url=${encodeURIComponent(baseUrl)}`);
333
+ if (response.ok) { const data = await response.json(); if (data.title) { iconName = data.title; } }
334
+ } catch (error) { console.error('Failed to fetch title:', error); }
335
+ const newIcon = { name: iconName, url: baseUrl };
336
+ let storedItems = shortcutManager.get();
337
+ const isAlreadyAdded = storedItems.some(item => getProcessedUrl(item.url) === getProcessedUrl(newIcon.url));
338
+ if (!isAlreadyAdded) {
339
+ if (storedItems.length >= 10) { storedItems.shift(); }
340
+ storedItems.push(newIcon);
341
+ shortcutManager.set(storedItems);
342
+ try { await fetch('https://isai.kr/register_app.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(newIcon) }); } catch (error) { console.error('DB 등록/갱신 실패:', error); }
343
+ initialRender();
344
+ if (widgetWrapper.classList.contains('search-mode')) { closeSearch(); }
345
+ } else {
346
+ showToast(t.alreadyAdded);
448
347
  }
449
- });
450
-
451
-
452
-
453
-
454
- const addCurrentPageToWidget=async e=>{e.preventDefault();const _s=["get_title.php?url=","ok","json","title","POST","Content-Type","application/json","register_app.php","DB 등록/갱신 실패:","Failed to fetch title:","some","shift","push","length","get","set","preventDefault","search-mode","contains","classList","closeSearch","initialRender","alreadyAdded"],_=(i)=>_s[i];try{const a=(translations&&translations[currentLang])?translations[currentLang]:translations.en,b=getProcessedUrl(window.location.href);let c=document.title||a.currentPage;try{const d=await fetch(`${_(0)}${encodeURIComponent(b)}`);if(d&&d.status&&d.status>=200&&d.status<300){const eJson=await d[_(2)]();if(eJson&&eJson[_(3)])c=eJson[_(3)];}}catch(errFetchTitle){console.error(_(9),errFetchTitle);}const newIcon={name:c,url:b},sm=shortcutManager;let stored=sm[_(14)]();if(!Array.isArray(stored))stored=[];const isAdded=stored[_(10)](it=>getProcessedUrl(it.url)===getProcessedUrl(newIcon.url));if(!isAdded){if(stored[_(13)]>=10)stored[_(11)]();stored[_(12)](newIcon);sm[_(15)](stored);try{await fetch(_(7),{method:_(4),headers:{[_(5)]:_(6)},body:JSON.stringify(newIcon)});}catch(errDb){console.error(_(8),errDb);}if(typeof window[_(20)]==="function")window[_(20)]();const ww=widgetWrapper;if(ww&&ww[_(19)]&&ww[_(19)][_(18)](_(17)))if(typeof closeSearch==="function")closeSearch();}else showToast(a[_(21)]);}catch(fatal){console.error("addCurrentPageToWidget error:",fatal);}};
348
+ };
455
349
 
456
350
  const copyWidgetScript = (e) => {
457
351
  e.preventDefault();
458
352
  const textToCopy = "<script src='https://cdn.jsdelivr.net/npm/cdnhost@latest/ws_cdn.js'><\/script>";
459
353
  const t = translations[currentLang] || translations['en'];
460
-
461
- const fallbackCopy = () => {
462
- const textArea = document.createElement("textarea");
463
- textArea.value = textToCopy;
464
- textArea.style.position = "fixed";
465
- textArea.style.top = 0;
466
- textArea.style.left = "-9999px";
467
- document.body.appendChild(textArea);
468
- textArea.focus();
469
- textArea.select();
470
- try {
471
- document.execCommand('copy');
472
- showToast(t.codeCopied);
473
- } catch (err) {
474
- console.error('Fallback: Oops, unable to copy', err);
475
- }
476
- document.body.removeChild(textArea);
477
- };
478
-
479
- if (navigator.clipboard && window.isSecureContext) {
480
- navigator.clipboard.writeText(textToCopy).then(() => {
481
- showToast(t.codeCopied);
482
- }).catch(() => {
483
- fallbackCopy();
484
- });
485
- } else {
486
- fallbackCopy();
487
- }
354
+ const fallbackCopy = () => { const textArea = document.createElement("textarea"); textArea.value = textToCopy; textArea.style.position = "fixed"; textArea.style.top = 0; textArea.style.left = "-9999px"; document.body.appendChild(textArea); textArea.focus(); textArea.select(); try { document.execCommand('copy'); showToast(t.codeCopied); } catch (err) { console.error('Fallback: Oops, unable to copy', err); } document.body.removeChild(textArea); };
355
+ if (navigator.clipboard && window.isSecureContext) { navigator.clipboard.writeText(textToCopy).then(() => { showToast(t.codeCopied); }).catch(() => { fallbackCopy(); }); } else { fallbackCopy(); }
488
356
  };
489
357
 
490
358
  addToHomeBtn.addEventListener('click', addCurrentPageToWidget);
491
359
  searchAddBtn.addEventListener('click', copyWidgetScript);
492
360
 
493
361
  shortcutManager.syncFromURL();
494
- shortcutManager.get();
362
+ shortcutManager.get();
495
363
 
496
364
  populateLanguagePanel();
497
365
  applyLanguage(currentLang);
package/ws_cdnhtml.js CHANGED
@@ -15,7 +15,6 @@ const widgetHTML = `
15
15
  <div class="fhl-icon-bar" id="fhl-icon-bar">
16
16
  <button class="fhl-icon-display" id="fhl-search-trigger"><i class="ph-bold ph-magnifying-glass"></i></button>
17
17
  <div class="fhl-list-container" id="fhl-list-container"></div>
18
- <!-- ✨ [수정됨] 언어 버튼이 다시 추가되었습니다. -->
19
18
  <button class="fhl-icon-display" id="fhl-language-btn"><i class="ph-bold ph-globe"></i></button>
20
19
  <a href="#" id="fhl-add-to-home-btn" class="fhl-icon-display"><i class="ph-bold ph-download-simple"></i></a>
21
20
  </div>
@@ -26,7 +25,6 @@ const widgetHTML = `
26
25
  </div>
27
26
  <div class="fhl-search-content">
28
27
  <div class="fhl-search-results" id="fhl-search-results"></div>
29
- <!-- ✨ [수정됨] 모든 고정 아이콘에 ID가 추가되고, 아이콘이 원래대로 복원되었습니다. -->
30
28
  <div class="fhl-fixed-panel">
31
29
  <a href="#" id="fhl-search-add-btn" class="fhl-fixed-item"><i class="ph-bold ph-download-simple"></i></a>
32
30
  <a href="#" id="fhl-fixed-image" class="fhl-fixed-item"><i class="ph-bold ph-image"></i></a>
package/ws_css.css CHANGED
@@ -1,3 +1,4 @@
1
+
1
2
  .fhl-widget-wrapper *, .fhl-widget-wrapper *::before, .fhl-widget-wrapper *::after { box-sizing: border-box; }
2
3
  .fhl-widget-container { position: fixed; z-index: 1000; bottom: 20px; left: 50%; transform: translateX(-50%) translateY(0); display: flex; flex-direction: column; align-items: center; width: calc(100% - 30px); max-width: 460px; pointer-events: auto; transition: opacity 0.4s ease-in-out, transform 0.4s ease-in-out; }
3
4
  .fhl-widget-container.hidden { opacity: 0; transform: translateX(-50%) translateY(20px); pointer-events: none; }
package/ws_lg.js CHANGED
@@ -16,5 +16,5 @@ const translations = {
16
16
  vi: { search: 'Tìm kiếm', question: 'Câu hỏi', forum: 'Diễn đàn', blog: 'Blog', characterChat: 'Trò chuyện nhân vật', translate: 'Dịch', tarot: 'Tarot', psychology: 'Tâm lý học', ads: 'Quảng cáo', openSearch: 'Mở tìm kiếm', addToWidget: 'Thêm vào tiện ích', closeSearch: 'Đóng tìm kiếm', changeLanguage: 'Đổi ngôn ngữ', images: 'Hình ảnh', searchPlaceholder: 'Tìm kiếm...', alreadyAdded: 'Trang này đã được thêm vào tiện ích.', currentPage: 'Trang hiện tại', codeCopied: 'Đã sao chép mã. Vui lòng dán vào trang web của bạn.' },
17
17
  id: { search: 'Cari', question: 'Pertanyaan', forum: 'Forum', blog: 'Blog', characterChat: 'Obrolan Karakter', translate: 'Terjemahkan', tarot: 'Tarot', psychology: 'Psikologi', ads: 'Iklan', openSearch: 'Buka Pencarian', addToWidget: 'Tambah ke Widget', closeSearch: 'Tutup Pencarian', changeLanguage: 'Ubah Bahasa', images: 'Gambar', searchPlaceholder: 'Cari...', alreadyAdded: 'Halaman ini sudah ditambahkan ke widget.', currentPage: 'Halaman Saat Ini', codeCopied: 'Kode disalin. Silakan tempel di situs Anda.' },
18
18
  tr: { search: 'Ara', question: 'Soru', forum: 'Forum', blog: 'Blog', characterChat: 'Karakter Sohbeti', translate: 'Çevir', tarot: 'Tarot', psychology: 'Psikoloji', ads: 'Reklamlar', openSearch: 'Aramayı Aç', addToWidget: 'Widget\'a Ekle', closeSearch: 'Aramayı Kapat', changeLanguage: 'Dili Değiştir', images: 'Görseller', searchPlaceholder: 'Ara...', alreadyAdded: 'Bu sayfa zaten widget\'a eklenmiş.', currentPage: 'Mevcut Sayfa', codeCopied: 'Kod kopyalandı. Lütfen sitenize yapıştırın.' },
19
- ur: { search: 'تلاش', question: 'سوال', forum: 'فورم', blog: 'بلاگ', characterChat: 'کردار چیٹ', translate: 'ترجمہ', tarot: 'ٹیرو', psychology: 'نفسیات', ads: 'اشتہارات', openSearch: 'تلاش کھولیں', addToWidget: 'ویجیٹ میں شامل کریں', closeSearch: 'تلاش بند کریں', changeLanguage: 'زبان تبدیل کریں', images: 'تصاویر', searchPlaceholder: 'تلاش کریں...', alreadyAdded: 'یہ صفحہ پہلے ہی ویجیٹ میں شامل ہے۔', currentPage: 'موجودہ صفحہ', codeCopied: 'کوڈ کاپی ہوگیا ہے۔ براہ کرم اسے اپنی سائٹ پر چسپاں کریں۔' }
19
+ ur: { search: 'تلاش', question: 'سوال', forum: 'فورم', blog: 'بلاگ', characterChat: 'کردار چیٹ', translate: 'ترجمہ', tarot: 'ٹیرو', psychology: 'نفسیات', ads: 'اشتہارات', openSearch: 'تلاش کھولیں', addToWidget: 'ویجیٹ میں شامل کریں', closeSearch: 'تلاش بند کریں', changeLanguage: 'زبان تبدیل کریں', images: 'تصاویر', searchPlaceholder: 'تلاش کریں...', alreadyAdded: 'یہ صفحہ پہلے ہی ویجیট میں شامل ہے۔', currentPage: 'موجودہ صفحہ', codeCopied: 'کوڈ کاپی ہوگیا ہے۔ براہ کرم اسے اپنی سائٹ پر چسپاں کریں۔' }
20
20
  };