From 4107954080c19b967101227b478234c7aa0d05c2 Mon Sep 17 00:00:00 2001 From: WaterRain <109326062+CATMIAOZHI@users.noreply.github.com> Date: Sat, 1 Aug 2026 13:43:05 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=BA=94=E7=94=A8=E5=86=85=E8=AF=AD?= =?UTF-8?q?=E8=A8=80=E5=88=87=E6=8D=A2=EF=BC=88=E8=B7=9F=E9=9A=8F=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F/=E7=AE=80/=E7=B9=81/=E8=8B=B1=EF=BC=89+=20=E7=B9=81?= =?UTF-8?q?=E4=BD=93=E4=B8=AD=E6=96=87=E6=94=AF=E6=8C=81=20+=20=E5=BA=94?= =?UTF-8?q?=E7=94=A8=E5=90=8D/=E7=BD=B2=E5=90=8D=E5=88=86=E8=AF=AD?= =?UTF-8?q?=E8=A8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 LocaleManager:语言偏好存 SharedPreferences,createConfigurationContext + LocaleList 包装 Application/MainActivity 的 attachBaseContext(零新依赖) - 设置页新增语言卡片与选择对话框:跟随系统 / 简体中文 / 繁體中文 / English - 新增繁体中文 values-b+zh+Hant(434 条,OpenCC s2twp 台湾标准转换+人工修正) - 简体目录改为 values-b+zh+Hans(BCP-47,与繁体对称;AGP9 不接受 values-zh-Hans) - widget_brand 可翻译:英=Rainy / 简繁=雨晴喵;app_name 可翻译:英=RainyToken / 简繁=雨晴Token - 资源三语言 434/434/434 对齐,占位符零差异 --- .../main/java/com/rainy/token/MainActivity.kt | 6 + .../com/rainy/token/RainyTokenApplication.kt | 6 + .../rainy/token/ui/settings/SettingsScreen.kt | 108 ++++ .../com/rainy/token/util/LocaleManager.kt | 55 ++ app/src/main/res/values-b+zh+Hans/strings.xml | 489 ++++++++++++++++++ app/src/main/res/values-b+zh+Hant/strings.xml | 489 ++++++++++++++++++ app/src/main/res/values/strings.xml | 22 +- 7 files changed, 1168 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/com/rainy/token/util/LocaleManager.kt create mode 100644 app/src/main/res/values-b+zh+Hans/strings.xml create mode 100644 app/src/main/res/values-b+zh+Hant/strings.xml diff --git a/app/src/main/java/com/rainy/token/MainActivity.kt b/app/src/main/java/com/rainy/token/MainActivity.kt index 98065c8..98ab02e 100644 --- a/app/src/main/java/com/rainy/token/MainActivity.kt +++ b/app/src/main/java/com/rainy/token/MainActivity.kt @@ -1,5 +1,6 @@ package com.rainy.token +import android.content.Context import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent @@ -20,6 +21,11 @@ import dagger.hilt.android.AndroidEntryPoint */ @AndroidEntryPoint class MainActivity : ComponentActivity() { + override fun attachBaseContext(newBase: Context) { + // 应用内语言偏好在此层生效:recreate() 后资源按所选语言重新解析 + super.attachBaseContext(com.rainy.token.util.LocaleManager.wrapContext(newBase)) + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() diff --git a/app/src/main/java/com/rainy/token/RainyTokenApplication.kt b/app/src/main/java/com/rainy/token/RainyTokenApplication.kt index 6b981ec..1939403 100644 --- a/app/src/main/java/com/rainy/token/RainyTokenApplication.kt +++ b/app/src/main/java/com/rainy/token/RainyTokenApplication.kt @@ -12,6 +12,12 @@ import dagger.hilt.android.HiltAndroidApp */ @HiltAndroidApp class RainyTokenApplication : Application() { + override fun attachBaseContext(base: Context) { + // 应用内语言偏好(跟随系统 / 中文 / English)在 Application 层生效, + // 保证 appContext 与系统级回调(如小组件)之外的代码都使用所选语言。 + super.attachBaseContext(com.rainy.token.util.LocaleManager.wrapContext(base)) + } + override fun onCreate() { super.onCreate() appContext = applicationContext diff --git a/app/src/main/java/com/rainy/token/ui/settings/SettingsScreen.kt b/app/src/main/java/com/rainy/token/ui/settings/SettingsScreen.kt index d0fcea0..8ad5f32 100644 --- a/app/src/main/java/com/rainy/token/ui/settings/SettingsScreen.kt +++ b/app/src/main/java/com/rainy/token/ui/settings/SettingsScreen.kt @@ -1,5 +1,6 @@ package com.rainy.token.ui.settings +import android.app.Activity import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -17,6 +18,7 @@ import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material3.AlertDialog import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.CircularProgressIndicator @@ -26,13 +28,18 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp @@ -50,6 +57,7 @@ import com.rainy.token.R import com.rainy.token.ui.components.AppTips import com.rainy.token.ui.theme.InkMuted import com.rainy.token.ui.theme.StrawberryPink +import com.rainy.token.util.LocaleManager /** * 设置页(雨晴风格重做版)。 @@ -74,6 +82,7 @@ fun SettingsScreen( } val uiState by viewModel.uiState.collectAsStateWithLifecycle() + var showLanguageDialog by remember { mutableStateOf(false) } Scaffold( containerColor = Color.Transparent, @@ -133,6 +142,9 @@ fun SettingsScreen( onClick = { onEditCredential(status.service) } ) } + item { + LanguageCard(onClick = { showLanguageDialog = true }) + } item { TipsCard(onClick = { onOpenTips() }) } @@ -146,6 +158,102 @@ fun SettingsScreen( } } } + + if (showLanguageDialog) { + LanguageDialog(onDismiss = { showLanguageDialog = false }) + } +} + +@Composable +private fun LanguageCard(onClick: () -> Unit) { + val context = LocalContext.current + val current = LocaleManager.getLocaleCode(context) + val labelRes = when (current) { + "zh" -> R.string.app_locale_zh + "zh-Hant" -> R.string.app_locale_zh_hant + "en" -> R.string.app_locale_en + else -> R.string.app_locale_follow_system + } + Card( + modifier = Modifier + .fillMaxWidth() + .clickable { onClick() }, + shape = RoundedCornerShape(20.dp), + colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface), + elevation = CardDefaults.cardElevation(defaultElevation = 0.dp) + ) { + Row( + modifier = Modifier.padding(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "🌐", + style = MaterialTheme.typography.titleLarge + ) + Spacer(modifier = Modifier.width(12.dp)) + Column(modifier = Modifier.weight(1f)) { + Text( + text = stringResource(R.string.title_language), + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.SemiBold + ) + Text( + text = stringResource(labelRes), + style = MaterialTheme.typography.bodySmall, + color = InkMuted, + modifier = Modifier.padding(top = 2.dp) + ) + } + } + } +} + +@Composable +private fun LanguageDialog(onDismiss: () -> Unit) { + val context = LocalContext.current + val current = LocaleManager.getLocaleCode(context) + val options: List> = listOf( + null to R.string.app_locale_follow_system, + "zh" to R.string.app_locale_zh, + "zh-Hant" to R.string.app_locale_zh_hant, + "en" to R.string.app_locale_en + ) + AlertDialog( + onDismissRequest = onDismiss, + title = { Text(stringResource(R.string.dialog_language_title)) }, + text = { + Column { + options.forEach { (code, labelRes) -> + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { + LocaleManager.saveLocale(context, code) + onDismiss() + // 重建 Activity 让资源按新语言重新解析 + (context as? Activity)?.recreate() + } + .padding(vertical = 12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = stringResource(labelRes), + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.weight(1f) + ) + if (current == code) { + Text("✓", color = StrawberryPink, style = MaterialTheme.typography.bodyMedium) + } + } + } + } + }, + confirmButton = { + TextButton(onClick = onDismiss) { + Text(stringResource(R.string.action_cancel)) + } + } + ) } @Composable diff --git a/app/src/main/java/com/rainy/token/util/LocaleManager.kt b/app/src/main/java/com/rainy/token/util/LocaleManager.kt new file mode 100644 index 0000000..1152bc3 --- /dev/null +++ b/app/src/main/java/com/rainy/token/util/LocaleManager.kt @@ -0,0 +1,55 @@ +package com.rainy.token.util + +import android.content.Context +import android.content.res.Configuration +import java.util.Locale + +/** + * 应用内语言偏好管理(自建轻量方案,不引入 appcompat 依赖)。 + * + * 语言值约定: + * - `null`(未设置)= 跟随系统 + * - `"zh"` = 简体中文(匹配 values-b+zh+Hans,BCP-47 写法,覆盖中国大陆等简体地区) + * - `"zh-Hant"` = 繁体中文(匹配 values-b+zh+Hant,BCP-47 写法,覆盖台/港/澳所有繁体地区) + * - `"en"` = English(匹配默认 values/ 英文资源) + * + * 注:AGP 9 资源合并器不接受 values-zh-Hans/values-zh-Hant 目录名,故用等价的 + * BCP-47 写法 values-b+zh+Hans / values-b+zh+Hant;编译产物同为 + * language=zh + script=Hans/Hant 配置。 + * + * 在 [Application] 与 [MainActivity] 的 attachBaseContext 中调用 [wrapContext] + * 应用偏好;切换语言后由设置页保存偏好并 recreate Activity 触发重新解析。 + */ +object LocaleManager { + + private const val PREFS_NAME = "locale_prefs" + private const val KEY_LOCALE = "locale" + + /** 当前保存的语言代码;null = 跟随系统 */ + fun getLocaleCode(context: Context): String? { + val code = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) + .getString(KEY_LOCALE, null) ?: return null + return code.takeIf { it == "zh" || it == "zh-Hant" || it == "en" } + } + + /** 保存语言偏好;传 null 表示跟随系统 */ + fun saveLocale(context: Context, code: String?) { + context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) + .edit() + .putString(KEY_LOCALE, code) + .apply() + } + + /** 把 base context 包装上应用语言配置;未设置偏好时原样返回 */ + fun wrapContext(base: Context): Context { + val code = getLocaleCode(base) ?: return base + val locale = when (code) { + "zh" -> Locale.SIMPLIFIED_CHINESE + "zh-Hant" -> Locale.forLanguageTag("zh-Hant") + else -> Locale.ENGLISH + } + val config = Configuration(base.resources.configuration) + config.setLocales(android.os.LocaleList(locale)) + return base.createConfigurationContext(config) + } +} diff --git a/app/src/main/res/values-b+zh+Hans/strings.xml b/app/src/main/res/values-b+zh+Hans/strings.xml new file mode 100644 index 0000000..8bc27aa --- /dev/null +++ b/app/src/main/res/values-b+zh+Hans/strings.xml @@ -0,0 +1,489 @@ + + + + AI用量面板 + OpenCode Go 用量 + DeepSeek 余额,自动刷新 + 雨晴喵 + 雨晴Token + + + + + 返回 + 取消 + 确认添加 + 我知道了 + 刷新 + 设置 + 删除 + 清除 + 保存 + 跳转 + ◀ 上一页 + 下一页 ▶ + 添加小组件到桌面 + 语言 + 选择语言 + 跟随系统 + 简体中文 + 繁體中文 + English + 查看详情 + 查看用量详情 + 详细数据 + 同步用量数据 + 我已登录,保存 + 继续等待 + + + 加载中… + 未知错误 + 全部模型 + + + 👈 选择服务查看详情 + 点击左侧卡片即可 + 设置 + 凭据管理 + 关于 RainyToken + 使用小技巧 + 调试日志 + 用量详情 + 总统计 + 详细数据 + 统计图表 + + + 未配置 + 已配置 + 已过期 + 需重登 + 刷新失败 + 未获取 + 待获取 + 正常 + 未配置 · 点击添加 + 已配置 · 凭据有效 + 已过期 · 点击重新配置 + 需要重新验证 + 手动输入模式 + 凭据已配置 + 未配置凭据 + 加载中 + 最新 + 缓存 + 失败 + 待输入 + + + AI 余额一览 + 从未获取 + 刚刚更新 + %1$d 分钟前更新 + %1$d 小时前更新 + %1$s 更新 + 💡 长按卡片可拖拽排序 + ⚠ %1$s + + 本地加密存储,不上传任何数据 + 添加桌面小组件? + 确认后会请求桌面启动器添加雨晴Token小组件。部分系统仍会弹出系统确认窗口。 + 桌面快捷方式权限已被拒绝,请在系统设置中允许后重试 + 桌面未接受添加请求,请检查启动器权限 + 当前桌面不支持应用内添加,请在桌面长按 → 小组件 → 查找雨晴Token + + + 点击配置凭据 + 下拉刷新 + 服务当前不可用 + 本月已用 %1$s%2$s + 剩余 + · 已用 %1$s / 共 %2$s + %1$s 已用 + 5h 已用 + · %1$s + %1$s 后重置 + REST API · ¥ + WebView 抓取 · 5h 配额 + JSON API · $ + ChatGPT Plus · Codex 额度 + Cookie 抓取 · Cloud 配额 + + + 天 + 小时 + 分 + + + 5 小时 + 5h + 5h 用量 + 5 小时滚动 + 本周 + 本月 + 每月 + 用量 + 用量窗口%1$s + 用量窗口 + + + OpenCode 用量 + CommandCode 用量 + %1$d 条记录 + 点击同步 + 输入 Token + 输出 Token + 推理 Token + 总花费 + 缓存读取 %1$s + 缓存写入 %1$s + 暂无数据,点击 🔄 同步 + ✓ 新增 %1$d 条 + 点击切换 + 所有时间按 UTC+0 计算 + 已切换到 UTC+8,图表按北京时间显示 + 已切换到 UTC+0,图表按世界协调时显示 + 已用 $%1$s / 共 $%2$s + 剩余 $%1$s + 额外充值 + 按模型统计 + 按天统计(UTC+0) + %1$d 个模型 + %1$d 个 + 开始时间 + 结束时间 + + + 消耗金额 (USD) + API 请求次数 + Token 消耗 + 输出 + 输入(未命中) + 命中缓存 + + + 时间 + 模型 + 输入(缓存) + 输出 + 成本 + + + 全部 + 最近5小时 + 最近12小时 + 最近24小时 + 今天 + 昨天 + 最近7天 + 最近30天 + 当月 + 自定义 + 自定义天 + 自定义月 + 自定义时间 + + + 统一查询 AI 余额的小工具。\n本地加密存储凭据,不上传任何数据。 + 查看全部 %1$d 条隐藏操作 + 查看 token 刷新、网络请求等调试记录 + + + ✓ 已配置登录态(%1$d 个 Cookie) + 如何获取 Cookie? + 1. 在桌面 Chrome 打开下方登录入口 + 2. 用 GitHub / Google 登录(不会被拦截) + 3. 登录后按 F12 → Application → Cookies + 4. 复制该站点下的所有 Cookie 粘贴到上面输入框 + 格式:name1=value1; name2=value2 + 如何获取 OpenCode Go 凭据? + 1. 桌面 Chrome 打开 opencode.ai/auth + 2. 用 GitHub / Google 登录(Google 不会拦截 Chrome) + 3. 登录后跳转到 dashboard,URL 形如: + https://opencode.ai/workspace/{id}/go + 4. 复制 URL 中的 {id} 段 → workspaceId + 5. F12 → Application → Cookies + 6. 找到域名 opencode.ai 下名为 auth 的 Cookie + 7. 复制它的 Value 字段 → auth cookie + 注意:只填 auth 一个 Cookie 即可,不要整个 Cookie 字符串 + 如何获取 CommandCode Go 凭据? + 1. 打开 https://commandcode.ai 并登录 GitHub + ▸ 获取 API Key(必填): + 右上角头像 → Settings → API Keys → 创建一个 Key + ▸ 获取全部 Cookie(用于用量记录): + F12 → Application → Cookies → commandcode.ai + 选中所有 Cookie,复制整个 Cookie 字符串 + 粘贴到下方输入框即可,无需逐个挑选 + 提示:不填 Cookie 也能正常查看余额,只是用量记录为空。 + 如何获取 Codex auth.json? + 1. 在终端运行 codex-oauth-proxy 或 + codex-proxy,完成 ChatGPT 登录 + 2. 凭证文件保存在: + + + 已保存 + 已保存凭据 + 连接成功!余额: %1$s %2$s + 连接成功,凭据已保存 + API Key 不能为空 + JSON 格式错误 + 服务拒绝该凭据 (401/403) + 测试期间凭据已变更 + 请求过于频繁 (429) + 服务端错误 (%1$d) + 网络错误:%1$s + ,已恢复原凭据 + ,检测到凭据已变化,未执行回滚 + 测试失败。%1$s%2$s + 暂不支持测试此服务 + auth cookie 和 workspaceId 都需要填写 + Cookie 不能为空 + 凭据未配置 + 凭据未配置或已过期 + 凭据无效,请在设置中重新配置 + 凭据无效,请重新登录 + 凭据已变更,正在重新加载 + 凭据已变更,请重新操作 + 网络异常,请检查网络 + 网络异常 + 服务端异常 (HTTP %1$d) + 请求过于频繁%1$s + ,请 %1$d 秒后重试 + 不支持激活用量 + 请先选择模型 + 未抓到 Cookie,请确认已登录 + 未配置登录 URL + URL 应以 %1$s 开头 + 授权失败: %1$s + State 不匹配,请重试 + 回调缺少授权码 + Token 交换失败,请查看调试日志 + + + ✓ 用量已激活 + ✓ 激活成功 + 请求结果 + 错误: %1$s + 响应内容:\n%1$s + 模型: %1$s + 响应 ID: %1$s + 回复: + (空) + (无回复内容) + (响应解析失败,但请求已发出) + 用量: prompt=%1$s completion=%2$s total=%3$s tokens + 用量: input=%1$s output=%2$s tokens + token 刷新失败:%1$s + + + 登录 %1$s + 完成登录了吗? + 如果你已经在页面上完成登录但页面没有跳转,可点击下方按钮让我抓取 Cookie 并保存登录态。 + + + 暂无日志记录\n刷新 Codex 用量后这里会出现调试信息 + + + OpenCode Go · 用量 + DeepSeek 余额 + 自动刷新 + %1$s · 额度 + %1$s · %2$s + 更新 %1$s + 暂无数据 + 刷新中… + + + 长按卡片拖拽排序 + 长按首页卡片可以拖拽排序 + 在首页长按任意服务卡片即可拖拽到想要的位置,排序会自动保存。 + 小组件点左上角进 APP + 小组件左上角标识可打开 APP + 桌面小组件左上角 RainyToken › 标识可以点击打开应用,其它区域点击切换服务。 + 图表点击查看详情 + 点击图表柱子可查看时段详情 + 在用量图表上点击或滑动手指,可查看对应时段的详细数值。 + 图表标题可点击 + 点击图表标题弹出按模型明细 + 图表卡片标题(如「消耗金额」)点击后会弹出按模型拆分的明细。 + 切换图表时区 + 用量详情页可切换 UTC+0 / UTC+8 + 用量详情页顶部可切换 UTC+0 / UTC+8,图表标签会跟随切换。 + CCGO 清除并重新同步 + CCGO 详情页可清除缓存重新同步 + CCGO 用量详情页右上角「清除」可删除本地缓存并重新拉取全部数据。 + 查看用量详情 + 服务卡片底部可进入用量图表 + 有凭据的服务卡片底部会出现「查看用量详情」入口,点击进入图表统计。 + 原始数据浏览 + 用量详情页右上角可查看原始记录 + 用量详情页右上角「详细数据」可查看每条原始调用记录,支持分页跳转。 + 下拉刷新联动用量 + 下拉刷新会同步余额和用量 + 首页下拉刷新不仅刷新余额,还会自动同步 OCGO / CCGO 用量记录。 + 凭据获取教程 + 凭据页底部有获取教程 + 凭据编辑页底部的「如何获取」按钮有针对每个服务的详细操作步骤。 + 剪贴板自动导入 + Codex 支持从剪贴板一键导入 + Codex 凭据页支持「从剪贴板自动导入」,粘贴 auth.json 后一键填入。 + 详细数据页码跳转 + 原始数据页可输入页码直跳 + 原始数据页底部可输入页码直接跳转,数据量大时不必逐页翻。 + 小组件自动刷新 + 划到小组件会自动刷新余额 + 桌面小组件划到负一屏时会自动刷新余额,也可手动点 ↻ 按钮刷新。 + + + %1$d次 + 选择日期 + 选择月份(任意一天) + 开始日期 + 结束日期 + 消费明细 + 调用次数明细 + Token 明细 + 清除数据 + 此操作将清除所有 CommandCode 用量本地缓存并重新同步,需要几秒钟完成。 + 是否继续? + 确认(%1$ds) + 确认清除 + 关闭 + 从 %1$s + 至 %1$s + 应用自定义范围 + 确定 + 结束日期不能早于开始日期 %1$s + 时间 %1$s:%2$s + 暂无数据 + 合计: %1$s + 总计 + 记录数 + 缓存读取 + 缓存写入 + 缓存 Token 细分 + ※ 缓存读取已计入「输入 Token」 + %1$d 次调用 + MM月dd日 EEEE + %1$d 次 · $%2$s + 原始数据 + 缓存写入(5m) + 缓存写入(1h) + 成本(USD) + 成本(Raw) + + + ~/.config/codex-oauth-proxy/auth.json + 3. 用文本编辑器打开该文件, + 复制全部内容粘贴到上方输入框 + JSON 格式示例: + {"tokens": { + "access_token": "...", + "refresh_token": "...", + "expiresAt": 1783727108252 + }} + 提示:导入后 APP 会自动刷新 token, + 不需要手动更新。 + Ollama Pro 凭据 + 需要从浏览器复制完整 Cookie。登录 ollama.com/settings 后,在 DevTools → Network → 请求头里找到 Cookie 行复制。 + 更新凭据 + 保存凭据 + 测试并保存 + 在浏览器中打开 Ollama Settings + 复制登录 URL 到剪贴板 + 一键激活用量 API Key(可选) + 填入 Ollama Cloud API Key 后,可在详情页一键发送请求激活用量统计。从 ollama.com/settings → API Keys 获取。 + ollama-xxx 或 sk-xxx + 保存 API Key + OpenCode Go 凭据 + 需要 auth cookie + workspaceId 两个值。APP 会用 OkHttp 抓 dashboard 并自动解析 5h/周/月 三档用量。 + 从剪贴板自动导入 + dashboard URL 中的 {id} 段 + auth Cookie 值 + F12 → Cookies → opencode.ai → auth 的 Value + 如何获取这两个值? + 在浏览器中打开登录入口 + 填入 OpenCode Zen API Key 后,可在详情页一键发送请求激活用量统计。从 opencode.ai/settings → API Keys 获取。 + CommandCode Go 凭据 + 需要 API Key(拉余额)。Session Cookie(可选)在浏览器 DevTools 复制,用于拉用量记录。不填也可正常查看余额。 + 从 Settings → API Keys 生成 + Session Cookie(可选) + F12 → Cookies → commandcode.ai → 全选复制粘贴即可 + 如何获取 API Key 和 Cookie? + 请输入 API Key + 凭据会使用 Android Keystore (AES-256 GCM) 加密后存储到本机 DataStore。 + 更新 + Codex / ChatGPT Plus 凭据 + 推荐使用 OAuth 登录自动获取凭据。也可手动粘贴 auth.json 内容。 + 🔐 OAuth 登录(推荐) + ── 或手动导入 ── + auth.json 完整内容 + 粘贴 {"tokens": {...}} 完整 JSON + 导入并保存 + 如何获取 auth.json? + 粘贴登录态 + Google OAuth 会拒绝 APP 内置 WebView,所以请用浏览器登录后把 Cookie 粘到这里。 + Cookie 字符串 + 保存 Cookie + + + 数据解析失败: %1$s + 每周 + 模型调用次数 + 缓存数据 · %1$s + 显示的是上次成功获取的余额 + 本月消费 %1$s%2$s + 余额构成 + 赠送余额 + 自费充值 + 加载模型列表... + 选择模型 + 刷新模型列表 + 激活中... + 重试激活用量 + ⚡ 一键激活用量 + 刷新余额 + 重新登录 + 配置凭据 + 当前余额 + 5h 实时用量 + 月度余额 + Session 用量 + 请选择模型 + 错误详情: + API 响应内容: + 复制 + *** (长度 %1$d) *** + %1$s***%2$s (长度 %3$d) + Cookie 格式错误,应为 name1=value1; name2=value2 + 已保存 %1$d 个 Cookie + 请粘贴 auth.json 内容 + auth.json 缺少 access_token 或 refresh_token + 已保存 Codex 凭据,token 到期后会自动刷新 + 解析失败:%1$s + 连接成功!余额: $%1$s + API Key 已保存 + 连接成功!Session: %1$s%% · %2$s + 凭据已删除 + 剪贴板为空 + 已识别 workspaceId 和 auth cookie + 已识别 workspaceId,请再粘贴 auth cookie + 已识别 auth cookie,请再粘贴 workspaceId + 未识别到有效凭据 + + + Codex OAuth 登录 + 正在交换 Token... + 重试 + 无头模式登录 + 1. 点击「在浏览器中打开」或复制下方链接到浏览器\n2. 在 OpenAI 页面完成登录和授权\n3. 浏览器会跳转到一个 localhost 地址(页面打不开是正常的)\n4. 复制浏览器地址栏的完整 URL 粘贴到下方输入框\n5. 点击「提交回调 URL」 + 授权链接 + 在浏览器中打开 + 复制链接 + ── 登录后粘贴回调 URL ── + 回调 URL + http://localhost:1455/auth/callback?code=... + 提交回调 URL + 重新生成授权链接 + 正在准备授权... + \ No newline at end of file diff --git a/app/src/main/res/values-b+zh+Hant/strings.xml b/app/src/main/res/values-b+zh+Hant/strings.xml new file mode 100644 index 0000000..9ed8670 --- /dev/null +++ b/app/src/main/res/values-b+zh+Hant/strings.xml @@ -0,0 +1,489 @@ + + + + AI用量面板 + OpenCode Go 用量 + DeepSeek 餘額,自動重新整理 + 雨晴喵 + 雨晴Token + + + + + 返回 + 取消 + 確認新增 + 我知道了 + 重新整理 + 設定 + 刪除 + 清除 + 儲存 + 跳轉 + ◀ 上一頁 + 下一頁 ▶ + 新增小工具到桌面 + 語言 + 選擇語言 + 跟隨系統 + 簡體中文 + 繁體中文 + English + 檢視詳情 + 檢視用量詳情 + 詳細數據 + 同步用量數據 + 我已登入,儲存 + 繼續等待 + + + 載入中… + 未知錯誤 + 全部模型 + + + 👈 選擇服務檢視詳情 + 點選左側卡片即可 + 設定 + 憑據管理 + 關於 RainyToken + 使用小技巧 + 除錯日誌 + 用量詳情 + 總統計 + 詳細數據 + 統計圖表 + + + 未配置 + 已配置 + 已過期 + 需重登 + 重新整理失敗 + 未獲取 + 待獲取 + 正常 + 未配置 · 點選新增 + 已配置 · 憑據有效 + 已過期 · 點選重新配置 + 需要重新驗證 + 手動輸入模式 + 憑據已配置 + 未配置憑據 + 載入中 + 最新 + 快取 + 失敗 + 待輸入 + + + AI 餘額一覽 + 從未獲取 + 剛剛更新 + %1$d 分鐘前更新 + %1$d 小時前更新 + %1$s 更新 + 💡 長按卡片可拖曳排序 + ⚠ %1$s + + 本地加密儲存,不上傳任何數據 + 新增桌面小工具? + 確認後會請求桌面啟動器新增雨晴Token小工具。部分系統仍會彈出系統確認視窗。 + 桌面快捷方式權限已被拒絕,請在系統設定中允許後重試 + 桌面未接受新增請求,請檢查啟動器權限 + 當前桌面不支援應用內新增,請在桌面長按 → 小工具 → 查詢雨晴Token + + + 點選配置憑據 + 下拉重新整理 + 服務當前不可用 + 本月已用 %1$s%2$s + 剩餘 + · 已用 %1$s / 共 %2$s + %1$s 已用 + 5h 已用 + · %1$s + %1$s 後重置 + REST API · ¥ + WebView 抓取 · 5h 配額 + JSON API · $ + ChatGPT Plus · Codex 額度 + Cookie 抓取 · Cloud 配額 + + + 天 + 小時 + 分 + + + 5 小時 + 5h + 5h 用量 + 5 小時滾動 + 本週 + 本月 + 每月 + 用量 + 用量視窗%1$s + 用量視窗 + + + OpenCode 用量 + CommandCode 用量 + %1$d 條記錄 + 點選同步 + 輸入 Token + 輸出 Token + 推理 Token + 總花費 + 快取讀取 %1$s + 快取寫入 %1$s + 暫無數據,點選 🔄 同步 + ✓ 新增 %1$d 條 + 點選切換 + 所有時間按 UTC+0 計算 + 已切換到 UTC+8,圖表按北京時間顯示 + 已切換到 UTC+0,圖表按世界協調時顯示 + 已用 $%1$s / 共 $%2$s + 剩餘 $%1$s + 額外充值 + 按模型統計 + 按天統計(UTC+0) + %1$d 個模型 + %1$d 個 + 開始時間 + 結束時間 + + + 消耗金額 (USD) + API 請求次數 + Token 消耗 + 輸出 + 輸入(未命中) + 命中快取 + + + 時間 + 模型 + 輸入(快取) + 輸出 + 成本 + + + 全部 + 最近5小時 + 最近12小時 + 最近24小時 + 今天 + 昨天 + 最近7天 + 最近30天 + 當月 + 自定義 + 自定義天 + 自定義月 + 自定義時間 + + + 統一查詢 AI 餘額的小工具。\n本地加密儲存憑據,不上傳任何數據。 + 檢視全部 %1$d 條隱藏操作 + 檢視 token 重新整理、網路請求等除錯記錄 + + + ✓ 已配置登入態(%1$d 個 Cookie) + 如何獲取 Cookie? + 1. 在桌面 Chrome 開啟下方登入入口 + 2. 用 GitHub / Google 登入(不會被攔截) + 3. 登入後按 F12 → Application → Cookies + 4. 複製該站點下的所有 Cookie 貼上到上面輸入框 + 格式:name1=value1; name2=value2 + 如何獲取 OpenCode Go 憑據? + 1. 桌面 Chrome 開啟 opencode.ai/auth + 2. 用 GitHub / Google 登入(Google 不會攔截 Chrome) + 3. 登入後跳轉到 dashboard,URL 形如: + https://opencode.ai/workspace/{id}/go + 4. 複製 URL 中的 {id} 段 → workspaceId + 5. F12 → Application → Cookies + 6. 找到域名 opencode.ai 下名為 auth 的 Cookie + 7. 複製它的 Value 欄位 → auth cookie + 注意:只填 auth 一個 Cookie 即可,不要整個 Cookie 字串 + 如何獲取 CommandCode Go 憑據? + 1. 開啟 https://commandcode.ai 並登入 GitHub + ▸ 獲取 API Key(必填): + 右上角頭像 → Settings → API Keys → 建立一個 Key + ▸ 獲取全部 Cookie(用於用量記錄): + F12 → Application → Cookies → commandcode.ai + 選中所有 Cookie,複製整個 Cookie 字串 + 貼上到下方輸入框即可,無需逐個挑選 + 提示:不填 Cookie 也能正常檢視餘額,只是用量記錄為空。 + 如何獲取 Codex auth.json? + 1. 在終端執行 codex-oauth-proxy 或 + codex-proxy,完成 ChatGPT 登入 + 2. 憑證檔案儲存在: + + + 已儲存 + 已儲存憑據 + 連線成功!餘額: %1$s %2$s + 連線成功,憑據已儲存 + API Key 不能為空 + JSON 格式錯誤 + 服務拒絕該憑據 (401/403) + 測試期間憑據已變更 + 請求過於頻繁 (429) + 服務端錯誤 (%1$d) + 網路錯誤:%1$s + ,已恢復原憑據 + ,檢測到憑據已變化,未執行回滾 + 測試失敗。%1$s%2$s + 暫不支援測試此服務 + auth cookie 和 workspaceId 都需要填寫 + Cookie 不能為空 + 憑據未配置 + 憑據未配置或已過期 + 憑據無效,請在設定中重新配置 + 憑據無效,請重新登入 + 憑據已變更,正在重新載入 + 憑據已變更,請重新操作 + 網路異常,請檢查網路 + 網路異常 + 服務端異常 (HTTP %1$d) + 請求過於頻繁%1$s + ,請 %1$d 秒後重試 + 不支援啟用用量 + 請先選擇模型 + 未抓到 Cookie,請確認已登入 + 未配置登入 URL + URL 應以 %1$s 開頭 + 授權失敗: %1$s + State 不匹配,請重試 + 回撥缺少授權碼 + Token 交換失敗,請檢視除錯日誌 + + + ✓ 用量已啟用 + ✓ 啟用成功 + 請求結果 + 錯誤: %1$s + 響應內容:\n%1$s + 模型: %1$s + 響應 ID: %1$s + 回覆: + (空) + (無回覆內容) + (響應解析失敗,但請求已發出) + 用量: prompt=%1$s completion=%2$s total=%3$s tokens + 用量: input=%1$s output=%2$s tokens + token 重新整理失敗:%1$s + + + 登入 %1$s + 完成登入了嗎? + 如果你已經在頁面上完成登入但頁面沒有跳轉,可點選下方按鈕讓我抓取 Cookie 並儲存登入態。 + + + 暫無日誌記錄\n重新整理 Codex 用量後這裡會出現除錯資訊 + + + OpenCode Go · 用量 + DeepSeek 餘額 + 自動重新整理 + %1$s · 額度 + %1$s · %2$s + 更新 %1$s + 暫無數據 + 重新整理中… + + + 長按卡片拖曳排序 + 長按首頁卡片可以拖曳排序 + 在首頁長按任意服務卡片即可拖曳到想要的位置,排序會自動儲存。 + 小工具點左上角進 APP + 小工具左上角標識可開啟 APP + 桌面小工具左上角 RainyToken › 標識可以點選開啟應用,其它區域點選切換服務。 + 圖表點選檢視詳情 + 點選圖表柱子可檢視時段詳情 + 在用量圖表上點選或滑動手指,可檢視對應時段的詳細數值。 + 圖表標題可點選 + 點選圖表標題彈出按模型明細 + 圖表卡片標題(如「消耗金額」)點選後會彈出按模型拆分的明細。 + 切換圖表時區 + 用量詳情頁可切換 UTC+0 / UTC+8 + 用量詳情頁頂部可切換 UTC+0 / UTC+8,圖表標籤會跟隨切換。 + CCGO 清除並重新同步 + CCGO 詳情頁可清除快取重新同步 + CCGO 用量詳情頁右上角「清除」可刪除本地快取並重新拉取全部數據。 + 檢視用量詳情 + 服務卡片底部可進入用量圖表 + 有憑據的服務卡片底部會出現「檢視用量詳情」入口,點選進入圖表統計。 + 原始數據瀏覽 + 用量詳情頁右上角可檢視原始記錄 + 用量詳情頁右上角「詳細數據」可檢視每條原始呼叫記錄,支援分頁跳轉。 + 下拉重新整理聯動用量 + 下拉重新整理會同步餘額和用量 + 首頁下拉重新整理不僅重新整理餘額,還會自動同步 OCGO / CCGO 用量記錄。 + 憑據獲取教程 + 憑據頁底部有獲取教程 + 憑據編輯頁底部的「如何獲取」按鈕有針對每個服務的詳細操作步驟。 + 剪貼簿自動匯入 + Codex 支援從剪貼簿一鍵匯入 + Codex 憑據頁支援「從剪貼簿自動匯入」,貼上 auth.json 後一鍵填入。 + 詳細數據頁碼跳轉 + 原始數據頁可輸入頁碼直跳 + 原始數據頁底部可輸入頁碼直接跳轉,數據量大時不必逐頁翻。 + 小工具自動重新整理 + 劃到小工具會自動重新整理餘額 + 桌面小工具劃到負一屏時會自動重新整理餘額,也可手動點 ↻ 按鈕重新整理。 + + + %1$d次 + 選擇日期 + 選擇月份(任意一天) + 開始日期 + 結束日期 + 消費明細 + 呼叫次數明細 + Token 明細 + 清除數據 + 此操作將清除所有 CommandCode 用量本地快取並重新同步,需要幾秒鐘完成。 + 是否繼續? + 確認(%1$ds) + 確認清除 + 關閉 + 從 %1$s + 至 %1$s + 應用自定義範圍 + 確定 + 結束日期不能早於開始日期 %1$s + 時間 %1$s:%2$s + 暫無數據 + 合計: %1$s + 總計 + 記錄數 + 快取讀取 + 快取寫入 + 快取 Token 細分 + ※ 快取讀取已計入「輸入 Token」 + %1$d 次呼叫 + MM月dd日 EEEE + %1$d 次 · $%2$s + 原始數據 + 快取寫入(5m) + 快取寫入(1h) + 成本(USD) + 成本(Raw) + + + ~/.config/codex-oauth-proxy/auth.json + 3. 用文字編輯器開啟該檔案, + 複製全部內容貼上到上方輸入框 + JSON 格式示例: + {"tokens": { + "access_token": "...", + "refresh_token": "...", + "expiresAt": 1783727108252 + }} + 提示:匯入後 APP 會自動重新整理 token, + 不需要手動更新。 + Ollama Pro 憑據 + 需要從瀏覽器複製完整 Cookie。登入 ollama.com/settings 後,在 DevTools → Network → 請求頭裡找到 Cookie 行複製。 + 更新憑據 + 儲存憑據 + 測試並儲存 + 在瀏覽器中開啟 Ollama Settings + 複製登入 URL 到剪貼簿 + 一鍵啟用用量 API Key(可選) + 填入 Ollama Cloud API Key 後,可在詳情頁一鍵傳送請求啟用用量統計。從 ollama.com/settings → API Keys 獲取。 + ollama-xxx 或 sk-xxx + 儲存 API Key + OpenCode Go 憑據 + 需要 auth cookie + workspaceId 兩個值。APP 會用 OkHttp 抓 dashboard 並自動解析 5h/周/月 三檔用量。 + 從剪貼簿自動匯入 + dashboard URL 中的 {id} 段 + auth Cookie 值 + F12 → Cookies → opencode.ai → auth 的 Value + 如何獲取這兩個值? + 在瀏覽器中開啟登入入口 + 填入 OpenCode Zen API Key 後,可在詳情頁一鍵傳送請求啟用用量統計。從 opencode.ai/settings → API Keys 獲取。 + CommandCode Go 憑據 + 需要 API Key(拉餘額)。Session Cookie(可選)在瀏覽器 DevTools 複製,用於拉用量記錄。不填也可正常檢視餘額。 + 從 Settings → API Keys 生成 + Session Cookie(可選) + F12 → Cookies → commandcode.ai → 全選複製貼上即可 + 如何獲取 API Key 和 Cookie? + 請輸入 API Key + 憑據會使用 Android Keystore (AES-256 GCM) 加密後儲存到本機 DataStore。 + 更新 + Codex / ChatGPT Plus 憑據 + 推薦使用 OAuth 登入自動獲取憑據。也可手動貼上 auth.json 內容。 + 🔐 OAuth 登入(推薦) + ── 或手動匯入 ── + auth.json 完整內容 + 貼上 {"tokens": {...}} 完整 JSON + 匯入並儲存 + 如何獲取 auth.json? + 貼上登入態 + Google OAuth 會拒絕 APP 內建 WebView,所以請用瀏覽器登入後把 Cookie 粘到這裡。 + Cookie 字串 + 儲存 Cookie + + + 數據解析失敗: %1$s + 每週 + 模型呼叫次數 + 快取數據 · %1$s + 顯示的是上次成功獲取的餘額 + 本月消費 %1$s%2$s + 餘額構成 + 贈送餘額 + 自費充值 + 載入模型列表... + 選擇模型 + 重新整理模型列表 + 啟用中... + 重試啟用用量 + ⚡ 一鍵啟用用量 + 重新整理餘額 + 重新登入 + 配置憑據 + 當前餘額 + 5h 實時用量 + 月度餘額 + Session 用量 + 請選擇模型 + 錯誤詳情: + API 響應內容: + 複製 + *** (長度 %1$d) *** + %1$s***%2$s (長度 %3$d) + Cookie 格式錯誤,應為 name1=value1; name2=value2 + 已儲存 %1$d 個 Cookie + 請貼上 auth.json 內容 + auth.json 缺少 access_token 或 refresh_token + 已儲存 Codex 憑據,token 到期後會自動重新整理 + 解析失敗:%1$s + 連線成功!餘額: $%1$s + API Key 已儲存 + 連線成功!Session: %1$s%% · %2$s + 憑據已刪除 + 剪貼簿為空 + 已識別 workspaceId 和 auth cookie + 已識別 workspaceId,請再貼上 auth cookie + 已識別 auth cookie,請再貼上 workspaceId + 未識別到有效憑據 + + + Codex OAuth 登入 + 正在交換 Token... + 重試 + 無頭模式登入 + 1. 點選「在瀏覽器中開啟」或複製下方連結到瀏覽器\n2. 在 OpenAI 頁面完成登入和授權\n3. 瀏覽器會跳轉到一個 localhost 地址(頁面打不開是正常的)\n4. 複製瀏覽器位址列的完整 URL 貼上到下方輸入框\n5. 點選「提交回撥 URL」 + 授權連結 + 在瀏覽器中開啟 + 複製連結 + ── 登入後貼上回撥 URL ── + 回撥 URL + http://localhost:1455/auth/callback?code=... + 提交回撥 URL + 重新生成授權連結 + 正在準備授權... + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 81830e8..67546ab 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,14 +1,16 @@ -雨晴Token -雨晴喵 -RainyToken -RainyToken · v1.0 + RainyToken + RainyToken · v1.0 - + + RainyToken + + AI Usage Panel OpenCode Go usage + DeepSeek balance, auto-refresh + Rainy Back @@ -24,6 +26,12 @@ ◀ Prev Next ▶ Add widget to home screen + Language + Select language + Follow system + 简体中文 + 繁體中文 + English View details View usage details Raw data @@ -114,8 +122,8 @@ 5h 5h usage 5-hour rolling - This week - This month + Week + Month Monthly Usage Usage window%1$s