// ==UserScript==
// @name Maybe 财务软件界面翻译(增强版)
// @namespace http://tampermonkey.net/
// @version 2.19
// @description 翻译 maybe 网站界面为中文,支持 SPA 页面跳转与日期翻译等增强功能。
// @author ChatGPT
// @match *://*/*
// @grant none
// @run-at document-end
// ==/UserScript==
(function () {
'use strict';
/* -------- ① 早退判断 -------- */
if (!/Maybe/i.test(document.title)) return;
/* --------------------------------------------------
静态文本翻译表(完整,不遗漏任何键)
-------------------------------------------------- */
const translations = {
// 常规 ——————————————————————————————————
"Transaction": "交易", // 已添加
"Category": "分类", // 已添加
"Amount": "金额", // 已添加
"Home": "首页",
"Default": "默认",
"Account": "账户",
"name": "名称",
"Enter asset details": "输入资产明细",
"Dashboard": "仪表盘",
"Transactions": "交易",
"Budgets": "预算",
"Assets": "资产",
"Debts": "负债",
"All": "全部",
"New debt": "新增负债",
"New budget": "新增预算",
"New transaction": "新增交易",
"New account": "新增账户",
"New asset": "新增资产",
"New import": "新增导入",
"New rule": "新增规则",
"New tag": "新增标签",
"New category": "新增分类",
"New merchant": "新增商家",
"Import": "导入",
"Edit rules": "编辑规则",
"Edit categories": "编辑分类",
"Edit tags": "编辑标签",
"Edit merchants": "编辑商家",
"Edit imports": "编辑导入",
"Total transactions": "交易总数",
"No entries found": "未找到记录",
"Try adding an entry, editing filters or refining your search": "请尝试添加记录、调整筛选或优化搜索",
"Welcome back": "欢迎回来",
"Welcome back, xiangshan": "欢迎回来,xiangshan",
"Here's what's happening with your finances": "您的财务概况",
"No accounts yet": "暂无账户",
"Add account": "添加账户",
"Add accounts to display net worth data": "添加账户以显示净资产",
"Preferences": "偏好设置",
"Security": "安全设置",
"Self hosting": "自托管",
"Accounts": "账户",
"Account": "账户",
"Imports": "数据导入",
"Tags": "标签",
"Categories": "分类",
"Rules": "规则",
"Merchants": "商家",
"More": "更多",
"What's new": "更新日志",
"Feedback": "反馈",
"Logout": "退出登录(不可用)",
"Save": "保存",
"Reset account": "重置账户",
"Delete account": "删除账户",
"Amount": "金额",
"Date format": "日期格式",
"Default Period": "默认周期",
"Country": "国家/地区",
"Language": "语言",
"Timezone": "时区",
"Currency": "货币",
"Chinese Renminbi Yuan (CNY)": "人民币 (CNY)",
"Chinese (Simplified) (zh-CN)": "中文(简体)",
"Today": "今天",
"Oops!": "哎呀!",
"Use defaults (recommended)": "使用默认分类(推荐)",
"Add an account either via connection, importing or entering manually.": "通过连接、导入或手动方式添加账户。",
"AI-enabled rule actions will cost money. Be sure to filter as narrowly as possible to avoid unnecessary costs.": "启用 AI 的规则会产生费用,请尽量精确过滤条件以避免不必要的支出。",
"Set up rules to perform actions to your transactions and other data on every account sync.": "设置规则,在每次账户同步时自动处理交易等数据。",
"Imports · 1": "导入 · 1",
"View": "查看",
"Delete": "删除",
"Require invite code for signup": "注册(不可用)需邀请码",
"Every new user that joins your instance of Maybe can only do so via an invite code": "新用户注册(不可用)必须使用邀请码",
"Require email confirmation": "需要邮箱验证",
"Generated codes": "已生成的邀请码",
"Generate new code": "生成新邀请码",
"Clear data cache": "清除数据缓存",
"Clearing the data cache will remove all exchange rates, security prices, account balances, and other data. This will not delete accounts, transactions, categories, or other user-owned data.": "此操作会清除汇率、证券价格、账户余额等缓存,不会删除账户、交易和分类等用户数据。",
"Hostings": "托管",
"Self-Hosting": "自托管",
"General Settings": "通用设置",
"Synth Settings": "Synth 设置",
"Input the API key provided by Synth": "输入 Synth 提供的 API Key",
"API Key": "API Key",
"Enter your API key here": "在此输入 API Key",
"In progress": "进行中",
// 仪表盘 ——————————————————————————
"Net Worth": "净资产",
"vs. last month": "较上月",
"No Liabilities yet": "暂无负债",
"Add your Liabilities accounts to see a full breakdown": "添加负债账户以查看完整明细",
"No cash flow data for this time period": "该周期暂无现金流数据",
"Add transactions to display cash flow data or expand the time period": "添加交易以查看现金流,或扩大时间范围",
"Add transaction": "新增交易",
"Weight": "占比",
"Value": "价值",
// 预算设置——Setup your budget
"Setup your budget": "设置预算",
"Enter your monthly earnings and planned spending below to setup your budget.": "请输入您的月收入和计划支出以设置预算。",
"Budgeted spending": "预算支出",
"Expected income": "预期收入",
"Autosuggest income & spending budget": "自动推荐收入和支出预算",
"This will be based on transaction history. AI can make mistakes, verify before continuing.": "此功能基于交易历史,AI 可能出错,请核对后继续。",
"Continue": "继续",
// 编辑分类预算——Edit your category budgets
"Edit your category budgets": "编辑分类预算",
"Adjust category budgets to set spending limits. Unallocated funds will be automatically assigned as uncategorized.":
"调整各分类预算以设置支出限额。未分配资金将自动归为未分类。",
"% set": "% 已设置",
"left to allocate": "剩余可分配",
"Confirm": "确认",
// 时间段控件 ——————————————————————
"1D": "1 天",
"7D": "7 天",
"30D": "30 天",
"90D": "90 天",
"365D": "365 天",
"5Y": "5 年",
"WTD": "本周",
"MTD": "本月",
"YTD": "年初至今",
// AI 提示 —————————————————————————
"Enable Maybe AI": "启用 Maybe AI",
"AI responses are informational only and are not financial advice.": "AI 回答仅供参考,并非财务建议。",
"To use the AI assistant, you need to set the OPENAI_ACCESS_TOKEN environment variable in your self-hosted instance.": "要使用 AI 助手,请在自托管实例中设置环境变量 OPENAI_ACCESS_TOKEN。",
"Disable anytime. All data sent to our LLM providers is anonymized.": "可随时停用。发送至大模型服务商的数据均已匿名化。",
// 导航辅助 ————————————————————————
"Back": "返回",
"Next": "下一步",
"esc": "关闭",
"Filter": "筛选",
"Clear filters": "清空筛选",
"Tag": "标签",
"Merchant": "商户",
// 规则页面 ————————————————————————
"New transaction rule": "新增交易规则",
"Rule name (optional)": "规则名称(可选)",
"Enter a name for this rule": "请输入规则名称",
"IF": "如果",
"Add condition": "添加条件",
"Add condition group": "添加条件组",
"THEN": "那么",
"Add action": "添加动作",
"FOR": "作用范围",
"All past and future transactions": "所有历史及未来交易",
"Starting from": "起始日期",
"yyyy/mm/日": "年/月/日",
// 分类设置 ————————————————————————
"unassigned": "未分配",
"categories": "分类",
"Income categories": "收入分类",
"Expense categories": "支出分类",
"Name": "名称",
"Edit": "编辑",
"Classification": "分类类型",
// 账户类型选择 —————————————————————
"Enter": "输入",
"account balance": "账户余额",
"balance": "余额",
"Select account type": "选择账户类型",
"Select": "选择",
"None": "无",
"Checking": "支票账户",
"Savings": "储蓄账户",
"Health Savings Account": "健康储蓄账户 (HSA)",
"Certificate of Deposit": "定期存款 (CD)",
"Money Market": "货币市场账户",
// 账户添加 ————————————————————————
"What would you like to add?": "请选择要添加的账户类型",
"Cash": "现金账户",
"Investment": "投资账户",
"Crypto": "加密资产",
"Property": "房产",
"Vehicle": "车辆",
"Credit Card": "信用卡",
"Loan": "贷款",
"Other Asset": "其他资产",
"Other Liability": "其他负债",
"Import accounts": "导入账户",
"Select": "选择",
"Navigate": "导航",
"Close": "关闭",
// 规则设置 ————————————————————————
"Rule": "规则",
"Transaction name": "交易名称",
"Transaction amount": "交易金额",
"Transaction merchant": "交易商户",
"Contains": "包含",
"Equal to": "等于",
"match": "满足",
"all": "全部条件",
"any": "任一条件",
"none": "不包含",
"enter a value": "输入值",
"of the following conditions": "以下条件",
"Set transaction category": "设置交易分类",
"Set transaction tags": "设置交易标签",
"Set transaction merchant": "设置交易商户",
"Set transaction name": "设置交易名称",
// 转账 / 新增交易 ————————————————————
"New transfer": "新增转账",
"Expense": "支出",
"Income": "收入",
"Expenses": "支出",
"Transfer": "转账",
"From": "转出账户",
"To": "转入账户",
"Select account": "选择账户",
"Date": "日期",
"Create transfer": "创建转账",
// 交易字段 ————————————————————————
"Description": "交易说明",
"Description*": "交易说明 *",
"Description *": "交易说明 *",
"Describe transaction": "请输入交易说明",
"Account*": "账户 *",
"Select an Account": "选择账户",
"Category": "分类",
"Select a Category": "选择分类",
"Details": "详细信息",
"Notes": "备注",
"Enter a note": "请输入备注",
"(none)": "无",
// 账户详情 ————————————————————————
"Other Assets": "其他资产",
"Cash Flow": "现金流",
"Balance": "余额",
"no change vs. last month": "与上月持平",
"Import transactions": "导入交易",
"Activity": "活动",
"Search entries by name": "按名称搜索条目",
"New": "新增",
"Delete Account?": "删除账户?",
"Are you sure you want to delete account?": "确定要删除账户吗?",
"This is not reversible.": "此操作不可撤销。",
"Delete Account": "删除账户",
"BALANCE": "余额",
// 房产相关 ————————————————————————————
"Select property type": "选择房产类型",
"Single Family Home": "独立住宅",
"Multi-Family Home": "多户住宅",
"Condominium": "公寓",
"Townhouse": "联排别墅",
"Investment Property": "投资房产",
"Second Home": "第二住宅",
"Year built": "建造年份",
"Living area": "居住面积",
"Street address": "街道地址",
"City": "城市",
"ZIP/Postal code": "邮政编码",
"Unit of measurement": "计量单位",
"Square Feet": "平方英尺",
"State/Province": "州/省",
// 其他 ————————————————————————————
"Liabilities": "负债",
"Cashflow": "现金流",
"Confirm": "确认",
// 导入 ————————————————————————————
"No imports yet.": "尚未导入数据。",
"New CSV Import": "新的 CSV 导入",
"You can manually import various types of data via CSV or use one of our import templates like Mint.": "您可以通过 CSV 手动导入各种数据,或使用我们的导入模板,如 Mint。",
"SOURCES": "来源",
"Import investments": "导入投资",
"Import from Mint": "从 Mint 导入",
"Import your data": "导入您的数据",
"Paste or upload your CSV file below. Please review the instructions in the table below before beginning.": "在下方粘贴或上传您的 CSV 文件。请在开始之前查看表格中的说明。",
"Upload CSV Copy & Paste": "上传 CSV 或复制粘贴",
"Browse to add your CSV file here": "浏览以添加您的 CSV 文件",
"Upload CSV": "上传 CSV",
"Download a sample CSV to see the required CSV format": "下载样本 CSV 查看所需的 CSV 格式",
"Resume Account Import": "恢复账户导入",
"Please finalize your file upload.": "请完成文件上传。",
"Account (optional)": "账户(可选)",
"Multi-account import": "多账户导入",
"Paste your CSV file contents here": "在此粘贴您的 CSV 文件内容",
"Configure your import": "配置您的导入",
"Select the columns that correspond to each field in your CSV.": "选择与 CSV 中每个字段对应的列。",
"Custom column": "自定义列",
"Amount type strategy": "金额类型设置", // 更加准确的翻译
"Signed amount": "带符号金额", // 更加准确的翻译
"Incomes are positive": "收入为正数",
"Incomes are negative": "收入为负数",
"Leave empty": "留空",
"Apply configuration": "应用配置",
"Sample data from your uploaded CSV": "来自您上传的 CSV 的示例数据",
"Please configure your import before proceeding": "请在继续之前配置您的导入",
"as amount type column": "将其设置为金额类型列",
"Set": "设置",
"Select column": "选择列",
"Upload": "上传",
"Configure": "配置",
"Clean": "清理",
"Map": "映射",
// CSV 相关术语
"Comma (,)": "逗号 (,)",
"Semicolon (;)": "分号 (;)",
"Col sep": "列分隔符",
"IMPORTS": "导入",
"Import": "导入",
"Transaction": "交易",
"Account": "账户",
"Revert import?": "撤销导入?",
"This will delete transactions that were imported, but you will still be able to review and re-import your data at any time.":
"这将删除已导入的交易,但您仍然可以随时查看并重新导入数据。",
"Revert": "撤销",
// 页面中的动态日期翻译
"Depositories": "存款账户",
"Current Balance": "当前余额",
"Initial Balance": "初始余额",
"Activity": "活动",
"Amount": "金额",
"Remaining": "剩余金额",
"Search entries by name": "按名称搜索记录",
"New": "新增",
"Date": "日期",
// 分类弹窗 ————————————————————————
"Uncategorized": "未分类",
"OVERVIEW": "概览",
"Monthly average spending": "平均月支出",
"Monthly median spending": "月支出中位数",
"RECENT TRANSACTIONS": "最近交易",
"No transactions found for this budget period.": "此预算周期内未找到交易。"
};
/* --------------------------------------------------
月份 & 星期映射(含缩写)
-------------------------------------------------- */
const monthMap = {
January: '1月', Jan: '1月',
February: '2月', Feb: '2月',
March: '3月', Mar: '3月',
April: '4月', Apr: '4月',
May: '5月',
June: '6月', Jun: '6月',
July: '7月', Jul: '7月',
August: '8月', Aug: '8月',
September: '9月',Sep: '9月', Sept: '9月',
October: '10月', Oct: '10月',
November: '11月',Nov: '11月',
December: '12月',Dec: '12月'
};
const weekdayMap = {
Monday: '星期一', Mon: '星期一',
Tuesday: '星期二', Tue: '星期二',
Wednesday: '星期三', Wed: '星期三',
Thursday: '星期四', Thu: '星期四',
Friday: '星期五', Fri: '星期五',
Saturday: '星期六', Sat: '星期六',
Sunday: '星期日', Sun: '星期日'
};
/* --------------------------------------------------
动态文本翻译函数
-------------------------------------------------- */
// 金额 + avg e.g. "¥0.00 avg"
const translateAmountAvg = txt => {
const m = txt.match(/^(.+?)\s+avg$/i);
return m ? `${m[1]} 平均` : null;
};
// AM/PM 转换
const timeMap = {
"AM": "上午",
"PM": "下午"
};
// 日期翻译函数
const translateFullDate = txt => {
const m = txt.match(
/^(Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:t(?:ember)?)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)\s+(\d{1,2}),\s+(\d{4})\s+at\s+(\d{1,2}):(\d{2})\s(AM|PM)$/i
);
if (m) {
return `${m[3]}年${monthMap[m[1]]}${parseInt(m[2], 10)}日 ${timeMap[m[6]]} ${m[4]}:${m[5]}`;
}
return null;
};
// "Jun 2025 spending"
const translateMonthlySpending = txt => {
const m = txt.match(
/^(Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:t(?:ember)?)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)\s(\d{4})\sspending$/i
);
return m ? `${m[2]}年${monthMap[m[1]]}支出` : null;
};
// "May 2025"
const translateYearMonth = txt => {
const m = txt.match(
/^(Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:t(?:ember)?)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)\s(\d{4})$/i
);
return m ? `${m[2]}年${monthMap[m[1]]}` : null;
};
// on or after / before YYYY-MM-DD
const translateDateRange = txt => {
const after = txt.match(/^on or after (\d{4}-\d{2}-\d{2})$/);
if (after) return `起始日期:${after[1]}`;
const before = txt.match(/^on or before (\d{4}-\d{2}-\d{2})$/);
return before ? `结束日期:${before[1]}` : null;
};
// 30D / 1W / 12M / 1Y
const translateShorthandPeriod = txt => {
const m = txt.match(/^(\d+)([DWMY])$/);
if (!m) return null;
const map = { D: "天", W: "周", M: "月", Y: "年" };
return `${m[1]}${map[m[2]]}`;
};
// Last 30 days
const translateLastPeriod = txt => {
const m = txt.match(/^Last (\d+) (days?|weeks?|months?|years?)$/i);
if (!m) return null;
const unit = m[2].toLowerCase().startsWith("day")
? "天"
: m[2].startsWith("week")
? "周"
: m[2].startsWith("month")
? "个月"
: "年";
return `最近${m[1]}${unit}`;
};
const translateTransactionImport = txt => {
const m = txt.match(/^Transaction Import: (.*) at (\d{1,2}):(\d{2}) (AM|PM)$/i);
if (m) {
const monthDayYear = `${m[1].replace(/\d{4}/, (match) => `${match}年`)}`; // 修改为包含中文年份
const time = `${m[2]}:${m[3]} ${m[4] === 'AM' ? '上午' : '下午'}`;
return `交易导入:${monthDayYear} ${time} 完成`;
}
return null;
};
// "¥0/m avg" -> "¥0.00/月 平均"
const translatePerMonthAvg = txt => {
const m = txt.match(/^(.+?)\/m avg$/i);
return m ? `${m[1]}/月 平均` : null;
};
// ---------------------------------------------
// 动态翻译函数列表更新
// ---------------------------------------------
const dynamicFns = [
translateTransactionImport,
translatePerMonthAvg,
translateAmountAvg,
translateFullDate,
translateMonthlySpending,
translateYearMonth,
translateDateRange,
translateShorthandPeriod,
translateLastPeriod
];
/* --------------------------------------------------
文本节点翻译入口
-------------------------------------------------- */
// 动态文本翻译函数
const translateTextNode = node => {
if (!node || node.nodeType !== 3) return;
const raw = node.textContent;
const txt = raw.trim();
if (!txt) return;
// 1) 静态翻译(大小写不敏感)
const cand =
translations[txt] ||
translations[txt.toUpperCase()] ||
translations[txt.toLowerCase()];
if (cand) {
node.textContent = raw.replace(txt, cand);
return;
}
// 2) 动态翻译: 日期
const dateTranslated = translateFullDate(txt);
if (dateTranslated) {
node.textContent = raw.replace(txt, dateTranslated);
return;
}
// 3) 单独月份或星期
if (monthMap[txt]) {
node.textContent = raw.replace(txt, monthMap[txt]);
} else if (weekdayMap[txt]) {
node.textContent = raw.replace(txt, weekdayMap[txt]);
}
};
const translatePage = () => {
const walker = document.createTreeWalker(
document.body,
NodeFilter.SHOW_TEXT,
null,
false
);
let n;
while ((n = walker.nextNode())) translateTextNode(n);
};
// 初始 + 轮询
translatePage();
const INTERVAL = 300;
setInterval(translatePage, INTERVAL);
// 监听 SPA 路由变化
let lastUrl = location.href;
setInterval(() => {
if (location.href !== lastUrl) {
lastUrl = location.href;
setTimeout(translatePage, 500);
}
}, 500);
})();