wineater-bot 3.7.0 → 3.8.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/dist/wineater-chatbot.mjs +648 -616
- package/dist/wineater-chatbot.umd.js +61 -38
- package/index.html +1 -1
- package/package.json +1 -1
- package/src/js/i18n.js +69 -33
- package/src/js/widget.js +47 -31
package/index.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>Chatbot</title>
|
|
7
|
-
<script>window.wineaterData = {CLIENT_TOKEN: '
|
|
7
|
+
<script>window.wineaterData = {CLIENT_TOKEN: 'OjvAprylAugk5pKTHRIzZxcPPb6HMPr9qHhrazG0sF3uHjkrvBzufPZ65igfRJJa', TYPE: 'widget', WIDGET_TYPE: 'bar', WIDGET_WRAPPER: 'wineater-widget-conteiner'};</script>
|
|
8
8
|
<!-- <script type="module" src="./dist/wineater-chatbot.umd.js"></script> -->
|
|
9
9
|
<script type="module" src="./src/index.js"></script>
|
|
10
10
|
</head>
|
package/package.json
CHANGED
package/src/js/i18n.js
CHANGED
|
@@ -470,8 +470,8 @@ class Translator {
|
|
|
470
470
|
headerOccasion: "случай",
|
|
471
471
|
headerOr: "или",
|
|
472
472
|
},
|
|
473
|
-
|
|
474
|
-
lang: '
|
|
473
|
+
kk: {
|
|
474
|
+
lang: 'kk',
|
|
475
475
|
welcomeMessage: "Сіз не жегіңіз келеді? Тағамыңызды сипаттаңыз, мен оған керемет шарап ұсынамын.",
|
|
476
476
|
noResultsFound: "Нәтижелер табылмады.",
|
|
477
477
|
somethingWentWrong: "Кешіріңіз, бірдеңе дұрыс болмады.",
|
|
@@ -540,29 +540,31 @@ class Translator {
|
|
|
540
540
|
|
|
541
541
|
async fetchConfig(storeName) {
|
|
542
542
|
try {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
543
|
+
const config = await this.api.getStoreConfig(storeName);
|
|
544
|
+
this.storeLanguage = config.lang;
|
|
545
|
+
this.supportedLanguages = config.available_languages;
|
|
546
|
+
this.OpenAIApiKey = config.open_ai_key;
|
|
547
|
+
|
|
548
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
549
|
+
const urlLang = urlParams.get('lang') || urlParams.get('language');
|
|
550
|
+
|
|
551
|
+
if (urlLang && this.translations[urlLang]) {
|
|
552
|
+
this.language = urlLang;
|
|
553
|
+
} else if (config.lang && this.translations[config.lang]) {
|
|
554
|
+
this.language = config.lang;
|
|
555
|
+
} else {
|
|
556
|
+
this.language = 'en';
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
this.currency = config.currency || 'USD';
|
|
561
560
|
} catch (e) {
|
|
562
|
-
|
|
563
|
-
|
|
561
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
562
|
+
const urlLang = urlParams.get('lang') || urlParams.get('language');
|
|
563
|
+
|
|
564
|
+
this.language = this.translations[urlLang] ? urlLang : 'en';
|
|
565
|
+
this.currency = 'USD';
|
|
564
566
|
}
|
|
565
|
-
|
|
567
|
+
}
|
|
566
568
|
|
|
567
569
|
getCurrency(key) {
|
|
568
570
|
const keys = key.split('.'); // Разбиваем ключ на части
|
|
@@ -598,12 +600,19 @@ class Translator {
|
|
|
598
600
|
return this.supportedLanguages;
|
|
599
601
|
}
|
|
600
602
|
|
|
601
|
-
|
|
603
|
+
getLangFromURL() {
|
|
604
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
605
|
+
const lang = urlParams.get('lang') || urlParams.get('language');
|
|
606
|
+
return lang && this.translations[lang] ? lang : null;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
async translateTexts(textObject) {
|
|
602
610
|
if (this.storeLanguage === this.language || !this.supportedLanguages) {
|
|
611
|
+
console.log('gривет');
|
|
603
612
|
return textObject;
|
|
604
613
|
}
|
|
605
614
|
|
|
606
|
-
const apiKey = this.OpenAIApiKey;
|
|
615
|
+
const apiKey = this.OpenAIApiKey;
|
|
607
616
|
const url = 'https://api.openai.com/v1/chat/completions';
|
|
608
617
|
|
|
609
618
|
const headers = {
|
|
@@ -611,12 +620,37 @@ class Translator {
|
|
|
611
620
|
'Authorization': `Bearer ${apiKey}`,
|
|
612
621
|
};
|
|
613
622
|
|
|
614
|
-
const
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
623
|
+
const entries = Object.entries(textObject).map(([key, text]) => ({
|
|
624
|
+
key,
|
|
625
|
+
text,
|
|
626
|
+
}));
|
|
618
627
|
|
|
619
|
-
|
|
628
|
+
const prompt = `
|
|
629
|
+
You are a professional translator.
|
|
630
|
+
|
|
631
|
+
The input texts are written in language with code "${this.storeLanguage}". Your task is to translate them strictly to the target language with code "${this.language}".
|
|
632
|
+
|
|
633
|
+
⚠️ DO NOT leave the original text.
|
|
634
|
+
⚠️ DO NOT skip any entry.
|
|
635
|
+
⚠️ Preserve the original order and keys exactly.
|
|
636
|
+
|
|
637
|
+
Translate only the "text" fields of the JSON array. Return an array of the same length and structure, like this:
|
|
638
|
+
|
|
639
|
+
Input:
|
|
640
|
+
[
|
|
641
|
+
{ "key": "shortDescription", "text": "Red wine with rich cherry notes." },
|
|
642
|
+
{ "key": "tags", "text": "fruity, dry, elegant" }
|
|
643
|
+
]
|
|
644
|
+
|
|
645
|
+
Output:
|
|
646
|
+
[
|
|
647
|
+
{ "key": "shortDescription", "text": "..." },
|
|
648
|
+
{ "key": "tags", "text": "..." }
|
|
649
|
+
]
|
|
650
|
+
|
|
651
|
+
Now translate the following input:
|
|
652
|
+
${JSON.stringify(entries, null, 2)}
|
|
653
|
+
`;
|
|
620
654
|
|
|
621
655
|
const body = JSON.stringify({
|
|
622
656
|
model: 'gpt-3.5-turbo',
|
|
@@ -640,11 +674,13 @@ class Translator {
|
|
|
640
674
|
|
|
641
675
|
if (!translatedRaw) throw new Error('No response from OpenAI');
|
|
642
676
|
|
|
643
|
-
const
|
|
677
|
+
const translatedArray = JSON.parse(translatedRaw);
|
|
644
678
|
|
|
645
679
|
const translatedObject = {};
|
|
646
|
-
|
|
647
|
-
|
|
680
|
+
translatedArray.forEach(entry => {
|
|
681
|
+
if (entry.key && entry.text !== undefined) {
|
|
682
|
+
translatedObject[entry.key] = entry.text;
|
|
683
|
+
}
|
|
648
684
|
});
|
|
649
685
|
|
|
650
686
|
return translatedObject;
|
package/src/js/widget.js
CHANGED
|
@@ -399,53 +399,63 @@ export class WineWidget {
|
|
|
399
399
|
if (translator.language === translator.storeLanguage || this.widgetType !== 'bar') {
|
|
400
400
|
return;
|
|
401
401
|
}
|
|
402
|
-
const fieldsToTranslate = ['Short Description', 'reason', 'pairing_recommendation', 'Tags'];
|
|
403
|
-
const language = translator.language;
|
|
404
402
|
|
|
403
|
+
const possibleFields = ['Short Description', 'reason', 'pairing_recommendation', 'Tags', 'tags'];
|
|
404
|
+
const firstWine = wines[0];
|
|
405
|
+
|
|
406
|
+
const fieldsToTranslate = possibleFields.filter(field => field in firstWine);
|
|
405
407
|
const batchSize = 4;
|
|
408
|
+
|
|
406
409
|
for (let i = 0; i < wines.length; i += batchSize) {
|
|
407
410
|
const batch = wines.slice(i, i + batchSize);
|
|
408
411
|
|
|
409
412
|
const translationPromises = batch.map(async (wine, index) => {
|
|
410
413
|
const globalIndex = i + index;
|
|
411
|
-
const textObject = {};
|
|
412
414
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
415
|
+
// Сбор оригинальных текстов в формате [{ key, text }]
|
|
416
|
+
const entries = fieldsToTranslate
|
|
417
|
+
.map(field => {
|
|
418
|
+
const val = wine[field] || wine[field.toLowerCase()];
|
|
419
|
+
if (!val) return null;
|
|
420
|
+
return { key: field, text: Array.isArray(val) ? val.join(', ') : val };
|
|
421
|
+
})
|
|
422
|
+
.filter(Boolean);
|
|
417
423
|
|
|
418
|
-
if (
|
|
424
|
+
if (entries.length === 0) return;
|
|
419
425
|
|
|
420
|
-
|
|
426
|
+
// Перевод
|
|
427
|
+
const translatedArray = await translator.translateTexts(
|
|
428
|
+
Object.fromEntries(entries.map(entry => [entry.key, entry.text]))
|
|
429
|
+
);
|
|
421
430
|
|
|
422
431
|
const popup = document.querySelector(`.card-popup[data-wine-index="${globalIndex}"]`);
|
|
423
432
|
if (!popup) return;
|
|
424
433
|
|
|
425
|
-
|
|
434
|
+
// Применение перевода
|
|
435
|
+
if (translatedArray['Short Description']) {
|
|
426
436
|
const el = popup.querySelector('.popup-short-desc');
|
|
427
|
-
if (el) el.textContent =
|
|
437
|
+
if (el) el.textContent = translatedArray['Short Description'];
|
|
428
438
|
}
|
|
429
439
|
|
|
430
|
-
if (
|
|
440
|
+
if (translatedArray.reason) {
|
|
431
441
|
const el = popup.querySelector('.popup-reason');
|
|
432
|
-
if (el) el.textContent =
|
|
442
|
+
if (el) el.textContent = translatedArray.reason;
|
|
433
443
|
}
|
|
434
444
|
|
|
435
|
-
if (
|
|
445
|
+
if (translatedArray.pairing_recommendation) {
|
|
436
446
|
const el = popup.querySelector('.popup-pairing');
|
|
437
|
-
if (el) el.textContent =
|
|
447
|
+
if (el) el.textContent = translatedArray.pairing_recommendation;
|
|
438
448
|
}
|
|
439
449
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
const
|
|
450
|
+
const tags = translatedArray.tags || translatedArray.Tags;
|
|
451
|
+
if (tags) {
|
|
452
|
+
const tagArray = tags.split(',').map(tag => tag.trim());
|
|
453
|
+
const tagHTML = tagArray.map(tag => `<span class="popup-tag">${tag}</span>`).join('');
|
|
443
454
|
const tagsContainer = popup.querySelector('.popup-tags');
|
|
444
455
|
if (tagsContainer) tagsContainer.innerHTML = tagHTML;
|
|
445
456
|
}
|
|
446
457
|
});
|
|
447
458
|
|
|
448
|
-
// Ждём выполнения всей пачки
|
|
449
459
|
await Promise.all(translationPromises);
|
|
450
460
|
}
|
|
451
461
|
}
|
|
@@ -510,9 +520,8 @@ export class WineWidget {
|
|
|
510
520
|
createLanguageSwitcher() {
|
|
511
521
|
const langContainer = document.querySelector('#powered-by-widget .powered-by-lang');
|
|
512
522
|
const languages = translator.getSupportedLanguages();
|
|
513
|
-
if (!languages)
|
|
514
|
-
|
|
515
|
-
}
|
|
523
|
+
if (!languages) return;
|
|
524
|
+
|
|
516
525
|
const currentLanguage = translator.language;
|
|
517
526
|
|
|
518
527
|
const dropdown = document.createElement('select');
|
|
@@ -520,18 +529,25 @@ createLanguageSwitcher() {
|
|
|
520
529
|
dropdown.className = 'language-dropdown';
|
|
521
530
|
|
|
522
531
|
languages.forEach(lang => {
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
532
|
+
const option = document.createElement('option');
|
|
533
|
+
option.value = lang;
|
|
534
|
+
option.textContent = lang.toUpperCase();
|
|
535
|
+
option.selected = lang === currentLanguage;
|
|
536
|
+
dropdown.appendChild(option);
|
|
528
537
|
});
|
|
529
538
|
|
|
530
539
|
dropdown.onchange = (event) => {
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
540
|
+
const selectedLang = event.target.value;
|
|
541
|
+
|
|
542
|
+
// Обновляем URL без перезагрузки страницы
|
|
543
|
+
const url = new URL(window.location.href);
|
|
544
|
+
url.searchParams.set('lang', selectedLang);
|
|
545
|
+
|
|
546
|
+
// Обновляем localStorage тоже, на всякий случай
|
|
547
|
+
localStorage.setItem('selectedLanguage', selectedLang);
|
|
548
|
+
|
|
549
|
+
// Перенаправляем пользователя с обновлённым URL
|
|
550
|
+
window.location.href = url.toString();
|
|
535
551
|
};
|
|
536
552
|
|
|
537
553
|
langContainer.appendChild(dropdown);
|