Maybe 财务软件界面翻译(增强版)

翻译 maybe 网站界面为中文,支持 SPA 页面跳转与日期翻译等增强功能。

当前为 2025-07-03 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Maybe 财务软件界面翻译(增强版)
// @namespace    http://tampermonkey.net/
// @version      2.15
// @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 = {
  // 常规 ——————————————————————————————————
  "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": "价值",

  // 时间段控件 ——————————————————————
  "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": "关闭",

  // 规则页面 ————————————————————————
  "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": "其他资产",
  "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": "账户",
      // 分类弹窗 ————————————————————————
  "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 dynamicFns = [
    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);
})();