亚马逊评论计算优化版(Enhanced Amazon Review Calculator)

精确计算各星级评价数量及提升评分所需五星好评数,支持全球亚马逊站点

Instalar este script¿?
Script recomendado por el autor

Puede que también te guste Temu模态框自动取消.

Instalar este script
// ==UserScript==
// @name         亚马逊评论计算优化版(Enhanced Amazon Review Calculator)
// @namespace    https://github.com/monty8800/amazon-seller-tools
// @version      3.8
// @description  精确计算各星级评价数量及提升评分所需五星好评数,支持全球亚马逊站点
// @author       Monty & Assistant
// @match        *://*.amazon.com/*dp/*
// @match        *://*.amazon.co.uk/*dp/*
// @match        *://*.amazon.de/*dp/*
// @match        *://*.amazon.fr/*dp/*
// @match        *://*.amazon.it/*dp/*
// @match        *://*.amazon.es/*dp/*
// @match        *://*.amazon.co.jp/*dp/*
// @match        *://*.amazon.ca/*dp/*
// @match        *://*.amazon.com.au/*dp/*
// @match        *://*.amazon.in/*dp/*
// @match        *://*.amazon.com.mx/*dp/*
// @match        *://*.amazon.com.br/*dp/*
// @match        *://*.amazon.nl/*dp/*
// @match        *://*.amazon.cn/*dp/*
// @match        *://*.amazon.sg/*dp/*
// @match        *://*.amazon.ae/*dp/*
// @match        *://*.amazon.sa/*dp/*
// @match        *://*.amzn.*/*dp/*
// @icon         https://www.amazon.com/favicon.ico
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_addStyle
// @require      https://cdn.tailwindcss.com
// @license      MIT
// ==/UserScript==

// 添加现代化的CSS样式
GM_addStyle(`
  /* 基础字体设置 */
  .monty-tw {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  }
  
  /* 容器样式 */
  .monty-review-box {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    border: 1px solid #e5e7eb;
    border-radius: 0.5rem;
    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
    background-color: white;
    padding: 1rem;
    margin: 1rem 0;
    transition: all 0.3s ease;
  }
  
  /* 标题样式 */
  .monty-review-title {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    font-size: 1.125rem;
    font-weight: 600;
    color: #1f2937;
    margin-bottom: 0.75rem;
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
  
  /* 折叠/展开按钮 */
  .monty-toggle-btn {
    background: none;
    border: none;
    color: #6b7280;
    cursor: pointer;
    padding: 0.25rem;
    border-radius: 0.25rem;
    transition: all 0.2s;
  }
  
  .monty-toggle-btn:hover {
    color: #4b5563;
    background-color: #f3f4f6;
  }
  
  /* 折叠状态样式 */
  .monty-collapsed .monty-detail-section {
    display: none;
  }
  
  .monty-collapsed .monty-summary-section {
    margin-bottom: 0;
  }
  
  /* 目标分数输入框 */
  #monty-target-score {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    width: 4rem;
    padding: 0.25rem 0.5rem;
    font-size: 0.875rem;
    border: 1px solid #d1d5db;
    border-radius: 0.25rem;
  }
  
  #monty-target-score:focus {
    outline: none;
    border-color: #3b82f6;
    box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.3);
  }
  
  /* 评论项目样式 */
  .monty-review-item {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    padding: 0.375rem 0;
    font-size: 0.875rem;
    color: #4b5563;
  }
  
  /* 高亮文本 */
  .monty-highlight {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    font-weight: 500;
    color: #2563eb;
  }
  
  /* 语言选择器 */
  .monty-lang-selector {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    font-size: 0.75rem;
    padding: 0.25rem 0.5rem;
    border: 1px solid #d1d5db;
    border-radius: 0.25rem;
    background-color: white;
    cursor: pointer;
  }
  
  .monty-lang-selector:focus {
    outline: none;
    border-color: #3b82f6;
    box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.3);
  }
  
  .monty-lang-selector:hover {
    border-color: #9ca3af;
  }
  
  /* 星级评分样式 */
  .monty-star {
    color: #f59e0b;
  }
  
  /* 分隔线 */
  .monty-divider {
    margin: 0.5rem 0;
    border-top: 1px solid #e5e7eb;
  }
  
  /* 底部版权信息 */
  .monty-footer {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    font-size: 0.75rem;
    color: #6b7280;
    margin-top: 0.75rem;
    padding-top: 0.5rem;
    text-align: right;
    border-top: 1px solid #e5e7eb;
  }
  
  /* 按钮样式 */
  .monty-button {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    padding: 0.25rem 0.75rem;
    font-size: 0.75rem;
    font-weight: 500;
    color: white;
    background-color: #2563eb;
    border-radius: 0.25rem;
  }
  
  .monty-button:hover {
    background-color: #1d4ed8;
  }
  
  .monty-button:focus {
    outline: none;
    box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.5);
  }
  
  /* 评分条 */
  .monty-rating-bar {
    display: flex;
    align-items: center;
    margin: 0.25rem 0;
  }
  
  .monty-rating-stars {
    width: 4rem;
    flex-shrink: 0;
  }
  
  .monty-rating-meter {
    flex-grow: 1;
    height: 0.5rem;
    background-color: #e5e7eb;
    border-radius: 9999px;
    overflow: hidden;
  }
  
  .monty-rating-meter-fill {
    height: 100%;
    background-color: #f59e0b;
  }
  
  .monty-rating-count {
    margin-left: 0.5rem;
    font-size: 0.75rem;
    color: #4b5563;
    width: 5rem;
    text-align: right;
  }
  
  /* 布局辅助类 */
  .flex {
    display: flex;
  }
  
  .flex-col {
    flex-direction: column;
  }
  
  .items-center {
    align-items: center;
  }
  
  .justify-between {
    justify-content: space-between;
  }
  
  .space-x-3 > * + * {
    margin-left: 0.75rem;
  }
  
  .mr-2 {
    margin-right: 0.5rem;
  }
  
  .mb-3 {
    margin-bottom: 0.75rem;
  }
  
  .text-sm {
    font-size: 0.875rem;
  }
  
  .text-lg {
    font-size: 1.125rem;
  }
  
  .text-xs {
    font-size: 0.75rem;
  }
  
  .text-gray-500 {
    color: #6b7280;
  }
  
  .text-gray-600 {
    color: #4b5563;
  }
  
  .py-2 {
    padding-top: 0.5rem;
    padding-bottom: 0.5rem;
  }
  
  .px-3 {
    padding-left: 0.75rem;
    padding-right: 0.75rem;
  }
  
  .bg-blue-50 {
    background-color: #eff6ff;
  }
  
  .bg-red-50 {
    background-color: #fef2f2;
  }
  
  .rounded {
    border-radius: 0.25rem;
  }
  
  .border-l-4 {
    border-left-width: 4px;
  }
  
  .border-blue-400 {
    border-color: #60a5fa;
  }
  
  .border-red-400 {
    border-color: #f87171;
  }
  
  .my-2 {
    margin-top: 0.5rem;
    margin-bottom: 0.5rem;
  }
`);

(function() {
    'use strict';

    const DEFAULT_TARGET_SCORE = 4.3;
    const DEBUG_MODE = true; // 生产环境中关闭调试模式
    
    // 获取用户设置的目标分数
    function getTargetScore() {
        const savedScore = GM_getValue('target_score', DEFAULT_TARGET_SCORE);
        // 确保分数在1-5之间
        return Math.min(5, Math.max(1, parseFloat(savedScore)));
    }
    
    // 设置用户目标分数
    function setTargetScore(score) {
        const validScore = Math.min(5, Math.max(1, parseFloat(score)));
        log('设置目标分数:', validScore);
        GM_setValue('target_score', validScore);
        return validScore;
    }
    
    // 日志输出函数
    function log(...args) {
        if (DEBUG_MODE) {
            console.log('[Review Calculator]', ...args);
        }
    }
    
    // 等待元素出现在页面上
    async function waitForElement(selector, timeout = 10000) {
        log(`等待元素出现: ${selector}`);
        const startTime = Date.now();
        
        while (Date.now() - startTime < timeout) {
            const element = document.querySelector(selector);
            if (element) {
                log(`找到元素: ${selector}`);
                return element;
            }
            
            // 等待100ms再检查
            await new Promise(resolve => setTimeout(resolve, 100));
        }
        
        log(`等待元素超时: ${selector}`);
        return null;
    }

    // 获取用户语言偏好
    function getUserLanguage() {
        // 获取用户保存的语言偏好,默认为中文
        const savedLanguage = GM_getValue('user_language', 'zh');
        log('用户语言偏好:', savedLanguage);
        return savedLanguage;
    }
    
    // 设置用户语言偏好
    function setUserLanguage(language) {
        log('设置用户语言偏好:', language);
        GM_setValue('user_language', language);
    }
    
    // 获取本地化文本
    function getLocalizedText() {
        // 优先使用用户选择的语言
        const userLanguage = getUserLanguage();
        log('获取本地化文本, 用户语言:', userLanguage);
        
        // 如果没有用户选择的语言或者设置为自动,则根据域名自动检测
        if (!userLanguage || userLanguage === 'auto') {
            const domain = window.location.hostname;
            log('当前域名:', domain);
            
            // 根据域名确定语言
            let detectedLanguage = 'en'; // 默认英语
            
            if (domain.includes('.fr')) detectedLanguage = 'fr';
            else if (domain.includes('.de')) detectedLanguage = 'de';
            else if (domain.includes('.it')) detectedLanguage = 'it';
            else if (domain.includes('.es')) detectedLanguage = 'es';
            else if (domain.includes('.co.jp') || domain.includes('.jp')) detectedLanguage = 'jp';
            else if (domain.includes('.cn')) detectedLanguage = 'zh';
            else if (domain.includes('.nl')) detectedLanguage = 'nl';
            else if (domain.includes('.com.br')) detectedLanguage = 'pt-br';
            else if (domain.includes('.com.mx')) detectedLanguage = 'es-mx';
            else if (domain.includes('.in')) detectedLanguage = 'en-in';
            else if (domain.includes('.ca')) detectedLanguage = domain.includes('/fr/') ? 'fr-ca' : 'en-ca';
            
            log('检测到语言:', detectedLanguage);
            return getLocalizedTextByLanguage(detectedLanguage);
        }
        
        // 如果用户选择了日语,但实际使用的是'ja',而我们的代码中使用的是'jp'
        if (userLanguage === 'ja') {
            log('将日语代码从 ja 转换为 jp');
            return getLocalizedTextByLanguage('jp');
        }
        
        return getLocalizedTextByLanguage(userLanguage);
    }
    
    // 根据指定语言获取本地化文本
    function getLocalizedTextByLanguage(language) {
        log('使用语言:', language);
        
        // 各种语言的本地化文本
        const localizedTexts = {
            // 评论数文本
            ratingsText: {
                'en': 'ratings',
                'fr': 'évaluations',
                'de': 'Bewertungen',
                'it': 'recensioni',
                'es': 'valoraciones',
                'es-mx': 'calificaciones',
                'jp': '件の評価',
                'zh': '条评论',
                'nl': 'beoordelingen',
                'pt-br': 'avaliações',
                'en-in': 'ratings',
                'en-ca': 'ratings',
                'fr-ca': 'évaluations'
            },
            
            // 星级文本 (用于匹配评分文本)
            starText: {
                'en': 'out of 5 stars',
                'fr': 'sur 5 étoiles',
                'de': 'von 5 Sternen',
                'it': 'su 5 stelle',
                'es': 'de 5 estrellas',
                'es-mx': 'de 5 estrellas',
                'jp': '5つ星のうち',
                'zh': '5 星,最多 5 星',
                'nl': 'van de 5 sterren',
                'pt-br': 'de 5 estrelas',
                'en-in': 'out of 5 stars',
                'en-ca': 'out of 5 stars',
                'fr-ca': 'sur 5 étoiles'
            },
            
            // 结果面板文本
            resultText: {
                'en': {
                    title: 'Review Analysis',
                    currentScore: 'Current Rating:',
                    required: 'Need additional',
                    fiveStarReviews: '5-star reviews',
                    toReach: 'to reach',
                    noNeed: 'Current rating already exceeds',
                    noNeedSuffix: ', no additional reviews needed',
                    error: 'Review Analysis Error',
                    errorHelp: 'Please refresh the page or contact the developer for help',
                    targetScore: 'Target Score',
                    star: 'stars',
                    requiredReviews: 'Required 5-star reviews:',
                    alreadyAchieved: 'Target score already achieved',
                    impossibleTarget: 'Cannot reach this target score',
                    toggleView: 'Toggle view'
                },
                'fr': {
                    title: 'Analyse des avis',
                    currentScore: 'Note actuelle:',
                    required: 'Besoin de',
                    fiveStarReviews: 'avis 5 étoiles supplémentaires',
                    toReach: 'pour atteindre',
                    noNeed: 'La note actuelle dépasse déjà',
                    noNeedSuffix: ', aucun avis supplémentaire nécessaire',
                    error: 'Erreur d\'analyse',
                    errorHelp: 'Veuillez rafraîchir la page ou contacter le développeur pour obtenir de l\'aide',
                    targetScore: 'Score cible',
                    star: 'étoiles',
                    requiredReviews: 'Avis 5 étoiles nécessaires:',
                    alreadyAchieved: 'Score cible déjà atteint',
                    impossibleTarget: 'Impossible d\'atteindre ce score cible',
                    toggleView: 'Afficher/masquer'
                },
                'de': {
                    title: 'Bewertungsanalyse',
                    currentScore: 'Aktuelle Bewertung:',
                    required: 'Benötigt zusätzlich',
                    fiveStarReviews: '5-Sterne-Bewertungen',
                    toReach: 'um zu erreichen',
                    noNeed: 'Aktuelle Bewertung überschreitet bereits',
                    noNeedSuffix: ', keine zusätzlichen Bewertungen erforderlich',
                    error: 'Analysefehler',
                    errorHelp: 'Bitte aktualisieren Sie die Seite oder kontaktieren Sie den Entwickler für Hilfe',
                    targetScore: 'Zielbewertung',
                    star: 'Sterne',
                    requiredReviews: 'Benötigte 5-Sterne-Bewertungen:',
                    alreadyAchieved: 'Zielbewertung bereits erreicht',
                    impossibleTarget: 'Dieses Zielbewertung kann nicht erreicht werden',
                    toggleView: 'Ein-/Ausblenden'
                },
                'zh': {
                    title: '评论分析结果',
                    currentScore: '当前评分:',
                    required: '需要额外',
                    fiveStarReviews: '个五星好评',
                    toReach: '才能达到',
                    noNeed: '当前评分已达到',
                    noNeedSuffix: '分,无需额外好评',
                    error: '评论分析出错',
                    errorHelp: '请刷新页面或联系开发者获取帮助',
                    targetScore: '目标分数',
                    star: '星',
                    requiredReviews: '需要的五星好评数:',
                    alreadyAchieved: '已达到目标分数,无需额外好评',
                    impossibleTarget: '无法达到该目标分数',
                    toggleView: '展开/折叠视图'
                },
                'jp': {
                    title: 'レビュー分析',
                    currentScore: '現在の評価:',
                    required: 'あと',
                    fiveStarReviews: '件の5つ星レビューが必要',
                    toReach: '目標の',
                    noNeed: '現在の評価は既に',
                    noNeedSuffix: 'を超えています。追加レビュー不要',
                    error: '分析エラー',
                    errorHelp: 'ページを更新するか、開発者にお問い合わせください',
                    targetScore: '目標スコア',
                    star: '星',
                    requiredReviews: '必要な5つ星レビュー:',
                    alreadyAchieved: '目標スコアは既に達成されています',
                    impossibleTarget: 'この目標スコアには到達できません',
                    toggleView: '表示切替'
                },
                'ja': {
                    title: 'レビュー分析',
                    currentScore: '現在の評価:',
                    required: 'あと',
                    fiveStarReviews: '件の5つ星レビューが必要',
                    toReach: '目標の',
                    noNeed: '現在の評価は既に',
                    noNeedSuffix: 'を超えています。追加レビュー不要',
                    error: '分析エラー',
                    errorHelp: 'ページを更新するか、開発者にお問い合わせください',
                    targetScore: '目標スコア',
                    star: '星',
                    requiredReviews: '必要な5つ星レビュー:',
                    alreadyAchieved: '目標スコアは既に達成されています',
                    impossibleTarget: 'この目標スコアには到達できません',
                    toggleView: '表示切替'
                }
                // 可以根据需要添加更多语言
            }
        };
        
        // 如果没有特定语言的翻译,使用英语作为后备
        const getTextWithFallback = (category, lang) => {
            return localizedTexts[category][lang] || localizedTexts[category]['en'];
        };
        
        return {
            ratingsText: getTextWithFallback('ratingsText', language),
            starText: getTextWithFallback('starText', language),
            resultText: localizedTexts.resultText[language] || localizedTexts.resultText['en']
        };
    }

    // 清洗数字格式(处理千位分隔符)
    function sanitizeNumber(numStr) {
        return numStr.replace(/[.,\s]/g, '')
                    .replace(/[^\d]/g, '');
    }

    // 计算加权平均分
    function calculateWeightedAverage(ratings) {
        const total = ratings.reduce((sum, r) => sum + r.count, 0);
        if (total === 0) return 0;

        return ratings.reduce((sum, r) => {
            return sum + (r.stars * r.count);
        }, 0) / total;
    }

    // 计算所需五星好评
    function calculateRequiredReviews(currentScore, totalReviews, targetScore = getTargetScore()) {
        // 如果当前评分已经达到目标,则不需要额外的好评
        if (currentScore >= targetScore) {
            return 0;
        }
        
        // 特殊情况:如果目标分数是5分,使用不同的计算方法
        if (targetScore >= 5) {
            // 计算达到5分需要的五星评价数
            // 公式:(5 * (totalReviews + x) - currentScore * totalReviews) / (totalReviews + x) = 5
            // 简化:x = (5 - currentScore) * totalReviews / (5 - 5) 无法计算
            // 因此使用另一种方法:计算将所有非5星评价抵消所需的5星评价数
            const nonFiveStarWeight = totalReviews * (5 - currentScore);
            return Math.ceil(nonFiveStarWeight);
        }
        
        // 常规情况:计算公式
        // (目标评分 * (总评论数 + x) - 当前评分 * 总评论数) / (总评论数 + x) = 目标评分
        // 简化后:x = (目标评分 * 总评论数 - 当前评分 * 总评论数) / (5 - 目标评分)
        const numerator = targetScore * totalReviews - currentScore * totalReviews;
        const denominator = 5 - targetScore;
        
        return Math.ceil(numerator / denominator);
    }

    // 主处理函数
    async function processReviews() {
        try {
            log('开始处理评论数据...');
            log('当前URL:', window.location.href);
            
            // 等待评分直方图加载 - 使用最新的选择器
            log('等待评分直方图加载...');
            const histogram = await waitForElement('#histogramTable');
            if (!histogram) {
                log('错误: 找不到评分直方图');
                throw new Error('找不到评分直方图');
            }
            log('成功找到评分直方图:', histogram);

            // 获取本地化文本
            const localizedText = getLocalizedText();
            log('本地化文本:', localizedText);
            
            // 直接使用data-hook属性查找总评论数
            const totalElement = document.querySelector('[data-hook="total-review-count"]');
            log('总评论数元素:', totalElement);
            
            if (!totalElement) {
                log('错误: 找不到总评论数元素');
                throw new Error('找不到总评论数元素');
            }

            log('总评论数文本:', totalElement.textContent);
            const totalReviews = parseInt(sanitizeNumber(totalElement.textContent));
            log('解析后的总评论数:', totalReviews);
            
            if (isNaN(totalReviews)) {
                log('错误: 总评论数格式错误');
                throw new Error('总评论数格式错误');
            }

            // 获取各星级评价 - 使用最新的选择器
            log('查找评分条...');
            const ratingBars = [...document.querySelectorAll('#histogramTable li a')];
            log('找到评分条数量:', ratingBars.length);
            
            if (ratingBars.length !== 5) {
                log('错误: 找不到完整的五星评价数据, 只找到', ratingBars.length, '条');
                throw new Error('找不到完整的五星评价数据');
            }

            log('开始提取各星级评价数据...');
            const ratings = ratingBars.map((bar, index) => {
                // 获取星级 (5星到1星)
                const stars = 5 - index;
                log(`处理 ${stars} 星评价...`);
                
                // 获取百分比 - 从aria-valuenow属性获取
                let percent = 0;
                const meter = bar.querySelector('.a-meter');
                log(`${stars}星评价条元素:`, meter);
                
                if (meter && meter.getAttribute('aria-valuenow')) {
                    percent = parseInt(meter.getAttribute('aria-valuenow')) / 100;
                    log(`${stars}星评价 - 从aria-valuenow获取百分比:`, percent);
                }
                
                // 如果无法从aria-valuenow获取,尝试从style.width获取
                if (percent === 0 && meter && meter.querySelector('.a-meter-bar')) {
                    const meterBar = meter.querySelector('.a-meter-bar');
                    const widthStyle = meterBar.style.width;
                    log(`${stars}星评价 - meter-bar宽度样式:`, widthStyle);
                    
                    if (widthStyle) {
                        percent = parseInt(widthStyle) / 100;
                        log(`${stars}星评价 - 从style.width获取百分比:`, percent);
                    }
                }
                
                // 如果仍然无法获取百分比,尝试从文本中提取
                if (percent === 0) {
                    log(`${stars}星评价 - 尝试从文本提取百分比...`);
                    const percentTexts = bar.querySelectorAll('.a-text-right, .aok-nowrap');
                    log(`${stars}星评价 - 找到可能包含百分比的文本元素:`, percentTexts.length);
                    
                    for (const el of percentTexts) {
                        log(`${stars}星评价 - 文本内容:`, el.textContent);
                        const percentMatch = el.textContent.match(/(\d+)%/);
                        if (percentMatch) {
                            percent = parseInt(percentMatch[1]) / 100;
                            log(`${stars}星评价 - 从文本提取的百分比:`, percent);
                            break;
                        }
                    }
                }
                
                const count = Math.round(totalReviews * percent);
                log(`${stars}星评价 - 最终数据:`, { stars, percent, count });
                
                return {
                    stars: stars,
                    percent: percent,
                    count: count
                };
            });

            // 计算当前评分
            log('计算加权平均分...');
            const currentScore = calculateWeightedAverage(ratings);
            log('计算得到的当前评分:', currentScore);

            // 获取目标分数
            const targetScore = getTargetScore();
            log('目标评分:', targetScore);
            
            // 计算结果
            log('计算所需五星好评数...');
            const required = calculateRequiredReviews(currentScore, totalReviews, targetScore);
            log('需要的五星好评数:', required);

            // 生成结果面板
            const resultBox = document.createElement('div');
            resultBox.className = 'monty-review-box';
            resultBox.id = 'monty-review-box';
            
            // 使用本地化文本
            const rt = localizedText.resultText;
            
            // 创建语言选择器
            const currentLang = getUserLanguage();
            const langOptions = {
                'zh': '中文',
                'en': 'English',
                'ja': '日本語',
                'de': 'Deutsch',
                'fr': 'Français',
                'es': 'Español'
            };
            
            let langSelector = `
                <select id="monty-lang-selector" class="monty-lang-selector text-xs bg-white border border-gray-300 rounded px-2 py-1 focus:border-blue-500 focus:ring-1 focus:ring-blue-500">
            `;
            
            Object.entries(langOptions).forEach(([code, name]) => {
                langSelector += `<option value="${code}" ${code === currentLang ? 'selected' : ''}>${name}</option>`;
            });
            
            langSelector += '</select>';
            
            // 生成星级评分条
            let ratingBarsHtml = '';
            ratings.forEach(rating => {
                const percent = (rating.percent * 100).toFixed(0);
                const starText = `${rating.stars} ${rt.star}`;
                
                ratingBarsHtml += `
                <div class="flex items-center space-x-2 py-1">
                    <div class="w-16 text-sm">${starText}</div>
                    <div class="flex-grow h-5 bg-gray-200 rounded overflow-hidden">
                        <div class="h-full bg-yellow-400" style="width: ${percent}%"></div>
                    </div>
                    <div class="w-16 text-right text-sm">${rating.count} (${percent}%)</div>
                </div>`;
            });
            
            // 生成所需好评数HTML
            let requiredHtml = '';
            if (required > 0) {
                requiredHtml = `
                <div class="monty-divider"></div>
                <div class="monty-review-item flex justify-between">
                    <span>${rt.targetScore}</span>
                    <div class="flex items-center">
                        <input type="number" id="monty-target-score" class="w-16 px-2 py-1 mr-2 text-center border border-gray-300 rounded" 
                            value="${targetScore}" min="1" max="5" step="0.1">
                    </div>
                </div>
                <div class="monty-review-item flex justify-between">
                    <span>${rt.requiredReviews}</span>
                    <span class="monty-highlight text-lg">${required}</span>
                </div>`;
            } else if (required === 0) {
                requiredHtml = `
                <div class="monty-divider"></div>
                <div class="monty-review-item flex justify-between">
                    <span>${rt.targetScore}</span>
                    <div class="flex items-center">
                        <input type="number" id="monty-target-score" class="w-16 px-2 py-1 mr-2 text-center border border-gray-300 rounded" 
                            value="${targetScore}" min="1" max="5" step="0.1">
                    </div>
                </div>
                <div class="monty-review-item flex justify-between">
                    <span>${rt.alreadyAchieved}</span>
                </div>`;
            } else {
                requiredHtml = `
                <div class="monty-divider"></div>
                <div class="monty-review-item flex justify-between">
                    <span>${rt.targetScore}</span>
                    <div class="flex items-center">
                        <input type="number" id="monty-target-score" class="w-16 px-2 py-1 mr-2 text-center border border-gray-300 rounded" 
                            value="${targetScore}" min="1" max="5" step="0.1">
                    </div>
                </div>
                <div class="monty-review-item flex justify-between">
                    <span>${rt.impossibleTarget}</span>
                </div>`;
            }
            
            // 生成结果面板HTML
            resultBox.innerHTML = `
                <div class="p-4">
                    <div class="flex items-center justify-between mb-3">
                        <div class="flex items-center">
                            <h3 class="text-lg font-semibold text-gray-800">${rt.title}</h3>
                            <button id="monty-toggle-btn" class="monty-toggle-btn ml-2" title="${rt.toggleView || '展开/折叠视图'}">
                                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                                    <polyline points="6 9 12 15 18 9"></polyline>
                                </svg>
                            </button>
                        </div>
                        ${langSelector}
                    </div>
                    
                    <!-- 重要信息摘要(始终显示) -->
                    <div class="monty-summary-section mb-3">
                        <div class="flex justify-between items-center py-1">
                            <span class="text-gray-700">${rt.currentScore}</span>
                            <span class="font-medium text-blue-600 text-lg">${currentScore.toFixed(2)}</span>
                        </div>
                        
                        <!-- 如果有需要的好评数,显示在摘要中 -->
                        ${required > 0 ? `
                        <div class="flex justify-between items-center py-1">
                            <span class="text-gray-700">${rt.requiredReviews}</span>
                            <span class="font-medium text-blue-600 text-lg">${required}</span>
                        </div>` : ''}
                    </div>
                    
                    <!-- 详细信息(可折叠) -->
                    <div class="monty-detail-section">
                        <!-- 评分分布图表 -->
                        <div class="mb-3">
                            ${ratingBarsHtml}
                        </div>
                        
                        <div class="border-t border-gray-200 my-2"></div>
                        
                        <!-- 目标分数设置 -->
                        <div class="flex justify-between items-center py-1">
                            <span class="text-gray-700">${rt.targetScore}</span>
                            <div class="flex items-center">
                                <input type="number" id="monty-target-score" class="w-16 px-2 py-1 mr-2 text-center border border-gray-300 rounded" 
                                    value="${targetScore}" min="1" max="5" step="0.1">
                            </div>
                        </div>
                        ${required < 0 ? `
                        <div class="flex justify-between items-center py-1">
                            <span class="text-gray-700">${rt.impossibleTarget}</span>
                        </div>` : ''}
                        ${required === 0 ? `
                        <div class="flex justify-between items-center py-1">
                            <span class="text-gray-700">${rt.alreadyAchieved}</span>
                        </div>` : ''}
                    </div>
                    
                    <div class="border-t border-gray-200 mt-3 pt-2 text-right text-xs text-gray-500">
                        &copy; 2025 Monty Ng. All rights reserved.
                    </div>
                </div>
            `;
            
            // 将结果面板插入到页面中
            log('将结果面板插入到页面中...');
            const insertPoints = [
                '#averageCustomerReviews',
                '#reviewsMedley',
                '#productTitle',
                '.product-title-word-break'
            ];
            
            let inserted = false;
            for (const selector of insertPoints) {
                const element = document.querySelector(selector);
                if (element) {
                    log(`在 ${selector} 后插入结果面板`);
                    element.parentNode.insertBefore(resultBox, element.nextSibling);
                    inserted = true;
                    break;
                }
            }
            
            if (!inserted) {
                log('找不到合适的插入点,插入到页面底部');
                document.body.appendChild(resultBox);
            }
            
            // 存储数据到面板中,便于重新生成
            resultBox.dataset.currentScore = currentScore;
            resultBox.dataset.totalReviews = totalReviews;
            resultBox.dataset.required = required;
            resultBox.dataset.ratings = JSON.stringify(ratings);
            
            // 添加事件监听器
            const newLangSelector = document.getElementById('monty-lang-selector');
            if (newLangSelector) {
                newLangSelector.addEventListener('change', function() {
                    const newLang = this.value;
                    log('切换语言到:', newLang);
                    setUserLanguage(newLang);
                    regenerateResultPanel(resultBox);
                });
            }
            
            // 添加目标分数输入框事件监听器
            const targetScoreInput = document.getElementById('monty-target-score');
            if (targetScoreInput) {
                targetScoreInput.addEventListener('change', function() {
                    const newScore = parseFloat(this.value);
                    if (!isNaN(newScore) && newScore >= 1 && newScore <= 5) {
                        log('修改目标分数:', newScore);
                        const validScore = setTargetScore(newScore);
                        this.value = validScore; // 确保显示有效的值
                        
                        // 重新计算所需评论数
                        const newRequired = calculateRequiredReviews(currentScore, totalReviews, validScore);
                        resultBox.dataset.required = newRequired;
                        
                        // 重新生成结果面板
                        regenerateResultPanel(resultBox);
                    } else {
                        // 恢复为有效值
                        this.value = getTargetScore();
                    }
                });
            }
            
            // 添加折叠/展开按钮事件监听器
            const toggleBtn = document.getElementById('monty-toggle-btn');
            if (toggleBtn) {
                // 检查是否有保存的折叠状态
                const isPanelCollapsed = localStorage.getItem('monty-panel-collapsed') === 'true';
                
                // 根据保存的状态设置初始折叠状态
                if (isPanelCollapsed) {
                    resultBox.classList.add('monty-collapsed');
                    toggleBtn.innerHTML = `
                        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                            <polyline points="18 15 12 9 6 15"></polyline>
                        </svg>
                    `;
                }
                
                toggleBtn.addEventListener('click', function() {
                    resultBox.classList.toggle('monty-collapsed');
                    const isCollapsed = resultBox.classList.contains('monty-collapsed');
                    
                    // 保存折叠状态到localStorage
                    localStorage.setItem('monty-panel-collapsed', isCollapsed);
                    
                    // 更新按钮图标
                    if (isCollapsed) {
                        this.innerHTML = `
                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                                <polyline points="18 15 12 9 6 15"></polyline>
                            </svg>
                        `;
                    } else {
                        this.innerHTML = `
                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                                <polyline points="6 9 12 15 18 9"></polyline>
                            </svg>
                        `;
                    }
                });
            }
            
            log('结果面板生成完成');
        } catch (error) {
            log('处理评论数据时出错:', error);
            showError(`处理评论数据时出错: ${error.message}`);
        }
}

// ...

// 显示错误信息
function showError(message) {
    if (DEBUG_MODE) console.error('[Review Calculator]', message);
    
    // 创建错误提示框
    const errorBox = document.createElement('div');
    errorBox.className = 'monty-review-box bg-red-50 border-l-4 border-red-400';
    errorBox.id = 'monty-error-box';
    
    // 使用Tailwind CSS样式
    errorBox.innerHTML = `
        <div class="p-4">
            <div class="flex items-start">
                <div class="flex-shrink-0">
                    <svg class="h-5 w-5 text-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
                        <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />
                    </svg>
                </div>
                <div class="ml-3">
                    <h3 class="text-sm font-medium text-red-800">亚马逊评论计算器错误</h3>
                    <div class="mt-2 text-sm text-red-700">
                        ${message}
                    </div>
                </div>
            </div>
        </div>
    `;
    
    // 尝试在页面上找到合适的插入点
    const insertPoints = [
        '#averageCustomerReviews',
        '#reviewsMedley',
        '#productTitle',
        '.product-title-word-break'
    ];
    
    for (const selector of insertPoints) {
        const element = document.querySelector(selector);
        if (element) {
            element.parentNode.insertBefore(errorBox, element.nextSibling);
            return;
        }
    }
    
    // 如果找不到合适的插入点,插入到页面底部
    document.body.appendChild(errorBox);
}
    
    // 初始化
    function init() {
        log('初始化脚本...');
        const targetScore = getTargetScore();
        log('目标评分:', targetScore);
        
        // 确保我们在产品页面上
        log('当前页面路径:', window.location.pathname);
        if (!window.location.pathname.includes('/dp/')) {
            log('不是产品页面,脚本不执行');
            return;
        }
        
        // 等待DOM完全加载
        log('当前文档状态:', document.readyState);
        if (document.readyState === 'loading') {
            log('文档仍在加载中,等待DOMContentLoaded事件...');
            document.addEventListener('DOMContentLoaded', () => {
                log('DOM已加载,延迟1500ms执行主函数');
                setTimeout(processReviews, 1500);
            });
        } else {
            // 如果DOM已加载,给页面一些时间来完成动态内容加载
            log('DOM已加载,延迟1500ms执行主函数');
            setTimeout(processReviews, 1500);
        }
    }
    
    // 重新生成结果面板
    function regenerateResultPanel(panel) {
        if (!panel || !panel.dataset) {
            log('错误: 无法重新生成结果面板,面板不存在');
            return;
        }
        
        log('重新生成结果面板...');
        
        // 获取面板数据
        const currentScore = parseFloat(panel.dataset.currentScore || '0');
        const totalReviews = parseInt(panel.dataset.totalReviews || '0');
        const required = parseInt(panel.dataset.required || '0');
        const ratings = JSON.parse(panel.dataset.ratings || '[]');
        
        // 获取本地化文本
        const localizedText = getLocalizedText();
        const rt = localizedText.resultText;
        
        // 创建语言选择器
        const currentLang = getUserLanguage();
        log('当前语言:', currentLang);
        const langOptions = {
            'zh': '中文',
            'en': 'English',
            'ja': '日本語',  // 注意这里使用'ja'作为日语代码
            'de': 'Deutsch',
            'fr': 'Français',
            'es': 'Español'
        };
        
        let langSelector = `
            <select id="monty-lang-selector" class="monty-lang-selector text-xs bg-white border border-gray-300 rounded px-2 py-1 focus:border-blue-500 focus:ring-1 focus:ring-blue-500">
        `;
        
        Object.entries(langOptions).forEach(([code, name]) => {
            langSelector += `<option value="${code}" ${code === currentLang ? 'selected' : ''}>${name}</option>`;
        });
        
        langSelector += '</select>';
        
        // 生成星级评分条
        let ratingBarsHtml = '';
        ratings.forEach(rating => {
            const percent = (rating.percent * 100).toFixed(0);
            const starText = `${rating.stars} ${rt.star}`;
            
            ratingBarsHtml += `
            <div class="flex items-center space-x-2 py-1">
                <div class="w-16 text-sm">${starText}</div>
                <div class="flex-grow h-5 bg-gray-200 rounded overflow-hidden">
                    <div class="h-full bg-yellow-400" style="width: ${percent}%"></div>
                </div>
                <div class="w-16 text-right text-sm">${rating.count} (${percent}%)</div>
            </div>`;
        });
        
        // 生成所需好评数HTML
        let requiredHtml = '';
        const targetScore = getTargetScore();
        
        if (required > 0) {
            requiredHtml = `
            <div class="flex justify-between items-center py-1">
                <span class="text-gray-700">${rt.targetScore}</span>
                <div class="flex items-center">
                    <input type="number" id="monty-target-score" class="w-16 px-2 py-1 mr-2 text-center border border-gray-300 rounded" 
                        value="${targetScore}" min="1" max="5" step="0.1">
                </div>
            </div>`;
        } else if (required === 0) {
            requiredHtml = `
            <div class="flex justify-between items-center py-1">
                <span class="text-gray-700">${rt.targetScore}</span>
                <div class="flex items-center">
                    <input type="number" id="monty-target-score" class="w-16 px-2 py-1 mr-2 text-center border border-gray-300 rounded" 
                        value="${targetScore}" min="1" max="5" step="0.1">
                </div>
            </div>
            <div class="flex justify-between items-center py-1">
                <span class="text-gray-700">${rt.alreadyAchieved}</span>
            </div>`;
        } else {
            requiredHtml = `
            <div class="flex justify-between items-center py-1">
                <span class="text-gray-700">${rt.targetScore}</span>
                <div class="flex items-center">
                    <input type="number" id="monty-target-score" class="w-16 px-2 py-1 mr-2 text-center border border-gray-300 rounded" 
                        value="${targetScore}" min="1" max="5" step="0.1">
                </div>
            </div>
            <div class="flex justify-between items-center py-1">
                <span class="text-gray-700">${rt.impossibleTarget}</span>
            </div>`;
        }
        
        // 生成结果面板HTML
        panel.innerHTML = `
            <div class="p-4">
                <div class="flex items-center justify-between mb-3">
                    <div class="flex items-center">
                        <h3 class="text-lg font-semibold text-gray-800">${rt.title}</h3>
                        <button id="monty-toggle-btn" class="monty-toggle-btn ml-2" title="${rt.toggleView || '展开/折叠视图'}">
                            <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                                <polyline points="6 9 12 15 18 9"></polyline>
                            </svg>
                        </button>
                    </div>
                    ${langSelector}
                </div>
                
                <!-- 重要信息摘要(始终显示) -->
                <div class="monty-summary-section mb-3">
                    <div class="flex justify-between items-center py-1">
                        <span class="text-gray-700">${rt.currentScore}</span>
                        <span class="font-medium text-blue-600 text-lg">${currentScore.toFixed(2)}</span>
                    </div>
                    
                    <!-- 如果有需要的好评数,显示在摘要中 -->
                    ${required > 0 ? `
                    <div class="flex justify-between items-center py-1">
                        <span class="text-gray-700">${rt.requiredReviews}</span>
                        <span class="font-medium text-blue-600 text-lg">${required}</span>
                    </div>` : ''}
                </div>
                
                <!-- 详细信息(可折叠) -->
                <div class="monty-detail-section">
                    <!-- 评分分布图表 -->
                    <div class="mb-3">
                        ${ratingBarsHtml}
                    </div>
                    
                    <div class="border-t border-gray-200 my-2"></div>
                    
                    <!-- 目标分数设置 -->
                    <div class="flex justify-between items-center py-1">
                        <span class="text-gray-700">${rt.targetScore}</span>
                        <div class="flex items-center">
                            <input type="number" id="monty-target-score" class="w-16 px-2 py-1 mr-2 text-center border border-gray-300 rounded" 
                                value="${targetScore}" min="1" max="5" step="0.1">
                        </div>
                    </div>
                    ${required < 0 ? `
                    <div class="flex justify-between items-center py-1">
                        <span class="text-gray-700">${rt.impossibleTarget}</span>
                    </div>` : ''}
                    ${required === 0 ? `
                    <div class="flex justify-between items-center py-1">
                        <span class="text-gray-700">${rt.alreadyAchieved}</span>
                    </div>` : ''}
                </div>
                
                <div class="border-t border-gray-200 mt-3 pt-2 text-right text-xs text-gray-500">
                    &copy; 2025 Monty Ng. All rights reserved.
                </div>
            </div>
        `;
        
        // 重新添加事件监听器
        const newLangSelector = document.getElementById('monty-lang-selector');
        if (newLangSelector) {
            newLangSelector.addEventListener('change', function() {
                const newLang = this.value;
                log('切换语言到:', newLang);
                setUserLanguage(newLang);
                regenerateResultPanel(panel);
            });
        }
        
        // 重新添加目标分数输入框事件监听器
        const targetScoreInput = document.getElementById('monty-target-score');
        if (targetScoreInput) {
            targetScoreInput.addEventListener('change', function() {
                const newScore = parseFloat(this.value);
                if (!isNaN(newScore) && newScore >= 1 && newScore <= 5) {
                    log('修改目标分数:', newScore);
                    const validScore = setTargetScore(newScore);
                    
                    // 重新计算所需的五星好评数
                    const currentScore = parseFloat(panel.dataset.currentScore || '0');
                    const totalReviews = parseInt(panel.dataset.totalReviews || '0');
                    const newRequired = calculateRequiredReviews(currentScore, totalReviews, validScore);
                    
                    // 更新面板数据集
                    panel.dataset.required = newRequired;
                    
                    // 重新生成结果面板
                    regenerateResultPanel(panel);
                }
            });
        }
        
        // 添加折叠/展开按钮事件监听器
        const toggleBtn = document.getElementById('monty-toggle-btn');
        if (toggleBtn) {
            // 检查是否有保存的折叠状态
            const isPanelCollapsed = localStorage.getItem('monty-panel-collapsed') === 'true';
            
            // 根据保存的状态设置初始折叠状态
            if (isPanelCollapsed) {
                panel.classList.add('monty-collapsed');
                toggleBtn.innerHTML = `
                    <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                        <polyline points="18 15 12 9 6 15"></polyline>
                    </svg>
                `;
            }
            
            toggleBtn.addEventListener('click', function() {
                panel.classList.toggle('monty-collapsed');
                const isCollapsed = panel.classList.contains('monty-collapsed');
                
                // 保存折叠状态到localStorage
                localStorage.setItem('monty-panel-collapsed', isCollapsed);
                
                // 更新按钮图标
                if (isCollapsed) {
                    this.innerHTML = `
                        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                            <polyline points="18 15 12 9 6 15"></polyline>
                        </svg>
                    `;
                } else {
                    this.innerHTML = `
                        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                            <polyline points="6 9 12 15 18 9"></polyline>
                        </svg>
                    `;
                    // 恢复为有效值
                    this.value = getTargetScore();
                }
            });
        }
    }
    
    // 启动脚本
    try {
        log('脚本开始执行...');
        log('浏览器信息:', navigator.userAgent);
        init();
    } catch (error) {
        log('初始化失败:', error);
        showError(`初始化失败: ${error.message}`);
    }
})();