// ==UserScript==
// @name Google AI Studio 全面翻译
// @namespace http://tampermonkey.net/
// @version 0.5
// @description 翻译 Google AI Studio 页面上的多个特定文本部分,包括左侧导航栏和工具提示
// @author YourName
// @match https://aistudio.google.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=aistudio.google.com
// @grant none
// ==/UserScript==
(function() {
'use strict';
// 集中所有需要翻译的文本和它们的定位器
const translations = [
// --- 核心页面内容翻译 ---
// 模型选择器部分
{
original: 'Gemini 2.5 Flash',
translated: 'Gemini 2.5 闪电版',
selector: 'button.model-selector-card span.title'
},
{
original: 'Our first hybrid reasoning model which supports a 1M token context window and has thinking budgets.',
translated: '我们首个支持 100 万 token 上下文窗口并具有思维预算的混合推理模型。',
selector: 'button.model-selector-card span[data-test-id="model-description"]'
},
// Token count
{
original: 'Token count',
translated: 'Token 计数',
selector: 'ms-token-count h3.v3-font-body'
},
// Temperature
{
original: 'Temperature',
translated: '温度',
selector: 'div[data-test-id="temperatureSliderContainer"] h3.v3-font-body'
},
// Media resolution
{
original: 'Media resolution',
translated: '媒体分辨率',
selector: 'div.media-resolution-selector h3.v3-font-body'
},
// Thinking section
{
original: 'Thinking',
translated: '思考',
selector: 'ms-thinking-budget-setting h3.thinking-group-title'
},
{
original: 'Thinking mode',
translated: '思考模式',
selector: 'ms-thinking-budget-setting p.v3-font-body'
},
// Tools group header
{
original: 'Tools',
translated: '工具',
selector: 'div.settings-group-header p.group-title'
},
// Structured output
{
original: 'Structured output',
translated: '结构化输出',
selector: 'div[data-test-id="structuredOutputTooltip"] h3.v3-font-body'
},
{
original: 'Edit', // Structured output旁边的Edit按钮
translated: '编辑',
selector: 'div[data-test-id="structuredOutputTooltip"] button.ms-button-borderless'
},
// Code execution
{
original: 'Code execution',
translated: '代码执行',
selector: 'div[data-test-id="codeExecutionTooltip"] h3.v3-font-body'
},
// Function calling
{
original: 'Function calling',
translated: '函数调用',
selector: 'div[data-test-id="functionCallingTooltip"] h3.v3-font-body'
},
{
original: 'Edit', // Function calling旁边的Edit按钮
translated: '编辑',
selector: 'div[data-test-id="functionCallingTooltip"] button.edit-function-declarations-button'
},
// Grounding with Google Search
{
original: 'Grounding with Google Search',
translated: '使用 Google 搜索进行接地',
selector: 'div[data-test-id="searchAsAToolTooltip"] h3.v3-font-body'
},
// URL context
{
original: 'URL context',
translated: 'URL 上下文',
selector: 'div[data-test-id="browseAsAToolTooltip"] h3.v3-font-body'
},
{
original: 'Default',
translated: '默认',
selector: 'span.mdc-list-item__primary-text'
},
{
original: 'Medium',
translated: '中等',
selector: 'span.mdc-list-item__primary-text'
},
{
original: 'Low',
translated: '低等',
selector: 'span.mdc-list-item__primary-text'
},
{
original: 'Medium',
translated: '中等',
selector: '.mat-mdc-select-value-text.ng-star-inserted'
},
{
original: 'Default',
translated: '默认',
selector: '.mat-mdc-select-value-text.ng-star-inserted'
},
{
original: 'Low',
translated: '低等',
selector: '.mat-mdc-select-value-text.ng-star-inserted'
},
// Advanced settings group header
{
original: 'Advanced settings',
translated: '高级设置',
selector: 'div.settings-item.settings-group-header.expanded p.group-title'
},
// Safety settings
{
original: 'Safety settings',
translated: '安全设置',
selector: 'div.settings-item.safety-settings h3.v3-font-body'
},
{
original: 'Edit', // Safety settings旁边的Edit按钮
translated: '编辑',
selector: 'div.settings-item.safety-settings button.edit-safety-button'
},
// Output length
{
original: 'Output length',
translated: '输出长度',
selector: 'div.settings-item.output-length h3.v3-font-body'
},
{
original: 'Add stop sequence',
translated: '添加停止序列',
selector: 'ms-stop-sequence-input label[for="chip-input"]'
},
{
original: 'Add stop...',
translated: '添加停止符...',
selector: 'ms-stop-sequence-input input#chip-input[placeholder="Add stop..."]',
attribute: 'placeholder'
},
{
original: 'Top P',
translated: 'Top P (概率阈值)',
selector: 'div.settings-item-column.ng-star-inserted h3.v3-font-body' // 注意此选择器仍可能不够精确,如果误伤请再次提供上下文
},
{
original: 'Top P set of tokens to consider during generation.',
translated: '在生成过程中考虑的 Top P token 集合。',
selector: 'ms-slider[title="Top P set of tokens to consider during generation."]',
attribute: 'title'
},
// --- 工具提示翻译 ---
// 这些通过ID定位的div是隐藏的,其innerText通常是用于aria-describedby或作为tooltip的内容源。
// 翻译它们理论上会影响最终显示的tooltip内容。
{ original: 'Expand prompts history', translated: '展开提示历史', selector: '#cdk-describedby-message-ng-1-2' },
{ original: 'Marc Data Import and Export', translated: 'MARC 数据导入导出', selector: '#cdk-describedby-message-ng-1-3' },
{ original: 'More options', translated: '更多选项', selector: '#cdk-describedby-message-ng-1-4' },
{ original: 'Rerun', translated: '重新运行', selector: '#cdk-describedby-message-ng-1-5' },
{ original: 'Select or upload a file on Google Drive to include in your prompt', translated: '选择或上传 Google 云端硬盘文件以包含在提示中', selector: '#cdk-describedby-message-ng-1-6' },
{ original: 'Upload a file to Google Drive to include in your prompt', translated: '上传文件到 Google 云端硬盘以包含在提示中', selector: '#cdk-describedby-message-ng-1-7' },
{ original: 'Insert assets such as images, videos, folders, files, or audio', translated: '插入图片、视频、文件夹、文件或音频等资产', selector: '#cdk-describedby-message-ng-1-8' },
{ original: 'Run prompt', translated: '运行提示', selector: '#cdk-describedby-message-ng-1-9' },
{ original: 'Get SDK code to chat with Gemini', translated: '获取 SDK 代码与 Gemini 聊天', selector: '#cdk-describedby-message-ng-1-10' },
{ original: 'Higher resolutions may provide better understanding but use more tokens.', translated: '更高的分辨率可能提供更好的理解,但会使用更多的 Token。', selector: '#cdk-describedby-message-ng-1-11' },
{ original: 'Enable or disable thinking for responses', translated: '启用或禁用响应的思考功能', selector: '#cdk-describedby-message-ng-1-12' },
{ original: 'Generate structured output\n\n This tool is not compatible with the current active tools.', translated: '生成结构化输出\n\n此工具与当前激活的工具不兼容。', selector: '#cdk-describedby-message-ng-1-13' },
{ original: 'Lets Gemini use code to solve complex tasks', translated: '让 Gemini 使用代码解决复杂任务', selector: '#cdk-describedby-message-ng-1-14' },
{ original: 'Lets you define functions that Gemini can call\n\n This tool is not compatible with the current active tools.', translated: '允许你定义 Gemini 可以调用的函数\n\n此工具与当前激活的工具不兼容。', selector: '#cdk-describedby-message-ng-1-15' },
{ original: 'Use Google Search', translated: '使用 Google 搜索', selector: '#cdk-describedby-message-ng-1-16' },
{ original: 'Browse the url context', translated: '浏览 URL 上下文', selector: '#cdk-describedby-message-ng-1-17' },
{ original: 'Adjust harmful response settings', translated: '调整有害响应设置', selector: '#cdk-describedby-message-ng-1-18' },
{ original: 'Probability threshold for top-p sampling', translated: 'Top P 采样概率阈值', selector: '#cdk-describedby-message-ng-1-19' },
{ original: 'Truncate response including and after string', translated: '截断响应,包含并去除指定字符串及之后的内容', selector: '#cdk-describedby-message-ng-1-20' },
{ original: 'Maximum number of tokens in response', translated: '响应的最大 Token 数量', selector: '#cdk-describedby-message-ng-1-21' },
{ original: 'Creativity allowed in the responses', translated: '响应的创造性等级', selector: '#cdk-describedby-message-ng-1-22' },
{ original: 'System Instructions', translated: '系统指令', selector: '#cdk-describedby-message-ng-1-23' },
{ original: 'Show conversation without markdown formatting', translated: '显示不带 Markdown 格式的对话', selector: '#cdk-describedby-message-ng-1-24' },
{ original: 'Share Prompt', translated: '分享提示', selector: '#cdk-describedby-message-ng-1-25' },
{ original: 'Compare mode', translated: '比较模式', selector: '#cdk-describedby-message-ng-1-26' },
{ original: 'Edit title and description', translated: '编辑标题和描述', selector: '#cdk-describedby-message-ng-1-27' },
{ original: 'Download', translated: '下载', selector: '#cdk-describedby-message-ng-1-28' },
{ original: 'Copy to clipboard', translated: '复制到剪贴板', selector: '#cdk-describedby-message-ng-1-29' },
{ original: 'Collapse code snippet', translated: '折叠代码片段', selector: '#cdk-describedby-message-ng-1-30' },
{ original: 'API pricing per 1M tokens. Usage in AI Studio UI is free of charge', translated: '每百万 Token 的 API 定价。AI Studio UI 中的使用免费。', selector: '#cdk-describedby-message-ng-1-31' },
{ original: 'Developer docs', translated: '开发者文档', selector: '#cdk-describedby-message-ng-1-32' },
// --- 左侧导航栏翻译 (新增部分) ---
{
original: 'Studio',
translated: '工作室',
selector: 'mat-expansion-panel-header:has(mat-panel-title:contains("Studio")) mat-panel-title' // 更精确的Studio面板标题
},
{
original: 'Chat',
translated: '聊天',
selector: 'a[href="/prompts/new_chat"] span.ng-trigger.ng-trigger-fadeInOut'
},
{
original: 'Stream',
translated: '流式输出',
selector: 'a[href="/live"] span.ng-trigger.ng-trigger-fadeInOut'
},
{
original: 'Generate media',
translated: '生成媒体',
selector: 'a[href="/gen-media"] span.ng-trigger.ng-trigger-fadeInOut'
},
{
original: 'Build',
translated: '构建',
selector: 'a[href="/apps"] span.ng-trigger.ng-trigger-fadeInOut'
},
{
original: 'History',
translated: '历史记录',
selector: 'a[href="/library"] span.title-label'
},
// 注意:历史记录列表项 "言之有话_前段" 等是用户输入,通常不翻译。
// "Marc Data Import and Export" 在历史记录中重复,如果它只出现一次,可以翻译。
// 但如果它也代表用户历史,最好不翻译。此处假设你需要翻译此特定链接文本。
{
original: 'Marc Data Import and Export',
translated: 'MARC 数据导入导出 (历史)',
selector: 'li[aria-describedby="cdk-describedby-message-ng-1-3"] a.prompt-link'
},
{
original: 'Dashboard',
translated: '仪表盘',
selector: 'mat-expansion-panel-header:has(mat-panel-title:contains("Dashboard")) mat-panel-title' // 更精确的Dashboard面板标题
},
{
original: 'API keys',
translated: 'API 密钥',
selector: 'a[href="/apikey"] span.ng-trigger.ng-trigger-fadeInOut'
},
{
original: 'Usage & Billing',
translated: '使用情况与计费',
selector: 'a[href="/usage"] span.ng-trigger.ng-trigger-fadeInOut'
},
{
original: 'Changelog',
translated: '更新日志',
selector: 'a[href="https://ai.google.dev/gemini-api/docs/changelog"] div.ng-trigger.ng-trigger-fadeInOut'
},
{
original: 'Documentation',
translated: '文档',
selector: 'a[href="https://ai.google.dev/gemini-api/docs"].documentation-link' // 链接文本
},
// 底部动作和免责声明
{
original: 'Google AI models may make mistakes, so double-check outputs.',
translated: 'Google AI 模型可能会出错,请仔细核对输出结果。',
selector: 'ms-navbar-disclaimer div.disclaimer'
},
{
original: 'Get API key',
translated: '获取 API 密钥',
selector: 'ms-api-key-button button[aria-label="Get API key"] span.v3-font-body'
},
{
original: 'Settings',
translated: '设置',
selector: 'ms-settings-menu button[data-test-id="settings-menu"]'
},
{
original: '[email protected]', // 替换为你的邮箱
translated: '你的邮箱 (已翻译)', // 如果你不想显示邮箱,可以替换为“我的账户”等
selector: 'button.account-switcher-button span.account-switcher-text'
},
// 如果想翻译账户切换按钮的 aria-label
{
original: 'Google 账号:嘉(logolo) ([email protected])',
translated: 'Google 账号:嘉(logolo) (已翻译)', // 或者替换为 "我的 Google 账号"
selector: '#account-switcher-button.container div.button-container',
attribute: 'aria-label'
},
];
// 翻译页面的核心函数
function applyTranslations() {
let changesMade = false; // 标记是否有元素被翻译
translations.forEach(item => {
// 使用 try-catch 捕获选择器可能导致的错误
try {
let elements = document.querySelectorAll(item.selector);
elements.forEach(element => {
// 处理 attribute 翻译 (placeholder, title, aria-label, alt 等)
if (item.attribute) {
const currentAttrValue = element.getAttribute(item.attribute);
if (currentAttrValue && currentAttrValue.trim() === item.original) {
element.setAttribute(item.attribute, item.translated);
changesMade = true;
}
}
// 默认处理 innerText 翻译
else {
if (element.innerText && element.innerText.trim() === item.original) {
element.innerText = item.translated;
changesMade = true;
}
}
});
} catch (error) {
// console.error(`选择器 "${item.selector}" 出现错误:`, error);
// 不打印错误消息
}
});
return changesMade;
}
// --- 执行翻译的策略 ---
// 1. 在 DOMContentLoaded 事件触发时尝试翻译
document.addEventListener('DOMContentLoaded', applyTranslations);
// 2. 延迟执行:对于单页应用,内容可能动态加载,延迟一段时间再次尝试
setTimeout(applyTranslations, 1000); // 延迟 1 秒后再次尝试
// 3. MutationObserver:监听DOM变化,当新内容加载或DOM结构变化时再次尝试翻译
const observer = new MutationObserver((mutationsList, observer) => {
applyTranslations();
});
// 监听整个 body 元素及其所有后代元素的子节点变化
observer.observe(document.body, { childList: true, subtree: true });
})();