Translate the remaining shell text at display and keep translated values out of logic
This commit is contained in:
1 parent
e236713d9f
commit
2b2302a279
17 files changed
+112
-25
No files matched your search
@@ -9,4 +9,8 @@ QtObject {
|
|||||||
function tr() {
|
function tr() {
|
||||||
return Tr.tr.apply(null, arguments)
|
return Tr.tr.apply(null, arguments)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function trMatch(text, templates) {
|
||||||
|
return Tr.trMatch(text, templates)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -69,6 +69,34 @@ var table = {
|
|||||||
"Saturday": "星期六",
|
"Saturday": "星期六",
|
||||||
"%1: %2% of its %3 allowance": "%1:占%3额度的 %2%",
|
"%1: %2% of its %3 allowance": "%1:占%3额度的 %2%",
|
||||||
"Subscriptions": "订阅",
|
"Subscriptions": "订阅",
|
||||||
|
"next expires in %1": "下一次%1后过期",
|
||||||
|
"Installing the %1 CLI…": "正在安装 %1 命令行…",
|
||||||
|
"Sign in to %1 again in the browser window that opens.": "在打开的浏览器窗口中重新登录 %1。",
|
||||||
|
"Sign in to %1 in the browser window that opens.": "在打开的浏览器窗口中登录 %1。",
|
||||||
|
"Sign in again as this account in the private window that opens.": "在打开的隐私窗口中以这个账户重新登录。",
|
||||||
|
"Sign in as the account you're adding in the private window that opens.": "在打开的隐私窗口中登录要添加的账户。",
|
||||||
|
"Signed in to %1 again.": "已重新登录 %1。",
|
||||||
|
"Signed in to %1.": "已登录 %1。",
|
||||||
|
"The %1 sign-in didn't finish.": "%1 登录没有完成。",
|
||||||
|
"Added %1.": "已添加 %1。",
|
||||||
|
"Starting…": "正在开始…",
|
||||||
|
"Checking the code…": "正在核对代码…",
|
||||||
|
"Run `claude auth login` to restore authoritative usage.": "运行 `claude auth login` 恢复准确的用量。",
|
||||||
|
"Run `codex login` to authenticate.": "运行 `codex login` 完成认证。",
|
||||||
|
"Set FIREWORKS_API_KEY, run `firectl set-api-key`, or sign in to Fireworks in pi or opencode.": "设置 FIREWORKS_API_KEY,运行 `firectl set-api-key`,或在 pi 或 opencode 中登录 Fireworks。",
|
||||||
|
"Start Grok, or run `grok login`, to sign in.": "启动 Grok 或运行 `grok login` 登录。",
|
||||||
|
"Start Grok to see its limits; signing in again is not needed.": "启动 Grok 查看额度,不需要重新登录。",
|
||||||
|
"Grok's saved sign-in expired. Start Grok to refresh it.": "Grok 保存的登录已过期,启动 Grok 刷新。",
|
||||||
|
"Fireworks unavailable": "Fireworks 不可用",
|
||||||
|
"Fireworks usage scan failed": "Fireworks 用量扫描失败",
|
||||||
|
"Balance unavailable": "余额不可用",
|
||||||
|
"Claude limits unavailable": "Claude 额度不可用",
|
||||||
|
"Codex limits unavailable": "Codex 额度不可用",
|
||||||
|
"Codex unavailable": "Codex 不可用",
|
||||||
|
"Grok limits unavailable": "Grok 额度不可用",
|
||||||
|
"Limits paused": "额度已暂停",
|
||||||
|
"Sign-in expired": "登录已过期",
|
||||||
|
"Waiting for auth": "等待认证",
|
||||||
// Network panel
|
// Network panel
|
||||||
"Wiring bits": "接线中",
|
"Wiring bits": "接线中",
|
||||||
"Handling packets": "处理数据包",
|
"Handling packets": "处理数据包",
|
||||||
@@ -152,6 +180,7 @@ var table = {
|
|||||||
"Lamp light": "灯光",
|
"Lamp light": "灯光",
|
||||||
"Candlelit": "烛光",
|
"Candlelit": "烛光",
|
||||||
"Night owl": "夜猫子",
|
"Night owl": "夜猫子",
|
||||||
|
"focused": "当前",
|
||||||
// Audio panel
|
// Audio panel
|
||||||
"Audio": "音频",
|
"Audio": "音频",
|
||||||
"OUTPUT": "输出",
|
"OUTPUT": "输出",
|
||||||
@@ -225,6 +254,9 @@ var table = {
|
|||||||
"Battery state": "电池状态",
|
"Battery state": "电池状态",
|
||||||
"Discharging": "正在放电",
|
"Discharging": "正在放电",
|
||||||
"Holding": "保持中",
|
"Holding": "保持中",
|
||||||
|
"Performance": "性能",
|
||||||
|
"Balanced": "均衡",
|
||||||
|
"Power-saver": "省电",
|
||||||
// Tailscale panel
|
// Tailscale panel
|
||||||
"Encrypting connections": "加密连接",
|
"Encrypting connections": "加密连接",
|
||||||
"Sending secrets": "传递秘密",
|
"Sending secrets": "传递秘密",
|
||||||
@@ -449,6 +481,8 @@ var table = {
|
|||||||
"Sunset": "日落",
|
"Sunset": "日落",
|
||||||
"It's %1 here": "这里现在是 %1",
|
"It's %1 here": "这里现在是 %1",
|
||||||
"Jump to a city": "跳到一个城市",
|
"Jump to a city": "跳到一个城市",
|
||||||
|
"Add a city": "添加城市",
|
||||||
|
"Loading zones…": "正在加载时区…",
|
||||||
// Media
|
// Media
|
||||||
"Pause": "暂停",
|
"Pause": "暂停",
|
||||||
"Play": "播放"
|
"Play": "播放"
|
||||||
|
|||||||
+16
-3
@@ -5,7 +5,20 @@
|
|||||||
var chinese = Qt.locale().name.indexOf("zh_CN") === 0
|
var chinese = Qt.locale().name.indexOf("zh_CN") === 0
|
||||||
|
|
||||||
function tr(text) {
|
function tr(text) {
|
||||||
var out = chinese && Zh.table[text] !== undefined ? Zh.table[text] : text
|
var args = arguments
|
||||||
for (var i = 1; i < arguments.length; i++) out = out.split("%" + i).join(String(arguments[i]))
|
var template = chinese && Object.prototype.hasOwnProperty.call(Zh.table, text) ? Zh.table[text] : text
|
||||||
return out
|
// One pass, so an argument that itself contains %2 stays as given.
|
||||||
|
return String(template).replace(/%(\d)/g, function(match, n) {
|
||||||
|
return n > 0 && n < args.length ? String(args[n]) : match
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Text built elsewhere around a name: the first English template that matches is translated with what %1.. captured.
|
||||||
|
function trMatch(text, templates) {
|
||||||
|
for (var i = 0; i < templates.length; i++) {
|
||||||
|
var pattern = "^" + templates[i].replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/%\d/g, "(.+?)") + "$"
|
||||||
|
var match = String(text).match(new RegExp(pattern))
|
||||||
|
if (match) return tr.apply(null, [templates[i]].concat(match.slice(1)))
|
||||||
|
}
|
||||||
|
return tr(text)
|
||||||
}
|
}
|
||||||
@@ -449,12 +449,18 @@ Panel {
|
|||||||
function resetCreditsText(credits) {
|
function resetCreditsText(credits) {
|
||||||
if (!credits || !(Number(credits.available) > 0)) return ""
|
if (!credits || !(Number(credits.available) > 0)) return ""
|
||||||
var count = Number(credits.available)
|
var count = Number(credits.available)
|
||||||
var text = count + " free reset" + (count === 1 ? "" : "s")
|
var text = I18n.tr(count === 1 ? "%1 free reset" : "%1 free resets", count)
|
||||||
var expires = new Date(String(credits.nextExpiresAt || "")).getTime()
|
var expires = new Date(String(credits.nextExpiresAt || "")).getTime()
|
||||||
if (isFinite(expires) && expires > nowMs) text += " · next expires in " + formatDuration(expires - nowMs)
|
if (isFinite(expires) && expires > nowMs) text += " · " + I18n.tr("next expires in %1", formatDuration(expires - nowMs))
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// omarchy-agent-account-add reports progress in English around the agent's name.
|
||||||
|
readonly property var addMessageTemplates: [
|
||||||
|
"Installing the %1 CLI…", "Sign in to %1 again in the browser window that opens.", "Sign in to %1 in the browser window that opens.",
|
||||||
|
"Signed in to %1 again.", "Signed in to %1.", "The %1 sign-in didn't finish.", "Added %1."
|
||||||
|
]
|
||||||
|
|
||||||
function planLabel(p) {
|
function planLabel(p) {
|
||||||
var tier = String(p && p.tierLabel || "")
|
var tier = String(p && p.tierLabel || "")
|
||||||
return tier === "" ? "" : tier.charAt(0).toUpperCase() + tier.slice(1)
|
return tier === "" ? "" : tier.charAt(0).toUpperCase() + tier.slice(1)
|
||||||
@@ -491,11 +497,11 @@ Panel {
|
|||||||
|
|
||||||
function windowTitle(label) {
|
function windowTitle(label) {
|
||||||
var text = String(label || "").toLowerCase()
|
var text = String(label || "").toLowerCase()
|
||||||
if (text.indexOf("month") >= 0) return I18n.tr("Monthly")
|
if (text.indexOf("month") >= 0) return "Monthly"
|
||||||
if (windowIsLong(text)) return I18n.tr("Weekly")
|
if (windowIsLong(text)) return "Weekly"
|
||||||
if (text.indexOf("session") >= 0 || windowSpanMs(label) > 0) return I18n.tr("Session")
|
if (text.indexOf("session") >= 0 || windowSpanMs(label) > 0) return "Session"
|
||||||
var plain = String(label || "").replace(/\s*\(.*\)\s*/, "").trim()
|
var plain = String(label || "").replace(/\s*\(.*\)\s*/, "").trim()
|
||||||
return plain === "" ? I18n.tr("Limit") : plain
|
return plain === "" ? "Limit" : plain
|
||||||
}
|
}
|
||||||
|
|
||||||
// A collector that already knows which window a limit belongs to says so,
|
// A collector that already knows which window a limit belongs to says so,
|
||||||
@@ -1064,6 +1070,7 @@ Panel {
|
|||||||
Text {
|
Text {
|
||||||
visible: root.blankSlate && root.picking
|
visible: root.blankSlate && root.picking
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("Sign in to an AI coding subscription, and this panel keeps track of how much of it you have left.")
|
text: I18n.tr("Sign in to an AI coding subscription, and this panel keeps track of how much of it you have left.")
|
||||||
color: root.dim
|
color: root.dim
|
||||||
font.family: root.fontFamily
|
font.family: root.fontFamily
|
||||||
@@ -1136,6 +1143,7 @@ Panel {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("Name this account. It signs in through a private window, so your browser's current account isn't picked up.")
|
text: I18n.tr("Name this account. It signs in through a private window, so your browser's current account isn't picked up.")
|
||||||
color: root.dim
|
color: root.dim
|
||||||
font.family: root.fontFamily
|
font.family: root.fontFamily
|
||||||
@@ -1165,7 +1173,7 @@ Panel {
|
|||||||
Text {
|
Text {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
text: root.addStatus
|
text: I18n.trMatch(root.addStatus, root.addMessageTemplates)
|
||||||
color: root.foreground
|
color: root.foreground
|
||||||
font.family: root.fontFamily
|
font.family: root.fontFamily
|
||||||
font.pixelSize: Style.font.bodySmall
|
font.pixelSize: Style.font.bodySmall
|
||||||
@@ -1178,6 +1186,7 @@ Panel {
|
|||||||
spacing: Style.space(4)
|
spacing: Style.space(4)
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("Confirm this code in your browser")
|
text: I18n.tr("Confirm this code in your browser")
|
||||||
color: root.dim
|
color: root.dim
|
||||||
font.family: root.fontFamily
|
font.family: root.fontFamily
|
||||||
@@ -1202,6 +1211,7 @@ Panel {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("If the page shows a code instead of finishing, paste it here.")
|
text: I18n.tr("If the page shows a code instead of finishing, paste it here.")
|
||||||
color: root.dim
|
color: root.dim
|
||||||
font.family: root.fontFamily
|
font.family: root.fontFamily
|
||||||
@@ -1233,7 +1243,7 @@ Panel {
|
|||||||
visible: root.addStage === "done" || root.addStage === "error"
|
visible: root.addStage === "done" || root.addStage === "error"
|
||||||
width: parent.width
|
width: parent.width
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
text: root.addResult
|
text: I18n.trMatch(root.addResult, root.addMessageTemplates)
|
||||||
color: root.addStage === "error" ? root.urgent : root.foreground
|
color: root.addStage === "error" ? root.urgent : root.foreground
|
||||||
font.family: root.fontFamily
|
font.family: root.fontFamily
|
||||||
font.pixelSize: Style.font.bodySmall
|
font.pixelSize: Style.font.bodySmall
|
||||||
@@ -1340,7 +1350,7 @@ Panel {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
// Shown for the status, so a record with no help to offer says the status rather than nothing.
|
// Shown for the status, so a record with no help to offer says the status rather than nothing.
|
||||||
text: section.provider ? String(section.provider.authHelpText || section.provider.usageStatusText || "") : ""
|
text: section.provider ? I18n.tr(String(section.provider.authHelpText || section.provider.usageStatusText || "")) : ""
|
||||||
color: root.urgent
|
color: root.urgent
|
||||||
font.family: root.fontFamily
|
font.family: root.fontFamily
|
||||||
font.pixelSize: Style.font.caption
|
font.pixelSize: Style.font.caption
|
||||||
@@ -1809,6 +1819,7 @@ Panel {
|
|||||||
id: headActive
|
id: headActive
|
||||||
visible: head.isActive
|
visible: head.isActive
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("ACTIVE")
|
text: I18n.tr("ACTIVE")
|
||||||
color: Color.accent
|
color: Color.accent
|
||||||
font.family: root.fontFamily
|
font.family: root.fontFamily
|
||||||
@@ -1857,6 +1868,7 @@ Panel {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: unavailableLabel
|
id: unavailableLabel
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("Usage unavailable")
|
text: I18n.tr("Usage unavailable")
|
||||||
color: root.dim
|
color: root.dim
|
||||||
font.family: root.fontFamily
|
font.family: root.fontFamily
|
||||||
@@ -1867,7 +1879,7 @@ Panel {
|
|||||||
|
|
||||||
PanelToolTip {
|
PanelToolTip {
|
||||||
visible: unavailableHover.hovered && unavailable.visible
|
visible: unavailableHover.hovered && unavailable.visible
|
||||||
text: unavailable.record ? String(unavailable.record.authHelpText || I18n.tr("Usage has not been updated yet.")) : ""
|
text: unavailable.record ? I18n.tr(String(unavailable.record.authHelpText || "Usage has not been updated yet.")) : ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1892,7 +1904,7 @@ Panel {
|
|||||||
text: {
|
text: {
|
||||||
var lines = []
|
var lines = []
|
||||||
if (compact.window) {
|
if (compact.window) {
|
||||||
lines.push(I18n.tr("%1: %2% used", compact.window.title, Math.round(compact.window.percent * 100))
|
lines.push(I18n.tr("%1: %2% used", I18n.tr(compact.window.title), Math.round(compact.window.percent * 100))
|
||||||
+ (compact.resetMs > 0 ? " · " + I18n.tr("resets in %1", root.formatDuration(compact.resetMs)) : ""))
|
+ (compact.resetMs > 0 ? " · " + I18n.tr("resets in %1", root.formatDuration(compact.resetMs)) : ""))
|
||||||
}
|
}
|
||||||
if (compact.stale)
|
if (compact.stale)
|
||||||
@@ -1900,8 +1912,8 @@ Panel {
|
|||||||
? I18n.tr("Last updated %1 ago", root.formatDuration(root.nowMs - compact.fetchedAt))
|
? I18n.tr("Last updated %1 ago", root.formatDuration(root.nowMs - compact.fetchedAt))
|
||||||
: compact.fetchedAt > 0 ? I18n.tr("Last updated less than a minute ago") : I18n.tr("Last updated time unavailable"))
|
: compact.fetchedAt > 0 ? I18n.tr("Last updated less than a minute ago") : I18n.tr("Last updated time unavailable"))
|
||||||
for (var i = 0; i < compact.scoped.length; i++)
|
for (var i = 0; i < compact.scoped.length; i++)
|
||||||
lines.push(I18n.tr("%1: %2% of its %3 allowance", compact.scoped[i].title, Math.round(compact.scoped[i].percent * 100),
|
lines.push(I18n.tr("%1: %2% of its %3 allowance", I18n.tr(compact.scoped[i].title), Math.round(compact.scoped[i].percent * 100),
|
||||||
String(compact.window ? compact.window.title : "").toLowerCase()))
|
I18n.tr(String(compact.window ? compact.window.title : "")).toLowerCase()))
|
||||||
return lines.join("\n")
|
return lines.join("\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1911,7 +1923,7 @@ Panel {
|
|||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
width: parent.width * 0.3
|
width: parent.width * 0.3
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
text: compact.window ? compact.window.title : ""
|
text: compact.window ? I18n.tr(compact.window.title) : ""
|
||||||
color: root.foreground
|
color: root.foreground
|
||||||
font.family: root.fontFamily
|
font.family: root.fontFamily
|
||||||
font.pixelSize: Style.font.bodySmall
|
font.pixelSize: Style.font.bodySmall
|
||||||
|
|||||||
@@ -410,6 +410,7 @@ BarWidget {
|
|||||||
spacing: Style.space(8)
|
spacing: Style.space(8)
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("Tray icons")
|
text: I18n.tr("Tray icons")
|
||||||
color: root.foreground
|
color: root.foreground
|
||||||
font.family: root.fontFamily
|
font.family: root.fontFamily
|
||||||
@@ -418,6 +419,7 @@ BarWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("Pinned icons stay visible. Hidden icons never show.")
|
text: I18n.tr("Pinned icons stay visible. Hidden icons never show.")
|
||||||
color: Qt.darker(root.foreground, 1.4)
|
color: Qt.darker(root.foreground, 1.4)
|
||||||
font.family: root.fontFamily
|
font.family: root.fontFamily
|
||||||
@@ -428,6 +430,7 @@ BarWidget {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
visible: root.allItems.length === 0
|
visible: root.allItems.length === 0
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("No tray items reporting.")
|
text: I18n.tr("No tray items reporting.")
|
||||||
color: Qt.darker(root.foreground, 1.5)
|
color: Qt.darker(root.foreground, 1.5)
|
||||||
font.family: root.fontFamily
|
font.family: root.fontFamily
|
||||||
|
|||||||
@@ -768,6 +768,7 @@ Panel {
|
|||||||
spacing: Style.space(2)
|
spacing: Style.space(2)
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("Audio")
|
text: I18n.tr("Audio")
|
||||||
color: root.bar.foreground
|
color: root.bar.foreground
|
||||||
font.family: root.bar.fontFamily
|
font.family: root.bar.fontFamily
|
||||||
|
|||||||
@@ -738,6 +738,7 @@ Panel {
|
|||||||
spacing: Style.space(2)
|
spacing: Style.space(2)
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("Bluetooth")
|
text: I18n.tr("Bluetooth")
|
||||||
color: root.bar.foreground
|
color: root.bar.foreground
|
||||||
font.family: root.bar.fontFamily
|
font.family: root.bar.fontFamily
|
||||||
|
|||||||
@@ -369,6 +369,7 @@ Panel {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("BORN")
|
text: I18n.tr("BORN")
|
||||||
color: Qt.darker(root.contentForeground, 1.5)
|
color: Qt.darker(root.contentForeground, 1.5)
|
||||||
font.family: root.contentFontFamily
|
font.family: root.contentFontFamily
|
||||||
@@ -392,6 +393,7 @@ Panel {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.verticalCenterOffset: 0
|
anchors.verticalCenterOffset: 0
|
||||||
leftPadding: Style.space(6)
|
leftPadding: Style.space(6)
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("LIVE TO")
|
text: I18n.tr("LIVE TO")
|
||||||
color: Qt.darker(root.contentForeground, 1.5)
|
color: Qt.darker(root.contentForeground, 1.5)
|
||||||
font.family: root.contentFontFamily
|
font.family: root.contentFontFamily
|
||||||
@@ -479,6 +481,7 @@ Panel {
|
|||||||
id: lifeLabel
|
id: lifeLabel
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("LIFE")
|
text: I18n.tr("LIFE")
|
||||||
color: Qt.darker(root.contentForeground, 1.5)
|
color: Qt.darker(root.contentForeground, 1.5)
|
||||||
font.family: root.contentFontFamily
|
font.family: root.contentFontFamily
|
||||||
|
|||||||
@@ -327,6 +327,7 @@ Panel {
|
|||||||
Text {
|
Text {
|
||||||
visible: dropbox.files.length === 0
|
visible: dropbox.files.length === 0
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("No synced files found.")
|
text: I18n.tr("No synced files found.")
|
||||||
color: root.dim
|
color: root.dim
|
||||||
font.family: root.fontFamily
|
font.family: root.fontFamily
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ Column {
|
|||||||
// Zone id -> offset text; read inside a binding, so its dependencies track.
|
// Zone id -> offset text; read inside a binding, so its dependencies track.
|
||||||
property var offsetLabel: function(zoneId) { return "" }
|
property var offsetLabel: function(zoneId) { return "" }
|
||||||
property bool loading: false
|
property bool loading: false
|
||||||
property string buttonText: "+ Add a city"
|
property string buttonText: "+ " + I18n.tr("Add a city")
|
||||||
property string placeholderText: I18n.tr("Search cities…")
|
property string placeholderText: I18n.tr("Search cities…")
|
||||||
property string loadingText: "Loading zones\u2026"
|
property string loadingText: I18n.tr("Loading zones…")
|
||||||
property real fontSize: Style.font.bodySmall
|
property real fontSize: Style.font.bodySmall
|
||||||
property color foreground: Color.foreground
|
property color foreground: Color.foreground
|
||||||
property color dim: Qt.darker(foreground, 1.55)
|
property color dim: Qt.darker(foreground, 1.55)
|
||||||
|
|||||||
@@ -555,6 +555,7 @@ Panel {
|
|||||||
spacing: Style.space(2)
|
spacing: Style.space(2)
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("Display")
|
text: I18n.tr("Display")
|
||||||
color: root.bar.foreground
|
color: root.bar.foreground
|
||||||
font.family: root.bar.fontFamily
|
font.family: root.bar.fontFamily
|
||||||
@@ -899,7 +900,7 @@ Panel {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
text: monitorRow.display.name + (monitorRow.display.focused ? " · focused" : "")
|
text: monitorRow.display.name + (monitorRow.display.focused ? " · " + I18n.tr("focused") : "")
|
||||||
color: root.bar.foreground
|
color: root.bar.foreground
|
||||||
font.family: root.bar.fontFamily
|
font.family: root.bar.fontFamily
|
||||||
font.pixelSize: Style.font.body
|
font.pixelSize: Style.font.body
|
||||||
|
|||||||
@@ -346,6 +346,7 @@ Panel {
|
|||||||
spacing: Style.space(2)
|
spacing: Style.space(2)
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("Battery")
|
text: I18n.tr("Battery")
|
||||||
color: root.bar.foreground
|
color: root.bar.foreground
|
||||||
font.family: root.bar.fontFamily
|
font.family: root.bar.fontFamily
|
||||||
@@ -483,7 +484,7 @@ Panel {
|
|||||||
width: profileRow.cellWidth
|
width: profileRow.cellWidth
|
||||||
iconText: root.profileIcon(String(modelData))
|
iconText: root.profileIcon(String(modelData))
|
||||||
iconSize: Style.font.title
|
iconSize: Style.font.title
|
||||||
text: String(modelData).charAt(0).toUpperCase() + String(modelData).slice(1)
|
text: I18n.tr(String(modelData).charAt(0).toUpperCase() + String(modelData).slice(1))
|
||||||
fontSize: Style.font.bodySmall
|
fontSize: Style.font.bodySmall
|
||||||
foreground: root.bar.foreground
|
foreground: root.bar.foreground
|
||||||
fontFamily: root.bar.fontFamily
|
fontFamily: root.bar.fontFamily
|
||||||
|
|||||||
@@ -520,6 +520,7 @@ Panel {
|
|||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.margins: Style.space(12)
|
anchors.margins: Style.space(12)
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("Tailscale CLI is not installed or not on PATH.")
|
text: I18n.tr("Tailscale CLI is not installed or not on PATH.")
|
||||||
color: root.dim
|
color: root.dim
|
||||||
font.family: root.fontFamily
|
font.family: root.fontFamily
|
||||||
@@ -638,6 +639,7 @@ Panel {
|
|||||||
Text {
|
Text {
|
||||||
visible: root.filteredMullvadRegions.length === 0
|
visible: root.filteredMullvadRegions.length === 0
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("No Mullvad regions found.")
|
text: I18n.tr("No Mullvad regions found.")
|
||||||
color: root.dim
|
color: root.dim
|
||||||
font.family: root.fontFamily
|
font.family: root.fontFamily
|
||||||
@@ -684,6 +686,7 @@ Panel {
|
|||||||
Text {
|
Text {
|
||||||
visible: tailscale.installed && tailscale.active && tailscale.peers.length === 0
|
visible: tailscale.installed && tailscale.active && tailscale.peers.length === 0
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("No machines found on this tailnet.")
|
text: I18n.tr("No machines found on this tailnet.")
|
||||||
color: root.dim
|
color: root.dim
|
||||||
font.family: root.fontFamily
|
font.family: root.fontFamily
|
||||||
@@ -777,6 +780,7 @@ Panel {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("Authorize Tailscale operator")
|
text: I18n.tr("Authorize Tailscale operator")
|
||||||
color: root.foreground
|
color: root.foreground
|
||||||
font.family: root.fontFamily
|
font.family: root.fontFamily
|
||||||
@@ -786,6 +790,7 @@ Panel {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("Allow this user to operate this Tailscale profile")
|
text: I18n.tr("Allow this user to operate this Tailscale profile")
|
||||||
color: root.dim
|
color: root.dim
|
||||||
font.family: root.fontFamily
|
font.family: root.fontFamily
|
||||||
|
|||||||
@@ -308,7 +308,7 @@ Panel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function dayName(dateString) {
|
function dayName(dateString) {
|
||||||
return Model.dayName(dateString, function(date) { return Qt.formatDate(date, "dddd") })
|
return Model.dayName(dateString, function(date) { return I18n.chinese ? Qt.locale().toString(date, "dddd") : Qt.formatDate(date, "dddd") })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bare degree value (no unit letter), used in the forecast row.
|
// Bare degree value (no unit letter), used in the forecast row.
|
||||||
@@ -683,6 +683,7 @@ Panel {
|
|||||||
Column {
|
Column {
|
||||||
spacing: Style.space(5)
|
spacing: Style.space(5)
|
||||||
Text {
|
Text {
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("FEELS")
|
text: I18n.tr("FEELS")
|
||||||
color: Qt.darker(root.bar.foreground, 1.5)
|
color: Qt.darker(root.bar.foreground, 1.5)
|
||||||
font.family: root.bar.fontFamily
|
font.family: root.bar.fontFamily
|
||||||
@@ -701,6 +702,7 @@ Panel {
|
|||||||
Column {
|
Column {
|
||||||
spacing: Style.space(5)
|
spacing: Style.space(5)
|
||||||
Text {
|
Text {
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("WIND")
|
text: I18n.tr("WIND")
|
||||||
color: Qt.darker(root.bar.foreground, 1.5)
|
color: Qt.darker(root.bar.foreground, 1.5)
|
||||||
font.family: root.bar.fontFamily
|
font.family: root.bar.fontFamily
|
||||||
@@ -719,6 +721,7 @@ Panel {
|
|||||||
Column {
|
Column {
|
||||||
spacing: Style.space(5)
|
spacing: Style.space(5)
|
||||||
Text {
|
Text {
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("HUMID")
|
text: I18n.tr("HUMID")
|
||||||
color: Qt.darker(root.bar.foreground, 1.5)
|
color: Qt.darker(root.bar.foreground, 1.5)
|
||||||
font.family: root.bar.fontFamily
|
font.family: root.bar.fontFamily
|
||||||
@@ -792,6 +795,7 @@ Panel {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
visible: !root.current
|
visible: !root.current
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("Fetching forecast…")
|
text: I18n.tr("Fetching forecast…")
|
||||||
color: Qt.darker(root.bar.foreground, 1.5)
|
color: Qt.darker(root.bar.foreground, 1.5)
|
||||||
font.family: root.bar.fontFamily
|
font.family: root.bar.fontFamily
|
||||||
|
|||||||
@@ -305,6 +305,7 @@ Item {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
visible: root.loading
|
visible: root.loading
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("Generating QR code…")
|
text: I18n.tr("Generating QR code…")
|
||||||
color: root.onScrimDim
|
color: root.onScrimDim
|
||||||
font.family: root.fontFamily
|
font.family: root.fontFamily
|
||||||
@@ -328,6 +329,7 @@ Item {
|
|||||||
|
|
||||||
Text {
|
Text {
|
||||||
visible: root.showingQr
|
visible: root.showingQr
|
||||||
|
textFormat: Text.PlainText
|
||||||
text: I18n.tr("Scan to join this network")
|
text: I18n.tr("Scan to join this network")
|
||||||
color: root.onScrimDim
|
color: root.onScrimDim
|
||||||
font.family: root.fontFamily
|
font.family: root.fontFamily
|
||||||
|
|||||||
@@ -320,10 +320,12 @@ pass "Claude collector caches a successful probe"
|
|||||||
# The panel reads a window out of a label, and that guess cannot survive a
|
# The panel reads a window out of a label, and that guess cannot survive a
|
||||||
# model name — "Opus 5 (1M context)" parses as a one-minute window. A collector
|
# model name — "Opus 5 (1M context)" parses as a one-minute window. A collector
|
||||||
# that states the title outright is taken at its word.
|
# that states the title outright is taken at its word.
|
||||||
run_node_test <<'JS'
|
run_node_test <<'JS' || exit 1
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const source = fs.readFileSync(root + '/shell/plugins/agents/Panel.qml', 'utf8')
|
const source = fs.readFileSync(root + '/shell/plugins/agents/Panel.qml', 'utf8')
|
||||||
const panelRoot = { nowMs: Date.now() }
|
const panelRoot = { nowMs: Date.now() }
|
||||||
|
// The panel's text goes through I18n; English here, with its %1 arguments filled.
|
||||||
|
const I18n = { tr: (text, ...args) => text.replace(/%(\d)/g, (m, n) => (n > 0 && n <= args.length ? String(args[n - 1]) : m)) }
|
||||||
const start = source.indexOf('function windowIsLong')
|
const start = source.indexOf('function windowIsLong')
|
||||||
const end = source.indexOf('// The window that decides')
|
const end = source.indexOf('// The window that decides')
|
||||||
assert(start > 0 && end > start, 'agents panel exposes its limit-window helpers')
|
assert(start > 0 && end > start, 'agents panel exposes its limit-window helpers')
|
||||||
|
|||||||
@@ -27,11 +27,11 @@ assert(/target\.kind === "signin"\) signInAgain\(/.test(panelSource) && /target\
|
|||||||
assert(/target\.kind === "autoswitch"\) setSwitchMode\(/.test(panelSource), 'Enter on Autoswitch flips the switch mode')
|
assert(/target\.kind === "autoswitch"\) setSwitchMode\(/.test(panelSource), 'Enter on Autoswitch flips the switch mode')
|
||||||
assert(/keyColumn = use >= 0 \? use : /.test(panelSource), 'moving up or down onto an account lands on Use')
|
assert(/keyColumn = use >= 0 \? use : /.test(panelSource), 'moving up or down onto an account lands on Use')
|
||||||
assert(/Qt\.callLater\(function\(\) \{ if \(picking\) pointAt\("choice", 0\) \}\)/.test(panelSource), 'picking an agent to add starts with the first one focused')
|
assert(/Qt\.callLater\(function\(\) \{ if \(picking\) pointAt\("choice", 0\) \}\)/.test(panelSource), 'picking an agent to add starts with the first one focused')
|
||||||
assert(!/opacity: stale \?/.test(panelSource) && /"Last updated " \+ root\.formatDuration/.test(panelSource), 'stale limits say how old they are only on hover')
|
assert(!/opacity: stale \?/.test(panelSource) && /I18n\.tr\("Last updated %1 ago", root\.formatDuration/.test(panelSource), 'stale limits say how old they are only on hover')
|
||||||
assert(/onPickingChanged: resetKeys\(\)/.test(panelSource), 'the cursor starts over when the agent list comes or goes')
|
assert(/onPickingChanged: resetKeys\(\)/.test(panelSource), 'the cursor starts over when the agent list comes or goes')
|
||||||
assert(!/t === "a" \|\| t === "A"/.test(panelSource), 'adding an account has no hotkey; the + is the way in')
|
assert(!/t === "a" \|\| t === "A"/.test(panelSource), 'adding an account has no hotkey; the + is the way in')
|
||||||
assert(/if \(!accounts\[a\]\.active\) \{/.test(panelSource) && /if \(row\.length > 0\) rows\.push\(row\)/.test(panelSource), 'the active account with nothing to fix is not a keyboard stop')
|
assert(/if \(!accounts\[a\]\.active\) \{/.test(panelSource) && /if \(row\.length > 0\) rows\.push\(row\)/.test(panelSource), 'the active account with nothing to fix is not a keyboard stop')
|
||||||
assert(/text: section\.provider \? String\(section\.provider\.authHelpText \|\| section\.provider\.usageStatusText \|\| ""\) : ""/.test(panelSource), 'the trouble line falls back to the usage status when a record has no auth help')
|
assert(/text: section\.provider \? I18n\.tr\(String\(section\.provider\.authHelpText \|\| section\.provider\.usageStatusText \|\| ""\)\) : ""/.test(panelSource), 'the trouble line falls back to the usage status when a record has no auth help')
|
||||||
const mainSource = fs.readFileSync(root + '/shell/plugins/agents/Main.qml', 'utf8')
|
const mainSource = fs.readFileSync(root + '/shell/plugins/agents/Main.qml', 'utf8')
|
||||||
assert(/var from = Math\.min\(0\.8, \(threshold - 15\) \/ 100\)/.test(mainSource), 'faster checks start 15 points below the switch threshold')
|
assert(/var from = Math\.min\(0\.8, \(threshold - 15\) \/ 100\)/.test(mainSource), 'faster checks start 15 points below the switch threshold')
|
||||||
assert(/rows\.push\(\[\{ kind: "provider", index: p \}\]\)/.test(panelSource), "every agent's header is a keyboard stop")
|
assert(/rows\.push\(\[\{ kind: "provider", index: p \}\]\)/.test(panelSource), "every agent's header is a keyboard stop")
|
||||||
|
|||||||
Reference in new issue
Block a user