Show the desktop shell in Chinese under zh_CN
This commit is contained in:
1 parent
e590f22de6
commit
e236713d9f
55 files changed
+867
-331
No files matched your search
@@ -103,6 +103,7 @@ Exceptions are allowed for migration and package-helper scripts where the helper
|
||||
- `default/region/china/pacman.sed` puts a `.omarchy.cn` twin ahead of every `.omarchy.org` server; `omarchy-refresh-pacman`, `install/post-install/pacman.sh` and `omarchy-region` apply it. Nothing in `omarchy update` or migrations may change the region or `/etc/locale.conf`, and a migration that edits an `.omarchy.org` `Server` line must edit its `.cn` twin the same way.
|
||||
- China-only menu rows carry `"when":"[[ \"$(omarchy-region)\" == \"China\" ]]"` on every row, not only the submenu, because search checks each row's own guard.
|
||||
- Chinese menu labels live in `default/omarchy/omarchy-menu.zh-cn.jsonc`, merged when `LANG` starts with `zh_CN`; give a new menu row with translatable words a label there.
|
||||
- Shell text goes through `I18n.tr("English %1", arg)` from `qs.Commons`, with its Chinese in `shell/Commons/I18nZh.js` keyed by the English; notifications and OSD messages are translated where the shell shows them, so bash senders pass English. `test/shell.d/shell-i18n-test.sh` fails on a missing entry.
|
||||
- `rime-ice-installer` is built from `omarchy-pkgs` with `omarchy-offline.patch` until it ships in the Omarchy repo.
|
||||
|
||||
# Config Structure
|
||||
|
||||
@@ -24,6 +24,10 @@ Run `omarchy-restart-shell` after making changes to QML files.
|
||||
- Panel / overlay / menu plugins must expose `open(payloadJson)` and
|
||||
`close()` lifecycle methods for `shell summon` and `shell hide`.
|
||||
|
||||
## Text
|
||||
|
||||
Wrap every user-visible string in `I18n.tr()` (`import qs.Commons`) and add its Chinese to `shell/Commons/I18nZh.js`; plain JS models that Node tests load stay English, and their text is wrapped where QML shows it.
|
||||
|
||||
## IPC
|
||||
|
||||
- `bin/omarchy-shell` is the canonical IPC entry point. It forwards to
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
pragma Singleton
|
||||
import QtQuick
|
||||
import "Tr.js" as Tr
|
||||
|
||||
// tr("English %1", arg) returns the Chinese entry under zh_CN; plain JS imports Tr.js for the same.
|
||||
QtObject {
|
||||
readonly property bool chinese: Tr.chinese
|
||||
|
||||
function tr() {
|
||||
return Tr.tr.apply(null, arguments)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,455 @@
|
||||
// Chinese for the shell, keyed by the English text each I18n.tr() call passes; %1, %2 are arguments.
|
||||
.pragma library
|
||||
|
||||
var table = {
|
||||
// Agents panel
|
||||
"Agents": "AI 代理",
|
||||
"Monthly": "每月",
|
||||
"Weekly": "每周",
|
||||
"Session": "会话",
|
||||
"Limit": "额度",
|
||||
"now": "现在",
|
||||
"%1d %2h": "%1天 %2小时",
|
||||
"%1h %2m": "%1小时 %2分",
|
||||
"%1m": "%1分钟",
|
||||
"%1 free reset": "%1 次免费重置",
|
||||
"%1 free resets": "%1 次免费重置",
|
||||
"%1 spent of %2 funded": "已用 %1,共充值 %2",
|
||||
"estimated": "估算",
|
||||
"%1 tokens this week": "本周 %1 tokens",
|
||||
"%1 tokens today": "今天 %1 tokens",
|
||||
"Mostly %1": "主要使用 %1",
|
||||
"Busiest day: %1": "最忙的一天:%1",
|
||||
"%1 prompt today": "今天 %1 条提示",
|
||||
"%1 prompts today": "今天 %1 条提示",
|
||||
"%1 session today": "今天 %1 个会话",
|
||||
"%1 sessions today": "今天 %1 个会话",
|
||||
"Merged from %1 device": "合并自 %1 台设备",
|
||||
"Merged from %1 devices": "合并自 %1 台设备",
|
||||
"Add an account": "添加账户",
|
||||
"Add an %1 account": "添加 %1 账户",
|
||||
"Add a %1 account": "添加 %1 账户",
|
||||
"The sign-in didn't finish.": "登录没有完成。",
|
||||
"Back to the limits": "返回额度",
|
||||
"Add a subscription": "添加订阅",
|
||||
"Start the default agent": "启动默认 AI 代理",
|
||||
"MAKE SOMETHING COOL": "做点有意思的",
|
||||
"Sign in to an AI coding subscription, and this panel keeps track of how much of it you have left.": "登录一个 AI 编程订阅,这个面板会记录剩余额度。",
|
||||
"Name this account. It signs in through a private window, so your browser's current account isn't picked up.": "给这个账户起个名字。登录在隐私窗口中进行,不会用到浏览器当前登录的账户。",
|
||||
"Work": "工作",
|
||||
"Confirm this code in your browser": "在浏览器中确认这个代码",
|
||||
"If the page shows a code instead of finishing, paste it here.": "如果页面显示代码而没有完成,把代码粘贴到这里。",
|
||||
"Code": "代码",
|
||||
"Open the sign-in page again": "重新打开登录页",
|
||||
"Sign in to %1 again": "重新登录 %1",
|
||||
"Balance": "余额",
|
||||
"Start your default agent on a new %1": "用默认 AI 代理新建一个%1",
|
||||
"Sign in to this account again": "重新登录这个账户",
|
||||
"ACTIVE": "使用中",
|
||||
"Autoswitch": "自动切换",
|
||||
"Stop switching automatically": "停止自动切换",
|
||||
"Switch here automatically at %1%": "用到 %1% 时自动切换到这里",
|
||||
"Use": "使用",
|
||||
"Usage unavailable": "用量不可用",
|
||||
"Usage has not been updated yet.": "用量还没有更新。",
|
||||
"%1: %2% used": "%1:已用 %2%",
|
||||
"resets in %1": "%1后重置",
|
||||
"Last updated %1 ago": "%1前更新",
|
||||
"Last updated less than a minute ago": "不到一分钟前更新",
|
||||
"Last updated time unavailable": "更新时间不可用",
|
||||
"Theme": "主题",
|
||||
"Plugin": "插件",
|
||||
"App": "应用",
|
||||
"Sunday": "星期日",
|
||||
"Monday": "星期一",
|
||||
"Tuesday": "星期二",
|
||||
"Wednesday": "星期三",
|
||||
"Thursday": "星期四",
|
||||
"Friday": "星期五",
|
||||
"Saturday": "星期六",
|
||||
"%1: %2% of its %3 allowance": "%1:占%3额度的 %2%",
|
||||
"Subscriptions": "订阅",
|
||||
// Network panel
|
||||
"Wiring bits": "接线中",
|
||||
"Handling packets": "处理数据包",
|
||||
"Sorting frames": "整理数据帧",
|
||||
"Hauling bytes": "搬运字节",
|
||||
"Routing crumbs": "转发碎片",
|
||||
"Counting collisions": "清点冲突",
|
||||
"Bending light": "弯折光线",
|
||||
"Turn Wi-Fi off": "关闭 Wi-Fi",
|
||||
"Turn Wi-Fi on": "打开 Wi-Fi",
|
||||
"WI-FI BAND": "WI-FI 频段",
|
||||
"WI-FI BAND: %1": "WI-FI 频段:%1",
|
||||
"Auto": "自动",
|
||||
"Let Wi-Fi pick the band": "由 Wi-Fi 自动选择频段",
|
||||
"Stay on %1": "固定在 %1",
|
||||
"Timeout": "超时",
|
||||
"KNOWN NETWORKS": "已知网络",
|
||||
"OTHER NETWORKS": "其他网络",
|
||||
"Ethernet": "有线网络",
|
||||
"Passphrase required": "需要密码",
|
||||
"Wrong password": "密码错误",
|
||||
"Network lost": "网络丢失",
|
||||
"Disconnected": "已断开",
|
||||
"Connection failed": "连接失败",
|
||||
"Failed to connect": "无法连接",
|
||||
"Timed out connecting": "连接超时",
|
||||
"Timed out disconnecting": "断开超时",
|
||||
"Timed out forgetting": "忘记网络超时",
|
||||
"Sign in to this network": "登录这个网络",
|
||||
"Limited internet access": "互联网访问受限",
|
||||
"Show QR code": "显示二维码",
|
||||
"Run a speed test": "测速",
|
||||
"No connection": "无连接",
|
||||
"SIGN-IN REQUIRED": "需要登录",
|
||||
"LIMITED INTERNET ACCESS": "互联网访问受限",
|
||||
"NOT CONNECTED": "未连接",
|
||||
"Open Captive Portal": "打开认证页面",
|
||||
"Sign in or accept this network’s terms to access the internet.": "登录或接受这个网络的条款后才能上网。",
|
||||
"AUTOMATIC": "自动",
|
||||
"DNS PROVIDER": "DNS 服务商",
|
||||
"Use DNS from DHCP": "使用 DHCP 提供的 DNS",
|
||||
"Set DNS to %1": "DNS 设为 %1",
|
||||
"Set custom DNS servers": "设置自定义 DNS 服务器",
|
||||
"Custom": "自定义",
|
||||
"SCANNING WI-FI…": "正在扫描 WI-FI…",
|
||||
"Connecting…": "正在连接…",
|
||||
"Disconnecting…": "正在断开…",
|
||||
"Forgetting…": "正在忘记…",
|
||||
"Failed": "失败",
|
||||
"Sign-in required": "需要登录",
|
||||
"Connected": "已连接",
|
||||
"Forget network": "忘记网络",
|
||||
"Hidden": "隐藏网络",
|
||||
"Identity (user@domain)": "身份(user@domain)",
|
||||
"Passphrase": "密码",
|
||||
"Connect": "连接",
|
||||
"Copy to clipboard": "复制到剪贴板",
|
||||
"Ping": "延迟",
|
||||
"Packet Loss": "丢包",
|
||||
"Receiving": "接收",
|
||||
"Sending": "发送",
|
||||
"Downloaded": "已下载",
|
||||
"Uploaded": "已上传",
|
||||
"IP Address": "IP 地址",
|
||||
"Gateway": "网关",
|
||||
"Copy IP": "复制 IP",
|
||||
"Copy gateway": "复制网关",
|
||||
"Connecting...": "正在连接…",
|
||||
// Display panel
|
||||
"Display": "显示",
|
||||
"FIXED BRIGHTNESS": "亮度固定",
|
||||
"BRIGHTNESS": "亮度",
|
||||
"TEXT SIZE": "文字大小",
|
||||
"SCALE": "缩放",
|
||||
"DISPLAYS": "显示器",
|
||||
"Sun blast": "烈日当空",
|
||||
"Solar flare": "耀眼",
|
||||
"Golden hour": "黄金时刻",
|
||||
"Even day": "白昼",
|
||||
"Soft glow": "柔光",
|
||||
"Lamp light": "灯光",
|
||||
"Candlelit": "烛光",
|
||||
"Night owl": "夜猫子",
|
||||
// Audio panel
|
||||
"Audio": "音频",
|
||||
"OUTPUT": "输出",
|
||||
"INPUT": "输入",
|
||||
"SOURCES": "音源",
|
||||
"Mute": "静音",
|
||||
"Unmute": "取消静音",
|
||||
"Muted": "已静音",
|
||||
"Silenced": "无声",
|
||||
"Concert hall": "音乐厅",
|
||||
"Party mode": "派对模式",
|
||||
"Cranked up": "开大了",
|
||||
"Steady groove": "稳稳的节奏",
|
||||
"Easy listening": "轻松聆听",
|
||||
"Murmur": "低语",
|
||||
"Whisper": "耳语",
|
||||
// Bluetooth panel
|
||||
"Untangling wires": "理顺线缆",
|
||||
"Streaming vikings": "维京人在传输",
|
||||
"Pairing mysteries": "配对谜题",
|
||||
"Herding headsets": "召集耳机",
|
||||
"Taming radios": "驯服无线电",
|
||||
"Summoning speakers": "召唤音箱",
|
||||
"Wrangling codecs": "摆弄编解码器",
|
||||
"Polishing packets": "打磨数据包",
|
||||
"Bluetooth": "蓝牙",
|
||||
"CONNECTED": "已连接",
|
||||
"Forget": "忘记",
|
||||
"No adapter": "没有适配器",
|
||||
"Turned Off": "已关闭",
|
||||
"Turn Bluetooth off": "关闭蓝牙",
|
||||
"Turn Bluetooth on": "打开蓝牙",
|
||||
"PAIRED": "已配对",
|
||||
"AVAILABLE": "可用",
|
||||
"No Bluetooth adapter": "没有蓝牙适配器",
|
||||
"Turn Bluetooth on to scan": "打开蓝牙后扫描",
|
||||
"Scanning for devices…": "正在扫描设备…",
|
||||
"Disconnect": "断开",
|
||||
"Pair": "配对",
|
||||
"Device": "设备",
|
||||
// Power panel
|
||||
"Pumping power": "注入电力",
|
||||
"Injecting electrons": "注入电子",
|
||||
"Pouring juice": "倒入电量",
|
||||
"Amassing watts": "积攒瓦特",
|
||||
"Hoarding joules": "囤积焦耳",
|
||||
"Sucking volts": "吸收伏特",
|
||||
"Topping reserves": "补满储备",
|
||||
"Soaking amps": "吸饱安培",
|
||||
"Inhaling kilowatts": "吸入千瓦",
|
||||
"Slurping power": "啜饮电力",
|
||||
"Spending joules": "花费焦耳",
|
||||
"Draining watts": "消耗瓦特",
|
||||
"Burning electrons": "燃烧电子",
|
||||
"Sipping juice": "小口用电",
|
||||
"Spending coulombs": "花费库仑",
|
||||
"Bleeding amps": "流失安培",
|
||||
"Guzzling volts": "狂饮伏特",
|
||||
"Munching reserves": "啃食储备",
|
||||
"Battery": "电池",
|
||||
"POWER PROFILE": "电源模式",
|
||||
"Fully charged": "已充满",
|
||||
"Threshold": "充电上限",
|
||||
"On battery": "使用电池",
|
||||
"Charging": "正在充电",
|
||||
"Battery size": "电池容量",
|
||||
"Charge cycles": "充电次数",
|
||||
"Charge limit": "充电上限",
|
||||
"Time left": "剩余时间",
|
||||
"Time to full": "充满还需",
|
||||
"Battery state": "电池状态",
|
||||
"Discharging": "正在放电",
|
||||
"Holding": "保持中",
|
||||
// Tailscale panel
|
||||
"Encrypting connections": "加密连接",
|
||||
"Sending secrets": "传递秘密",
|
||||
"Guarding wires": "守护线路",
|
||||
"Braiding packets": "编织数据包",
|
||||
"Polishing tunnels": "打磨隧道",
|
||||
"Hiding routes": "隐藏路由",
|
||||
"Sealing ports": "封好端口",
|
||||
"Sorting tailnets": "整理 tailnet",
|
||||
"Shuffling keys": "洗牌密钥",
|
||||
"Watching machines": "照看机器",
|
||||
"Tailscale CLI is not installed or not on PATH.": "没有安装 Tailscale 命令行,或它不在 PATH 中。",
|
||||
"CONNECTIONS": "连接",
|
||||
"EXIT NODES": "出口节点",
|
||||
"Search regions": "搜索地区",
|
||||
"No Mullvad regions found.": "没有找到 Mullvad 地区。",
|
||||
"MACHINES": "机器",
|
||||
"No machines found on this tailnet.": "这个 tailnet 上没有机器。",
|
||||
"Authorize Tailscale operator": "授权 Tailscale 操作员",
|
||||
"Allow this user to operate this Tailscale profile": "允许这个用户操作这个 Tailscale 配置",
|
||||
"Send files": "发送文件",
|
||||
"Turn Tailscale off": "关闭 Tailscale",
|
||||
"Authorize this device": "授权这台设备",
|
||||
"Turn Tailscale on": "打开 Tailscale",
|
||||
"Choose Mullvad region": "选择 Mullvad 地区",
|
||||
"Tailscale is disconnected": "Tailscale 已断开",
|
||||
"Account": "账户",
|
||||
"Starting Tailscale login…": "正在开始 Tailscale 登录…",
|
||||
"Authorizing Tailscale operator...": "正在授权 Tailscale 操作员…",
|
||||
"Tailscale login link not available yet": "Tailscale 登录链接还不可用",
|
||||
"Authorize Tailscale operator to show connections": "授权 Tailscale 操作员后显示连接",
|
||||
"Could not list Tailscale connections": "无法列出 Tailscale 连接",
|
||||
"Tailscale command failed": "Tailscale 命令失败",
|
||||
"Account switch failed": "切换账户失败",
|
||||
"Exit node selection failed": "选择出口节点失败",
|
||||
"Tailscale authorization failed": "Tailscale 授权失败",
|
||||
"Tailscale operator authorized": "Tailscale 操作员已授权",
|
||||
"Unknown": "未知",
|
||||
// Dropbox panel
|
||||
"Filing files": "归档文件",
|
||||
"Distributing data": "分发数据",
|
||||
"Shuffling folders": "整理文件夹",
|
||||
"Boxing bytes": "装箱字节",
|
||||
"Sorting stuff": "分拣东西",
|
||||
"Syncing secrets": "同步秘密",
|
||||
"Packing packets": "打包数据包",
|
||||
"Moving memories": "搬运回忆",
|
||||
"Wrangling revisions": "整理修订",
|
||||
"Cataloging chaos": "编目混乱",
|
||||
"RECENT FILES": "最近文件",
|
||||
"No synced files found.": "没有找到同步的文件。",
|
||||
"Pause syncing": "暂停同步",
|
||||
"Resume syncing": "继续同步",
|
||||
"Syncing paused": "同步已暂停",
|
||||
"Stored": "已存储",
|
||||
"Login to Dropbox": "登录 Dropbox",
|
||||
"Dropbox CLI is not installed": "没有安装 Dropbox 命令行",
|
||||
"Start the authentication flow": "开始认证",
|
||||
"Install Dropbox from the service menu": "从服务菜单安装 Dropbox",
|
||||
"Untitled": "未命名",
|
||||
// Weather panel
|
||||
"Search city": "搜索城市",
|
||||
"FEELS": "体感",
|
||||
"WIND": "风",
|
||||
"HUMID": "湿度",
|
||||
"Fetching forecast…": "正在获取天气预报…",
|
||||
// Wi-Fi QR panel
|
||||
"Generating QR code…": "正在生成二维码…",
|
||||
"Scan to join this network": "扫码加入这个网络",
|
||||
"Could not generate the Wi-Fi QR code": "无法生成 Wi-Fi 二维码",
|
||||
"Could not read the Wi-Fi password": "无法读取 Wi-Fi 密码",
|
||||
"Show password": "显示密码",
|
||||
// Speed tests
|
||||
"Speed test failed": "测速失败",
|
||||
"DOWNLOAD": "下载",
|
||||
"UPLOAD": "上传",
|
||||
"Measure again via fast.com": "通过 fast.com 重新测速",
|
||||
"Disk speed test failed": "磁盘测速失败",
|
||||
"READ": "读取",
|
||||
"WRITE": "写入",
|
||||
"Measure again": "重新测速",
|
||||
"Run Again": "再测一次",
|
||||
// Lock screen
|
||||
"Enter Password": "输入密码",
|
||||
"Checking…": "正在验证…",
|
||||
"Fingerprint reader unavailable": "指纹识别器不可用",
|
||||
"Authentication failed (%1)": "认证失败(%1)",
|
||||
// Authentication prompt
|
||||
"Authentication is needed...": "需要认证…",
|
||||
"Wrong": "错误",
|
||||
"Checking...": "正在验证…",
|
||||
"Enter password": "输入密码",
|
||||
// Reminders
|
||||
"Reminder message": "提醒内容",
|
||||
"Remind in minutes": "几分钟后提醒",
|
||||
// Notifications
|
||||
"No recent notifications": "没有最近的通知",
|
||||
// Bar
|
||||
"Microphone muted": "麦克风已静音",
|
||||
"Microphone in use": "麦克风使用中",
|
||||
"Microphone live": "麦克风已开启",
|
||||
"Pending Omarchy Updates": "有待安装的 Omarchy 更新",
|
||||
"Tray icons": "托盘图标",
|
||||
"Pinned icons stay visible. Hidden icons never show.": "固定的图标一直显示,隐藏的图标不再显示。",
|
||||
"No tray items reporting.": "没有托盘项目。",
|
||||
"Unpin": "取消固定",
|
||||
"Pin": "固定",
|
||||
"Show": "显示",
|
||||
"Hide": "隐藏",
|
||||
"Dictate": "语音输入",
|
||||
"Allow Notifications": "允许通知",
|
||||
"Silence Notifications": "静默通知",
|
||||
"Day Light": "日间光线",
|
||||
"Night Light": "夜间光线",
|
||||
"Stop recording": "停止录屏",
|
||||
"Screen Recording": "录屏",
|
||||
"Allow Idle Lock & Screensaver": "允许空闲锁屏和屏保",
|
||||
"Stay Awake": "保持唤醒",
|
||||
// Shared controls
|
||||
"Cancel": "取消",
|
||||
"Confirm": "确认",
|
||||
"Search...": "搜索…",
|
||||
"No options": "没有选项",
|
||||
"None selected": "未选择",
|
||||
"Loading…": "正在加载…",
|
||||
"No matches": "没有匹配项",
|
||||
// Notifications and on-screen messages
|
||||
"Learn Keybindings": "了解快捷键",
|
||||
"Super + K for cheatsheet.\nSuper + Space for Omarchy Menu.": "Super + K 查看快捷键速查表。\nSuper + Space 打开 Omarchy 菜单。",
|
||||
"Update System": "更新系统",
|
||||
"Click to update the system.": "点击更新系统。",
|
||||
"Setup Wi-Fi": "设置 Wi-Fi",
|
||||
"Click to configure the wireless network.": "点击配置无线网络。",
|
||||
"All reminders have been cleared": "所有提醒已清除",
|
||||
"Animations disabled": "动画已关闭",
|
||||
"Animations enabled": "动画已开启",
|
||||
"Can't disable the only active display": "不能关闭唯一的显示器",
|
||||
"Copied text from selection to clipboard": "已把选中文字复制到剪贴板",
|
||||
"Could not complete shutdown. Please try again.": "无法完成关机,请重试。",
|
||||
"Could not share": "无法分享",
|
||||
"Crash capture disabled": "崩溃捕获已关闭",
|
||||
"Crash capture enabled": "崩溃捕获已开启",
|
||||
"Disable single-window square aspect ratio": "关闭单窗口方形比例",
|
||||
"Enable single-window square aspect ratio": "开启单窗口方形比例",
|
||||
"Download failed": "下载失败",
|
||||
"Editing config file": "正在编辑配置文件",
|
||||
"Extended mode restored": "已恢复扩展模式",
|
||||
"FIDO2 authfile needs attention": "FIDO2 认证文件需要处理",
|
||||
"Failed to start Windows VM": "无法启动 Windows 虚拟机",
|
||||
"Herdr theme sync disabled": "Herdr 主题同步已关闭",
|
||||
"Herdr theme sync enabled": "Herdr 主题同步已开启",
|
||||
"Hold F9 to dictate (or toggle with Super + Ctrl + X).": "按住 F9 语音输入(或用 Super + Ctrl + X 切换)。",
|
||||
"Laptop display disabled": "笔记本屏幕已关闭",
|
||||
"Laptop display enabled": "笔记本屏幕已开启",
|
||||
"Logging out": "正在注销",
|
||||
"Microphone on": "麦克风已开启",
|
||||
"No QR code found": "没有找到二维码",
|
||||
"No RetroArch cores found": "没有找到 RetroArch 核心",
|
||||
"No audio devices found": "没有找到音频设备",
|
||||
"No background was found for theme": "这个主题没有壁纸",
|
||||
"No external monitors found for mirror": "没有可镜像的外接显示器",
|
||||
"No laptop display found": "没有找到笔记本屏幕",
|
||||
"No laptop monitor found to mirror": "没有可镜像的笔记本屏幕",
|
||||
"No outstanding reminders": "没有待办提醒",
|
||||
"No video found for download": "没有可下载的视频",
|
||||
"No webcam devices found": "没有找到摄像头",
|
||||
"QR code copied to clipboard": "二维码已复制到剪贴板",
|
||||
"Rebooting": "正在重启",
|
||||
"Recording process had to be force-killed. Video may be corrupted.": "录屏进程被强制结束,视频可能已损坏。",
|
||||
"Refusing unsafe monitor name": "拒绝不安全的显示器名称",
|
||||
"Reminder": "提醒",
|
||||
"Restore using Super + Home on this workspace.": "在这个工作区用 Super + Home 恢复。",
|
||||
"Saved and copied to clipboard.": "已保存并复制到剪贴板。",
|
||||
"Screen recording error": "录屏出错",
|
||||
"Screensaver disabled": "屏保已关闭",
|
||||
"Screensaver enabled": "屏保已开启",
|
||||
"Screensaver only runs in Alacritty, Foot, Ghostty, or Kitty": "屏保只能在 Alacritty、Foot、Ghostty 或 Kitty 中运行",
|
||||
"Select a region containing a QR code": "框选包含二维码的区域",
|
||||
"Shutdown failed": "关机失败",
|
||||
"Shutting down": "正在关机",
|
||||
"Start it with Super + Space": "用 Super + Space 启动",
|
||||
"Suspend now available in system menu": "系统菜单中已加入挂起",
|
||||
"Suspend removed from system menu": "系统菜单中已移除挂起",
|
||||
"TUI removed": "TUI 已移除",
|
||||
"The file chooser did not open": "文件选择器没有打开",
|
||||
"Theme removed": "主题已移除",
|
||||
"Time to recharge!": "该充电了!",
|
||||
"Transcoding video…": "正在转码视频…",
|
||||
"URL copied to clipboard": "链接已复制到剪贴板",
|
||||
"Upcoming reminders": "即将到来的提醒",
|
||||
"Use Super + Alt + Home to save one for this workspace.": "用 Super + Alt + Home 为这个工作区保存一个。",
|
||||
"Voxtype Dictation Ready": "Voxtype 语音输入就绪",
|
||||
"Web app removed": "网页应用已移除",
|
||||
"Windows VM": "Windows 虚拟机",
|
||||
// Clock
|
||||
"Right-click to toggle format": "右键切换格式",
|
||||
"Back to today": "回到今天",
|
||||
"BORN": "出生",
|
||||
"LIVE TO": "活到",
|
||||
"LIFE": "人生",
|
||||
"Start weeks on %1": "每周从%1开始",
|
||||
"Previous month": "上个月",
|
||||
"Next month": "下个月",
|
||||
// Menu
|
||||
"Go": "前往",
|
||||
"Input": "输入",
|
||||
"Select": "选择",
|
||||
"Do you want to uninstall %1?": "要卸载 %1 吗?",
|
||||
"Uninstall": "卸载",
|
||||
"No matches for “%1”": "没有匹配“%1”的结果",
|
||||
"Nothing here yet": "这里还没有内容",
|
||||
// World clock, emojis, clipboard
|
||||
"Search cities…": "搜索城市…",
|
||||
"World": "世界",
|
||||
"Clock": "时钟",
|
||||
"Remove": "移除",
|
||||
"World clock": "世界时钟",
|
||||
"Delete entire clipboard history?": "删除全部剪贴板历史?",
|
||||
"Delete": "删除",
|
||||
"Clipboard is empty": "剪贴板是空的",
|
||||
"Sunrise": "日出",
|
||||
"Sunset": "日落",
|
||||
"It's %1 here": "这里现在是 %1",
|
||||
"Jump to a city": "跳到一个城市",
|
||||
// Media
|
||||
"Pause": "暂停",
|
||||
"Play": "播放"
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
.pragma library
|
||||
.import "I18nZh.js" as Zh
|
||||
|
||||
// Shell text in the session's language; QML reaches this through the I18n singleton, plain JS imports it.
|
||||
var chinese = Qt.locale().name.indexOf("zh_CN") === 0
|
||||
|
||||
function tr(text) {
|
||||
var out = chinese && Zh.table[text] !== undefined ? Zh.table[text] : text
|
||||
for (var i = 1; i < arguments.length; i++) out = out.split("%" + i).join(String(arguments[i]))
|
||||
return out
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
module qs.Commons
|
||||
singleton Border 1.0 Border.qml
|
||||
singleton Color 1.0 Color.qml
|
||||
singleton I18n 1.0 I18n.qml
|
||||
singleton IpcRegistry 1.0 IpcRegistry.qml
|
||||
singleton Style 1.0 Style.qml
|
||||
singleton Util 1.0 Util.qml
|
||||
|
||||
@@ -6,8 +6,8 @@ Item {
|
||||
|
||||
property bool opened: false
|
||||
property string message: ""
|
||||
property string cancelText: "Cancel"
|
||||
property string confirmText: "Confirm"
|
||||
property string cancelText: I18n.tr("Cancel")
|
||||
property string confirmText: I18n.tr("Confirm")
|
||||
property int selectedIndex: 1
|
||||
property color background: Color.background
|
||||
property color foreground: Color.foreground
|
||||
|
||||
@@ -27,9 +27,9 @@ Item {
|
||||
property var options: []
|
||||
property var optionsCommand: []
|
||||
property string optionsCommandCwd: ""
|
||||
property string placeholderText: "Search..."
|
||||
property string emptyText: "No options"
|
||||
property string noSelectionText: "None selected"
|
||||
property string placeholderText: I18n.tr("Search...")
|
||||
property string emptyText: I18n.tr("No options")
|
||||
property string noSelectionText: I18n.tr("None selected")
|
||||
property string triggerLabel: ""
|
||||
property bool showLabel: true
|
||||
|
||||
@@ -492,7 +492,7 @@ Item {
|
||||
textFormat: Text.PlainText
|
||||
anchors.centerIn: parent
|
||||
visible: resultList.count === 0
|
||||
text: root.loadingOptions ? "Loading…" : (root.optionsError !== "" ? root.optionsError : root.emptyText)
|
||||
text: root.loadingOptions ? I18n.tr("Loading…") : (root.optionsError !== "" ? root.optionsError : root.emptyText)
|
||||
color: Qt.darker(root.foreground, 1.6)
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
|
||||
@@ -21,8 +21,8 @@ Item {
|
||||
property string label: ""
|
||||
property string value: ""
|
||||
property var options: []
|
||||
property string placeholderText: "Search..."
|
||||
property string emptyText: "No matches"
|
||||
property string placeholderText: I18n.tr("Search...")
|
||||
property string emptyText: I18n.tr("No matches")
|
||||
property string triggerLabel: ""
|
||||
|
||||
property color foreground: Color.popups.text
|
||||
|
||||
@@ -22,7 +22,7 @@ PanelWindow {
|
||||
property string unit: "Mbps"
|
||||
property string title: ""
|
||||
property string layerNamespace: "omarchy-speed-test"
|
||||
property string runAgainTooltip: "Measure again"
|
||||
property string runAgainTooltip: I18n.tr("Measure again")
|
||||
property real leftValue: 0
|
||||
property real rightValue: 0
|
||||
property bool leftLive: false
|
||||
@@ -164,7 +164,7 @@ PanelWindow {
|
||||
// Centered on the dial pair. Fades rather than unmounts while a run
|
||||
// is in flight, so the cluster never shifts.
|
||||
Button {
|
||||
text: "Run Again"
|
||||
text: I18n.tr("Run Again")
|
||||
tooltipText: root.runAgainTooltip
|
||||
bordered: true
|
||||
enabled: !root.running
|
||||
|
||||
@@ -338,9 +338,9 @@ Panel {
|
||||
|
||||
// A few ways into making Omarchy your own, handed to the default agent.
|
||||
readonly property var starterPrompts: [
|
||||
{ glyph: "", label: "Theme", prompt: "Make me a new Omarchy theme. Ask me what look or inspiration I have in mind, then build it following the Omarchy skill's theming guide and switch to it." },
|
||||
{ glyph: "", label: "Plugin", prompt: "Make me a new Omarchy shell plugin. Ask me what I'd like it to do, then build it following the Omarchy skill's plugin guide and enable it." },
|
||||
{ glyph: "", label: "App", prompt: "Make me a new app for my Omarchy desktop. Ask me what it should do, then build it following the omarchy-app skill and install it so it shows up in the app launcher." }
|
||||
{ glyph: "", label: I18n.tr("Theme"), prompt: "Make me a new Omarchy theme. Ask me what look or inspiration I have in mind, then build it following the Omarchy skill's theming guide and switch to it." },
|
||||
{ glyph: "", label: I18n.tr("Plugin"), prompt: "Make me a new Omarchy shell plugin. Ask me what I'd like it to do, then build it following the Omarchy skill's plugin guide and enable it." },
|
||||
{ glyph: "", label: I18n.tr("App"), prompt: "Make me a new app for my Omarchy desktop. Ask me what it should do, then build it following the omarchy-app skill and install it so it shows up in the app launcher." }
|
||||
]
|
||||
|
||||
function startPrompt(prompt) {
|
||||
@@ -406,7 +406,7 @@ Panel {
|
||||
var parts = []
|
||||
if (String(account.plan || "") !== "") parts.push(account.plan)
|
||||
if (account.resetCredits && Number(account.resetCredits.available) > 0)
|
||||
parts.push(account.resetCredits.available + " free reset" + (Number(account.resetCredits.available) === 1 ? "" : "s"))
|
||||
parts.push(I18n.tr(Number(account.resetCredits.available) === 1 ? "%1 free reset" : "%1 free resets", account.resetCredits.available))
|
||||
return parts.join(" · ")
|
||||
}
|
||||
|
||||
@@ -491,11 +491,11 @@ Panel {
|
||||
|
||||
function windowTitle(label) {
|
||||
var text = String(label || "").toLowerCase()
|
||||
if (text.indexOf("month") >= 0) return "Monthly"
|
||||
if (windowIsLong(text)) return "Weekly"
|
||||
if (text.indexOf("session") >= 0 || windowSpanMs(label) > 0) return "Session"
|
||||
if (text.indexOf("month") >= 0) return I18n.tr("Monthly")
|
||||
if (windowIsLong(text)) return I18n.tr("Weekly")
|
||||
if (text.indexOf("session") >= 0 || windowSpanMs(label) > 0) return I18n.tr("Session")
|
||||
var plain = String(label || "").replace(/\s*\(.*\)\s*/, "").trim()
|
||||
return plain === "" ? "Limit" : plain
|
||||
return plain === "" ? I18n.tr("Limit") : plain
|
||||
}
|
||||
|
||||
// A collector that already knows which window a limit belongs to says so,
|
||||
@@ -575,13 +575,13 @@ Panel {
|
||||
}
|
||||
|
||||
function formatDuration(ms) {
|
||||
if (!(ms > 0)) return "now"
|
||||
if (!(ms > 0)) return I18n.tr("now")
|
||||
var minutes = Math.floor(ms / 60000)
|
||||
var hours = Math.floor(minutes / 60)
|
||||
var days = Math.floor(hours / 24)
|
||||
if (days > 0) return days + "d " + (hours % 24) + "h"
|
||||
if (hours > 0) return hours + "h " + (minutes % 60) + "m"
|
||||
return Math.max(1, minutes) + "m"
|
||||
if (days > 0) return I18n.tr("%1d %2h", days, hours % 24)
|
||||
if (hours > 0) return I18n.tr("%1h %2m", hours, minutes % 60)
|
||||
return I18n.tr("%1m", Math.max(1, minutes))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- balance
|
||||
@@ -611,8 +611,8 @@ Panel {
|
||||
|
||||
function balanceDetailText(b) {
|
||||
if (!b || !(b.funded > 0)) return ""
|
||||
var text = formatMoney(b.spent, b.currency) + " spent of " + formatMoney(b.funded, b.currency) + " funded"
|
||||
if (b.estimated) text += " · estimated"
|
||||
var text = I18n.tr("%1 spent of %2 funded", formatMoney(b.spent, b.currency), formatMoney(b.funded, b.currency))
|
||||
if (b.estimated) text += " · " + I18n.tr("estimated")
|
||||
return text
|
||||
}
|
||||
|
||||
@@ -653,16 +653,16 @@ Panel {
|
||||
}
|
||||
|
||||
var phrases = []
|
||||
if (week > 0) phrases.push(usage.formatTokenCount(week) + " tokens this week")
|
||||
if (today > 0) phrases.push(usage.formatTokenCount(today) + " tokens today")
|
||||
if (week > 0) phrases.push(I18n.tr("%1 tokens this week", usage.formatTokenCount(week)))
|
||||
if (today > 0) phrases.push(I18n.tr("%1 tokens today", usage.formatTokenCount(today)))
|
||||
var topModel = ""
|
||||
for (var model in byModel) if (topModel === "" || byModel[model] > byModel[topModel]) topModel = model
|
||||
if (topModel !== "" && byModel[topModel] > 0) phrases.push("Mostly " + topModel)
|
||||
if (topModel !== "" && byModel[topModel] > 0) phrases.push(I18n.tr("Mostly %1", topModel))
|
||||
var busiest = ""
|
||||
for (var date in byDay) if (busiest === "" || byDay[date] > byDay[busiest]) busiest = date
|
||||
if (busiest !== "" && byDay[busiest] > 0) phrases.push("Busiest day: " + dayName(busiest))
|
||||
if (prompts > 0) phrases.push(prompts + " prompt" + (prompts === 1 ? "" : "s") + " today")
|
||||
if (sessions > 0) phrases.push(sessions + " session" + (sessions === 1 ? "" : "s") + " today")
|
||||
if (busiest !== "" && byDay[busiest] > 0) phrases.push(I18n.tr("Busiest day: %1", dayName(busiest)))
|
||||
if (prompts > 0) phrases.push(I18n.tr(prompts === 1 ? "%1 prompt today" : "%1 prompts today", prompts))
|
||||
if (sessions > 0) phrases.push(I18n.tr(sessions === 1 ? "%1 session today" : "%1 sessions today", sessions))
|
||||
return phrases
|
||||
}
|
||||
property int phraseIndex: 0
|
||||
@@ -672,7 +672,7 @@ Panel {
|
||||
property string shownPhrase: ""
|
||||
readonly property string heroPhrase: addStage !== "" || blankSlate
|
||||
? addHeading
|
||||
: (shownPhrase !== "" ? shownPhrase : "Subscriptions")
|
||||
: (shownPhrase !== "" ? shownPhrase : I18n.tr("Subscriptions"))
|
||||
|
||||
function showPhrase() {
|
||||
var n = summaryPhrases.length
|
||||
@@ -684,7 +684,7 @@ Panel {
|
||||
function dayName(date) {
|
||||
var parsed = new Date(String(date || "") + "T00:00:00")
|
||||
if (isNaN(parsed.getTime())) return String(date || "")
|
||||
return ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][parsed.getDay()]
|
||||
return I18n.tr(["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][parsed.getDay()])
|
||||
}
|
||||
|
||||
// Only speaks up when the numbers cover more than this machine.
|
||||
@@ -693,7 +693,7 @@ Panel {
|
||||
for (var i = 0; i < providers.length; i++) {
|
||||
var count = Number(providers[i].syncDeviceCount || 0)
|
||||
if (providers[i].syncEnabled && count > 0)
|
||||
return "Merged from " + count + " device" + (count === 1 ? "" : "s")
|
||||
return I18n.tr(count === 1 ? "Merged from %1 device" : "Merged from %1 devices", count)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -743,8 +743,8 @@ Panel {
|
||||
|
||||
// Adding takes over the hero's line, and the cursor starts over.
|
||||
readonly property string addHeading: picking || addProvider === ""
|
||||
? "Add an account"
|
||||
: "Add " + (/^[AEIOU]/.test(addProviderName(addProvider)) ? "an " : "a ") + addProviderName(addProvider) + " account"
|
||||
? I18n.tr("Add an account")
|
||||
: I18n.tr(/^[AEIOU]/.test(addProviderName(addProvider)) ? "Add an %1 account" : "Add a %1 account", addProviderName(addProvider))
|
||||
onAddStageChanged: {
|
||||
phraseSwap.stop()
|
||||
hero.metaOpacity = 1.0
|
||||
@@ -803,7 +803,7 @@ Panel {
|
||||
stdout: SplitParser { onRead: function(line) { root.handleAddLine(line) } }
|
||||
onExited: {
|
||||
if (root.addStage === "running") {
|
||||
root.addResult = "The sign-in didn't finish."
|
||||
root.addResult = I18n.tr("The sign-in didn't finish.")
|
||||
root.addStage = "error"
|
||||
}
|
||||
}
|
||||
@@ -938,7 +938,7 @@ Panel {
|
||||
PanelHero {
|
||||
id: hero
|
||||
width: parent.width
|
||||
title: "Agents"
|
||||
title: I18n.tr("Agents")
|
||||
meta: root.heroPhrase
|
||||
foreground: root.foreground
|
||||
fontFamily: root.fontFamily
|
||||
@@ -964,7 +964,7 @@ Panel {
|
||||
onHovered: root.pointAt("add")
|
||||
readonly property bool adding: root.addStage !== ""
|
||||
glyph: adding ? "" : ""
|
||||
tooltip: adding ? "Back to the limits" : "Add a subscription"
|
||||
tooltip: adding ? I18n.tr("Back to the limits") : I18n.tr("Add a subscription")
|
||||
onClicked: adding ? root.cancelAdd() : root.addAccount()
|
||||
}
|
||||
|
||||
@@ -973,7 +973,7 @@ Panel {
|
||||
hasCursor: root.hasKey("launch")
|
||||
onHovered: root.pointAt("launch")
|
||||
glyph: ""
|
||||
tooltip: "Start the default agent"
|
||||
tooltip: I18n.tr("Start the default agent")
|
||||
onClicked: root.launchAgent()
|
||||
}
|
||||
}
|
||||
@@ -1010,7 +1010,7 @@ Panel {
|
||||
spacing: Style.space(12)
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "MAKE SOMETHING COOL"
|
||||
text: I18n.tr("MAKE SOMETHING COOL")
|
||||
foreground: root.foreground
|
||||
fontFamily: root.fontFamily
|
||||
}
|
||||
@@ -1064,7 +1064,7 @@ Panel {
|
||||
Text {
|
||||
visible: root.blankSlate && root.picking
|
||||
width: parent.width
|
||||
text: "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
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.caption
|
||||
@@ -1136,7 +1136,7 @@ Panel {
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
text: "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
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.caption
|
||||
@@ -1146,7 +1146,7 @@ Panel {
|
||||
TextField {
|
||||
id: addNameField
|
||||
width: parent.width
|
||||
placeholderText: "Work"
|
||||
placeholderText: I18n.tr("Work")
|
||||
foreground: root.foreground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
@@ -1178,7 +1178,7 @@ Panel {
|
||||
spacing: Style.space(4)
|
||||
|
||||
Text {
|
||||
text: "Confirm this code in your browser"
|
||||
text: I18n.tr("Confirm this code in your browser")
|
||||
color: root.dim
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.caption
|
||||
@@ -1202,7 +1202,7 @@ Panel {
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
text: "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
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.caption
|
||||
@@ -1212,7 +1212,7 @@ Panel {
|
||||
TextField {
|
||||
id: pasteField
|
||||
width: parent.width
|
||||
placeholderText: "Code"
|
||||
placeholderText: I18n.tr("Code")
|
||||
foreground: root.foreground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
@@ -1223,7 +1223,7 @@ Panel {
|
||||
|
||||
TextLink {
|
||||
visible: root.addUrl !== ""
|
||||
text: "Open the sign-in page again"
|
||||
text: I18n.tr("Open the sign-in page again")
|
||||
onClicked: root.reopenSignIn()
|
||||
}
|
||||
}
|
||||
@@ -1329,9 +1329,9 @@ Panel {
|
||||
TextLink {
|
||||
visible: !section.multi && root.needsSignIn(section.provider)
|
||||
picked: root.hasKey("providerSignin", section.providerIndex)
|
||||
text: "Sign-in required"
|
||||
text: I18n.tr("Sign-in required")
|
||||
idleColor: root.urgent
|
||||
tooltip: "Sign in to " + (section.provider ? section.provider.providerName : "") + " again"
|
||||
tooltip: I18n.tr("Sign in to %1 again", section.provider ? section.provider.providerName : "")
|
||||
onClicked: root.signInAgain(section.provider, null)
|
||||
}
|
||||
|
||||
@@ -1397,7 +1397,7 @@ Panel {
|
||||
textFormat: Text.PlainText
|
||||
width: parent.width * 0.3
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "Balance"
|
||||
text: I18n.tr("Balance")
|
||||
color: root.foreground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.caption
|
||||
@@ -1567,7 +1567,7 @@ Panel {
|
||||
|
||||
PanelToolTip {
|
||||
visible: tileMouse.containsMouse
|
||||
text: "Start your default agent on a new " + tile.title.toLowerCase()
|
||||
text: I18n.tr("Start your default agent on a new %1", tile.title.toLowerCase())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1771,9 +1771,9 @@ Panel {
|
||||
id: signInLink
|
||||
visible: root.needsSignIn(head.account)
|
||||
picked: head.pickedKind === "signin"
|
||||
text: "Sign-in required"
|
||||
text: I18n.tr("Sign-in required")
|
||||
idleColor: root.urgent
|
||||
tooltip: "Sign in to this account again"
|
||||
tooltip: I18n.tr("Sign in to this account again")
|
||||
onClicked: root.signInAgain(head.owner, head.account)
|
||||
}
|
||||
}
|
||||
@@ -1809,7 +1809,7 @@ Panel {
|
||||
id: headActive
|
||||
visible: head.isActive
|
||||
anchors.right: parent.right
|
||||
text: "ACTIVE"
|
||||
text: I18n.tr("ACTIVE")
|
||||
color: Color.accent
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.caption
|
||||
@@ -1828,19 +1828,19 @@ Panel {
|
||||
|
||||
TextLink {
|
||||
visible: head.autoOn || useHover.hovered || head.picked
|
||||
text: head.pickedKind === "autoswitch" ? "Autoswitch ⏎" : "Autoswitch"
|
||||
text: I18n.tr("Autoswitch") + (head.pickedKind === "autoswitch" ? " ⏎" : "")
|
||||
picked: head.pickedKind === "autoswitch"
|
||||
current: head.autoOn
|
||||
tooltip: head.autoOn
|
||||
? "Stop switching automatically"
|
||||
: "Switch here automatically at " + root.switchThreshold(head.owner) + "%"
|
||||
? I18n.tr("Stop switching automatically")
|
||||
: I18n.tr("Switch here automatically at %1%", root.switchThreshold(head.owner))
|
||||
onClicked: root.setSwitchMode(head.owner, head.autoOn ? "manual" : "auto")
|
||||
}
|
||||
|
||||
// With Autoswitch on, Use waits until the line is hovered or picked.
|
||||
TextLink {
|
||||
visible: !head.autoOn || useHover.hovered || head.picked
|
||||
text: head.pickedKind === "account" ? "Use ⏎" : "Use"
|
||||
text: I18n.tr("Use") + (head.pickedKind === "account" ? " ⏎" : "")
|
||||
picked: head.pickedKind === "account"
|
||||
onClicked: root.useAccount(head.owner, head.account)
|
||||
}
|
||||
@@ -1857,7 +1857,7 @@ Panel {
|
||||
|
||||
Text {
|
||||
id: unavailableLabel
|
||||
text: "Usage unavailable"
|
||||
text: I18n.tr("Usage unavailable")
|
||||
color: root.dim
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
@@ -1867,7 +1867,7 @@ Panel {
|
||||
|
||||
PanelToolTip {
|
||||
visible: unavailableHover.hovered && unavailable.visible
|
||||
text: unavailable.record ? String(unavailable.record.authHelpText || "Usage has not been updated yet.") : ""
|
||||
text: unavailable.record ? String(unavailable.record.authHelpText || I18n.tr("Usage has not been updated yet.")) : ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1892,16 +1892,16 @@ Panel {
|
||||
text: {
|
||||
var lines = []
|
||||
if (compact.window) {
|
||||
lines.push(compact.window.title + ": " + Math.round(compact.window.percent * 100) + "% used"
|
||||
+ (compact.resetMs > 0 ? " · resets in " + root.formatDuration(compact.resetMs) : ""))
|
||||
lines.push(I18n.tr("%1: %2% used", compact.window.title, Math.round(compact.window.percent * 100))
|
||||
+ (compact.resetMs > 0 ? " · " + I18n.tr("resets in %1", root.formatDuration(compact.resetMs)) : ""))
|
||||
}
|
||||
if (compact.stale)
|
||||
lines.push(compact.fetchedAt > 0 && root.nowMs - compact.fetchedAt > 60000
|
||||
? "Last updated " + root.formatDuration(root.nowMs - compact.fetchedAt) + " ago"
|
||||
: compact.fetchedAt > 0 ? "Last updated less than a minute ago" : "Last updated time unavailable")
|
||||
? 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"))
|
||||
for (var i = 0; i < compact.scoped.length; i++)
|
||||
lines.push(compact.scoped[i].title + ": " + Math.round(compact.scoped[i].percent * 100) + "% of its "
|
||||
+ String(compact.window ? compact.window.title : "").toLowerCase() + " allowance")
|
||||
lines.push(I18n.tr("%1: %2% of its %3 allowance", compact.scoped[i].title, Math.round(compact.scoped[i].percent * 100),
|
||||
String(compact.window ? compact.window.title : "").toLowerCase()))
|
||||
return lines.join("\n")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import Quickshell.Io
|
||||
import qs.Commons
|
||||
import qs.Ui
|
||||
|
||||
BarIndicator {
|
||||
@@ -12,7 +13,7 @@ BarIndicator {
|
||||
activeText: icon
|
||||
inactiveText: ""
|
||||
activeTooltipText: state
|
||||
inactiveTooltipText: "Dictate"
|
||||
inactiveTooltipText: I18n.tr("Dictate")
|
||||
|
||||
function update(raw) {
|
||||
var data = extractData(raw)
|
||||
|
||||
@@ -11,8 +11,8 @@ BarIndicator {
|
||||
active: dnd
|
||||
activeText: ""
|
||||
inactiveText: ""
|
||||
activeTooltipText: "Allow Notifications"
|
||||
inactiveTooltipText: "Silence Notifications"
|
||||
activeTooltipText: I18n.tr("Allow Notifications")
|
||||
inactiveTooltipText: I18n.tr("Silence Notifications")
|
||||
|
||||
onPressed: function() {
|
||||
if (root.notificationService) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import QtQuick
|
||||
import qs.Commons
|
||||
import qs.Ui
|
||||
|
||||
BarIndicator {
|
||||
@@ -9,8 +10,8 @@ BarIndicator {
|
||||
active: nightlightService ? nightlightService.enabled : false
|
||||
activeText: ""
|
||||
inactiveText: ""
|
||||
activeTooltipText: "Day Light"
|
||||
inactiveTooltipText: "Night Light"
|
||||
activeTooltipText: I18n.tr("Day Light")
|
||||
inactiveTooltipText: I18n.tr("Night Light")
|
||||
|
||||
function toggle() {
|
||||
if (root.nightlightService) root.nightlightService.setNightlight(!root.active)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import Quickshell.Io
|
||||
import qs.Commons
|
||||
import qs.Ui
|
||||
|
||||
BarIndicator {
|
||||
@@ -10,8 +11,8 @@ BarIndicator {
|
||||
active: recording
|
||||
activeText: ""
|
||||
inactiveText: ""
|
||||
activeTooltipText: "Stop recording"
|
||||
inactiveTooltipText: "Screen Recording"
|
||||
activeTooltipText: I18n.tr("Stop recording")
|
||||
inactiveTooltipText: I18n.tr("Screen Recording")
|
||||
|
||||
function refresh() {
|
||||
if (!root.bar || statusProc.running) return
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import QtQuick
|
||||
import qs.Commons
|
||||
import qs.Ui
|
||||
|
||||
BarIndicator {
|
||||
@@ -9,8 +10,8 @@ BarIndicator {
|
||||
active: idleService ? idleService.stayAwake : false
|
||||
activeText: ""
|
||||
inactiveText: ""
|
||||
activeTooltipText: "Allow Idle Lock & Screensaver"
|
||||
inactiveTooltipText: "Stay Awake"
|
||||
activeTooltipText: I18n.tr("Allow Idle Lock & Screensaver")
|
||||
inactiveTooltipText: I18n.tr("Stay Awake")
|
||||
|
||||
function toggle() {
|
||||
if (root.idleService) root.idleService.setIdleEnabled(root.active)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Services.Pipewire
|
||||
import qs.Commons
|
||||
import qs.Ui
|
||||
|
||||
BarWidget {
|
||||
@@ -40,7 +41,7 @@ BarWidget {
|
||||
bar: root.bar
|
||||
text: root.muted ? "" : ""
|
||||
active: root.inUse
|
||||
tooltipText: root.muted ? "Microphone muted" : (root.inUse ? "Microphone in use" : "Microphone live")
|
||||
tooltipText: root.muted ? I18n.tr("Microphone muted") : (root.inUse ? I18n.tr("Microphone in use") : I18n.tr("Microphone live"))
|
||||
onPressed: function(b) {
|
||||
if (b === Qt.MiddleButton) root.bar.run("omarchy-shell shell toggle omarchy.audio")
|
||||
else root.toggleMute()
|
||||
|
||||
@@ -59,7 +59,7 @@ BarWidget {
|
||||
text: "\uf021"
|
||||
slotSize: Style.bar.statusSlot
|
||||
fontSize: Style.font.caption
|
||||
tooltipText: "Pending Omarchy Updates"
|
||||
tooltipText: I18n.tr("Pending Omarchy Updates")
|
||||
onPressed: root.runUpdate()
|
||||
}
|
||||
}
|
||||
@@ -410,7 +410,7 @@ BarWidget {
|
||||
spacing: Style.space(8)
|
||||
|
||||
Text {
|
||||
text: "Tray icons"
|
||||
text: I18n.tr("Tray icons")
|
||||
color: root.foreground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
@@ -418,7 +418,7 @@ BarWidget {
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "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)
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.caption
|
||||
@@ -428,7 +428,7 @@ BarWidget {
|
||||
|
||||
Text {
|
||||
visible: root.allItems.length === 0
|
||||
text: "No tray items reporting."
|
||||
text: I18n.tr("No tray items reporting.")
|
||||
color: Qt.darker(root.foreground, 1.5)
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
@@ -485,7 +485,7 @@ BarWidget {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
iconText: "\uf08d"
|
||||
text: rowRoot.isPinned ? "Unpin" : "Pin"
|
||||
text: rowRoot.isPinned ? I18n.tr("Unpin") : I18n.tr("Pin")
|
||||
foreground: root.foreground
|
||||
horizontalPadding: 8
|
||||
verticalPadding: 3
|
||||
@@ -500,7 +500,7 @@ BarWidget {
|
||||
anchors.right: rowPinBtn.left
|
||||
anchors.rightMargin: Style.space(6)
|
||||
iconText: "\uf06e"
|
||||
text: rowRoot.isHidden ? "Show" : "Hide"
|
||||
text: rowRoot.isHidden ? I18n.tr("Show") : I18n.tr("Hide")
|
||||
foreground: root.foreground
|
||||
horizontalPadding: 8
|
||||
verticalPadding: 3
|
||||
|
||||
@@ -398,8 +398,8 @@ Item {
|
||||
anchors.fill: parent
|
||||
opened: root.clearConfirmOpen
|
||||
z: 10
|
||||
message: "Delete entire clipboard history?"
|
||||
confirmText: "Delete"
|
||||
message: I18n.tr("Delete entire clipboard history?")
|
||||
confirmText: I18n.tr("Delete")
|
||||
background: root.background
|
||||
foreground: root.foreground
|
||||
scrim: root.scrim
|
||||
@@ -592,7 +592,7 @@ Item {
|
||||
|
||||
Text {
|
||||
textFormat: Text.PlainText
|
||||
text: root.history.length === 0 ? "Clipboard is empty" : "No matches for “" + root.filterText + "”"
|
||||
text: root.history.length === 0 ? I18n.tr("Clipboard is empty") : I18n.tr("No matches for “%1”", root.filterText)
|
||||
color: root.foreground
|
||||
opacity: 0.7
|
||||
font.family: root.fontFamily
|
||||
|
||||
@@ -324,7 +324,7 @@ Item {
|
||||
|
||||
Text {
|
||||
textFormat: Text.PlainText
|
||||
text: "No matches for “" + root.filterText + "”"
|
||||
text: I18n.tr("No matches for “%1”", root.filterText)
|
||||
color: root.foreground
|
||||
opacity: 0.7
|
||||
font.family: root.fontFamily
|
||||
|
||||
@@ -91,7 +91,7 @@ Item {
|
||||
|
||||
function currentLabel() {
|
||||
var path = currentPath()
|
||||
if (!path) return filterText ? "No matches" : ""
|
||||
if (!path) return filterText ? I18n.tr("No matches") : ""
|
||||
|
||||
return labelForPath(path)
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ Item {
|
||||
property string passwordText: ""
|
||||
property bool syncingPasswordText: false
|
||||
|
||||
readonly property string placeholderText: "Enter Password"
|
||||
readonly property string placeholderText: I18n.tr("Enter Password")
|
||||
readonly property int fieldWidth: 381
|
||||
readonly property int fieldHeight: 67
|
||||
readonly property int outlineThickness: 3
|
||||
@@ -222,7 +222,7 @@ Item {
|
||||
Text {
|
||||
textFormat: Text.PlainText
|
||||
anchors.fill: passwordInput
|
||||
text: root.authenticatingPassword ? "Checking…" : (root.failureMessage.length > 0 ? root.failureMessage : root.placeholderText)
|
||||
text: root.authenticatingPassword ? I18n.tr("Checking…") : (root.failureMessage.length > 0 ? root.failureMessage : root.placeholderText)
|
||||
visible: passwordInput.text.length === 0
|
||||
color: root.authenticatingPassword ? Color.lock.text : (root.failureMessage.length > 0 ? Color.lock.textError : Color.lock.placeholder)
|
||||
font.family: Style.font.family
|
||||
@@ -265,7 +265,7 @@ Item {
|
||||
anchors.topMargin: 18
|
||||
anchors.horizontalCenter: inputField.horizontalCenter
|
||||
visible: root.fingerprintConfigured && root.fingerprintUnavailable
|
||||
text: "Fingerprint reader unavailable"
|
||||
text: I18n.tr("Fingerprint reader unavailable")
|
||||
color: Color.lock.textError
|
||||
font.family: Style.font.family
|
||||
font.pixelSize: Style.font.heading
|
||||
|
||||
@@ -350,7 +350,7 @@ Item {
|
||||
enteredPassword = ""
|
||||
pendingPassword = ""
|
||||
failedAttempts += 1
|
||||
failureMessage = "Authentication failed (" + failedAttempts + ")"
|
||||
failureMessage = I18n.tr("Authentication failed (%1)", failedAttempts)
|
||||
runWake()
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ Item {
|
||||
// path doesn't have to shell out to bash + jq on every open.
|
||||
property string defaultMenuPath: omarchyPath + "/default/omarchy/omarchy-menu.jsonc"
|
||||
property string userMenuPath: Quickshell.env("HOME") + "/.config/omarchy/extensions/omarchy-menu.jsonc"
|
||||
property string localeMenuPath: (Quickshell.env("LANG") || "").startsWith("zh_CN") ? omarchyPath + "/default/omarchy/omarchy-menu.zh-cn.jsonc" : ""
|
||||
property string localeMenuPath: I18n.chinese ? omarchyPath + "/default/omarchy/omarchy-menu.zh-cn.jsonc" : ""
|
||||
property var defaultMenuItems: []
|
||||
property var localeMenuItems: []
|
||||
property var userMenuItems: []
|
||||
@@ -867,7 +867,7 @@ Item {
|
||||
function openDmenu(payload) {
|
||||
requestSerial += 1
|
||||
mode = payload.mode === "input" ? "input" : "select"
|
||||
dmenuPrompt = String(payload.prompt || (mode === "input" ? "Input" : "Select"))
|
||||
dmenuPrompt = String(payload.prompt || (mode === "input" ? I18n.tr("Input") : I18n.tr("Select")))
|
||||
dmenuOptions = Array.isArray(payload.options) ? payload.options : []
|
||||
selectionFile = String(payload.selectionFile || "")
|
||||
doneFile = String(payload.doneFile || "")
|
||||
@@ -1182,8 +1182,8 @@ Item {
|
||||
anchors.fill: parent
|
||||
opened: root.deleteConfirmOpen
|
||||
z: 10
|
||||
message: "Do you want to uninstall " + ((root.deleteTarget && root.deleteTarget.label) || "") + "?"
|
||||
confirmText: "Uninstall"
|
||||
message: I18n.tr("Do you want to uninstall %1?", (root.deleteTarget && root.deleteTarget.label) || "")
|
||||
confirmText: I18n.tr("Uninstall")
|
||||
background: root.background
|
||||
foreground: root.foreground
|
||||
scrim: root.scrim
|
||||
@@ -1215,7 +1215,7 @@ Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: root.filterText || (root.dmenuActive ? (root.dmenuPrompt + "…") : ((root.item(root.activeMenu) ? (root.item(root.activeMenu).title || root.item(root.activeMenu).label) : "Go") + "…"))
|
||||
text: root.filterText || (root.dmenuActive ? (root.dmenuPrompt + "…") : ((root.item(root.activeMenu) ? I18n.tr(root.item(root.activeMenu).title || root.item(root.activeMenu).label) : I18n.tr("Go")) + "…"))
|
||||
color: root.foreground
|
||||
opacity: root.filterText ? 1 : 0.58
|
||||
font.family: root.fontFamily
|
||||
@@ -1471,7 +1471,7 @@ Item {
|
||||
|
||||
Text {
|
||||
textFormat: Text.PlainText
|
||||
text: root.filterText ? "No matches for “" + root.filterText + "”" : "Nothing here yet"
|
||||
text: root.filterText ? I18n.tr("No matches for “%1”", root.filterText) : I18n.tr("Nothing here yet")
|
||||
color: root.foreground
|
||||
opacity: 0.7
|
||||
font.family: root.fontFamily
|
||||
|
||||
@@ -721,7 +721,7 @@ Item {
|
||||
originalId: -1,
|
||||
app: "omarchy-action",
|
||||
appIcon: "",
|
||||
summary: "No recent notifications",
|
||||
summary: I18n.tr("No recent notifications"),
|
||||
body: "",
|
||||
image: "",
|
||||
glyph: "",
|
||||
|
||||
@@ -44,7 +44,7 @@ BorderSurface {
|
||||
readonly property bool singleLineToast: sanitizedBody.length === 0
|
||||
readonly property bool collapseRedundantIcon: singleLineToast && !hasGlyph && summaryStartsWithGlyph
|
||||
readonly property string sanitizedBody: sanitizeBody(body)
|
||||
readonly property string styledBody: NotificationLogic.styledBody(body, app, appIcon)
|
||||
readonly property string styledBody: NotificationLogic.styledBody(I18n.tr(body), app, appIcon)
|
||||
|
||||
readonly property color dimColor: Qt.darker(Color.notifications.text, 1.4)
|
||||
readonly property color bodyColor: Qt.darker(Color.notifications.text, 1.15)
|
||||
@@ -168,7 +168,7 @@ BorderSurface {
|
||||
textFormat: Text.PlainText
|
||||
Layout.fillWidth: true
|
||||
visible: root.summary.length > 0
|
||||
text: root.summary
|
||||
text: I18n.tr(root.summary)
|
||||
font.family: "Liberation Sans"
|
||||
color: Color.notifications.text
|
||||
font.pixelSize: Style.font.title
|
||||
|
||||
@@ -90,7 +90,7 @@ Item {
|
||||
font.family: Style.font.family
|
||||
font.bold: true
|
||||
font.pixelSize: Style.font.title
|
||||
text: root.message
|
||||
text: I18n.tr(root.message)
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
@@ -190,7 +190,7 @@ Item {
|
||||
// a hole in the padding; the slack lands in the gap after the bar.
|
||||
horizontalAlignment: root.hasProgress ? Text.AlignRight : Text.AlignLeft
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: root.message
|
||||
text: I18n.tr(root.message)
|
||||
font: messageMetrics.font
|
||||
color: Color.popups.text
|
||||
elide: Text.ElideRight
|
||||
|
||||
@@ -176,7 +176,7 @@ Panel {
|
||||
readonly property bool hasOutput: !!(volumeSink && volumeSink.audio)
|
||||
readonly property bool hasInput: !!(source && source.audio)
|
||||
readonly property bool anyAudible: (hasOutput && !outputMuted) || (hasInput && !inputMuted)
|
||||
readonly property string toggleHint: anyAudible ? "Mute" : "Unmute"
|
||||
readonly property string toggleHint: anyAudible ? I18n.tr("Mute") : I18n.tr("Unmute")
|
||||
|
||||
readonly property color hoverFill: bar
|
||||
? Style.hoverFillFor(bar.foreground, Color.accent)
|
||||
@@ -434,7 +434,7 @@ Panel {
|
||||
// panel's brightnessName ladder; bands are wide enough that small
|
||||
// tweaks don't rename the room you're in.
|
||||
function outputVolumeName(volume, muted) {
|
||||
return Model.outputVolumeName(volume, muted)
|
||||
return I18n.tr(Model.outputVolumeName(volume, muted))
|
||||
}
|
||||
|
||||
function setOutputVolume(v) {
|
||||
@@ -768,7 +768,7 @@ Panel {
|
||||
spacing: Style.space(2)
|
||||
|
||||
Text {
|
||||
text: "Audio"
|
||||
text: I18n.tr("Audio")
|
||||
color: root.bar.foreground
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.title
|
||||
@@ -810,7 +810,7 @@ Panel {
|
||||
|
||||
PanelSectionHeader {
|
||||
id: outputHeader
|
||||
text: "OUTPUT"
|
||||
text: I18n.tr("OUTPUT")
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
anchors.left: parent.left
|
||||
@@ -897,7 +897,7 @@ Panel {
|
||||
|
||||
PanelSectionHeader {
|
||||
id: microphoneHeader
|
||||
text: "INPUT"
|
||||
text: I18n.tr("INPUT")
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
anchors.left: parent.left
|
||||
@@ -1000,7 +1000,7 @@ Panel {
|
||||
visible: root.displayAudioStreams.length > 0
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "SOURCES"
|
||||
text: I18n.tr("SOURCES")
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
}
|
||||
|
||||
@@ -64,19 +64,19 @@ Panel {
|
||||
|
||||
property int phraseIndex: 0
|
||||
readonly property var activePhrases: [
|
||||
"Untangling wires",
|
||||
"Streaming vikings",
|
||||
"Pairing mysteries",
|
||||
"Herding headsets",
|
||||
"Taming radios",
|
||||
"Summoning speakers",
|
||||
"Wrangling codecs",
|
||||
"Polishing packets"
|
||||
I18n.tr("Untangling wires"),
|
||||
I18n.tr("Streaming vikings"),
|
||||
I18n.tr("Pairing mysteries"),
|
||||
I18n.tr("Herding headsets"),
|
||||
I18n.tr("Taming radios"),
|
||||
I18n.tr("Summoning speakers"),
|
||||
I18n.tr("Wrangling codecs"),
|
||||
I18n.tr("Polishing packets")
|
||||
]
|
||||
readonly property bool rotatingPhrases: adapter && adapter.enabled
|
||||
readonly property string heroStatusText: {
|
||||
if (!adapter) return "No adapter"
|
||||
if (!adapter.enabled) return "Turned Off"
|
||||
if (!adapter) return I18n.tr("No adapter")
|
||||
if (!adapter.enabled) return I18n.tr("Turned Off")
|
||||
return activePhrases[phraseIndex % activePhrases.length]
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ Panel {
|
||||
// sits above the device sections so the adapter can be toggled by keyboard
|
||||
// even when it is off and no device rows exist.
|
||||
readonly property bool headerHasCursor: cursorActive && focusSection === "header"
|
||||
readonly property string toggleHint: root.adapter && root.adapter.enabled ? "Turn Bluetooth off" : "Turn Bluetooth on"
|
||||
readonly property string toggleHint: root.adapter && root.adapter.enabled ? I18n.tr("Turn Bluetooth off") : I18n.tr("Turn Bluetooth on")
|
||||
|
||||
readonly property color hoverFill: bar
|
||||
? Style.hoverFillFor(bar.foreground, Color.accent)
|
||||
@@ -183,7 +183,7 @@ Panel {
|
||||
var rows = scrollRows
|
||||
if (index < 0 || index >= rows.length) return ""
|
||||
if (index > 0 && rows[index - 1].section === rows[index].section) return ""
|
||||
return rows[index].section === "known" ? "PAIRED" : "AVAILABLE"
|
||||
return rows[index].section === "known" ? I18n.tr("PAIRED") : I18n.tr("AVAILABLE")
|
||||
}
|
||||
|
||||
function audioSinks() {
|
||||
@@ -738,7 +738,7 @@ Panel {
|
||||
spacing: Style.space(2)
|
||||
|
||||
Text {
|
||||
text: "Bluetooth"
|
||||
text: I18n.tr("Bluetooth")
|
||||
color: root.bar.foreground
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.title
|
||||
@@ -775,7 +775,7 @@ Panel {
|
||||
spacing: Style.space(10)
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "CONNECTED"
|
||||
text: I18n.tr("CONNECTED")
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
}
|
||||
@@ -867,9 +867,9 @@ Panel {
|
||||
Text {
|
||||
textFormat: Text.PlainText
|
||||
visible: root.connectedDevices.length === 0 && root.scrollRows.length === 0
|
||||
text: !root.adapter ? "No Bluetooth adapter"
|
||||
: !root.adapter.enabled ? "Turn Bluetooth on to scan"
|
||||
: "Scanning for devices…"
|
||||
text: !root.adapter ? I18n.tr("No Bluetooth adapter")
|
||||
: !root.adapter.enabled ? I18n.tr("Turn Bluetooth on to scan")
|
||||
: I18n.tr("Scanning for devices…")
|
||||
color: Qt.darker(root.bar.foreground, 1.5)
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
@@ -894,9 +894,9 @@ Panel {
|
||||
readonly property string action: root.pendingAction(dev ? dev.address : "")
|
||||
readonly property string actionTooltip: {
|
||||
if (!dev) return ""
|
||||
if (isConnected) return "Disconnect"
|
||||
if (isDiscovered) return "Pair"
|
||||
return "Connect"
|
||||
if (isConnected) return I18n.tr("Disconnect")
|
||||
if (isDiscovered) return I18n.tr("Pair")
|
||||
return I18n.tr("Connect")
|
||||
}
|
||||
|
||||
readonly property bool rowSelected: root.cursorActive && root.focusSection === sectionName && root.selectedIndex === rowIndex
|
||||
@@ -911,13 +911,13 @@ Panel {
|
||||
|
||||
readonly property string statusText: {
|
||||
if (!dev) return ""
|
||||
if (action === "forgetting") return "Forgetting…"
|
||||
if (action === "disconnecting" || devState === 2) return "Disconnecting…"
|
||||
if (action === "forgetting") return I18n.tr("Forgetting…")
|
||||
if (action === "disconnecting" || devState === 2) return I18n.tr("Disconnecting…")
|
||||
if (isConnected) {
|
||||
if (dev.batteryAvailable) return Math.round(dev.battery * 100) + "%"
|
||||
return sectionName === "connected" ? "" : "Connected"
|
||||
return sectionName === "connected" ? "" : I18n.tr("Connected")
|
||||
}
|
||||
if (action === "connecting" || devState === 3 || dev.pairing === true) return "Connecting…"
|
||||
if (action === "connecting" || devState === 3 || dev.pairing === true) return I18n.tr("Connecting…")
|
||||
if (isDiscovered) return ""
|
||||
return ""
|
||||
}
|
||||
@@ -994,7 +994,7 @@ Panel {
|
||||
|
||||
Text {
|
||||
textFormat: Text.PlainText
|
||||
text: root.deviceLabel(row.dev) || "Device"
|
||||
text: root.deviceLabel(row.dev) || I18n.tr("Device")
|
||||
color: root.bar.foreground
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
@@ -1019,7 +1019,7 @@ Panel {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: row.showForgetButton
|
||||
iconText: ""
|
||||
tooltipText: "Forget"
|
||||
tooltipText: I18n.tr("Forget")
|
||||
foreground: root.bar.foreground
|
||||
hoverColor: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
|
||||
@@ -21,7 +21,7 @@ BarWidget {
|
||||
: setting("format", "dddd HH:mm")
|
||||
readonly property string configuredAltFormat: vertical
|
||||
? setting("verticalFormatAlt", "dd\nMMM\n'W'ww\n''yy")
|
||||
: setting("formatAlt", "d MMMM 'W'ww yyyy")
|
||||
: setting("formatAlt", I18n.chinese ? "yyyy年M月d日 'W'ww" : "d MMMM 'W'ww yyyy")
|
||||
|
||||
readonly property var formatRing: Model.clockFormatRing(configuredFormat, configuredAltFormat, Model.clockFormats(vertical))
|
||||
|
||||
@@ -56,8 +56,10 @@ BarWidget {
|
||||
root.bar.shell.updateEntryInline(root.moduleName, entry)
|
||||
}
|
||||
|
||||
// Qt.formatDateTime is always English; a Chinese session takes day and month names from its locale.
|
||||
function formatted(date) {
|
||||
return Qt.formatDateTime(date, activeFormat.replace(/ww/g, Model.isoWeekLiteral(date.getFullYear(), date.getMonth(), date.getDate())))
|
||||
var format = activeFormat.replace(/ww/g, Model.isoWeekLiteral(date.getFullYear(), date.getMonth(), date.getDate()))
|
||||
return I18n.chinese ? Qt.locale().toString(date, format) : Qt.formatDateTime(date, format)
|
||||
}
|
||||
|
||||
// ---- Calendar popup. Shape contract for shell.summon/hide/toggle
|
||||
@@ -153,7 +155,7 @@ BarWidget {
|
||||
fixedHeight: root.vertical ? root.verticalLines.length * Style.bar.iconSlot : -1
|
||||
horizontalMargin: 8.75
|
||||
verticalPadding: 8.75
|
||||
tooltipText: "Right-click to toggle format"
|
||||
tooltipText: I18n.tr("Right-click to toggle format")
|
||||
|
||||
onPressed: function(b) {
|
||||
if (b === Qt.RightButton) root.cycleFormat()
|
||||
|
||||
@@ -64,10 +64,8 @@ Panel {
|
||||
// convention. Clicking the grid's "W" heading writes the choice back to
|
||||
// shell.json.
|
||||
readonly property int weekStart: Model.normalizedWeekStart(setting("weekStartDay", null), Qt.locale().firstDayOfWeek)
|
||||
// The interface is English throughout, so day names are not taken from the
|
||||
// system locale. Where the week starts still is: that is a regional
|
||||
// convention rather than a translation, and it stays overridable above.
|
||||
readonly property var labelLocale: Qt.locale("en_US")
|
||||
// Day names follow the interface language, not the system locale; the week start above is a regional convention.
|
||||
readonly property var labelLocale: I18n.chinese ? Qt.locale("zh_CN") : Qt.locale("en_US")
|
||||
readonly property string nextWeekStartLabel: labelLocale.dayName(Model.toggledWeekStart(weekStart), Locale.LongFormat)
|
||||
readonly property var weekdays: Model.weekdayOrder(weekStart)
|
||||
readonly property var weeks: Model.monthGrid(viewYear, viewMonth, weekStart, todayKey)
|
||||
@@ -315,7 +313,7 @@ Panel {
|
||||
id: heroDate
|
||||
textFormat: Text.PlainText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: Qt.formatDate(root.today, "MMMM d")
|
||||
text: I18n.chinese ? root.labelLocale.toString(root.today, "M月d日") : Qt.formatDate(root.today, "MMMM d")
|
||||
color: heroMouse.containsMouse
|
||||
? Style.hoverStateColor(root.contentForeground, Color.accent)
|
||||
: root.contentForeground
|
||||
@@ -338,7 +336,7 @@ Panel {
|
||||
|
||||
PanelToolTip {
|
||||
visible: heroMouse.containsMouse
|
||||
text: "Back to today"
|
||||
text: I18n.tr("Back to today")
|
||||
fontFamily: root.contentFontFamily
|
||||
}
|
||||
}
|
||||
@@ -371,7 +369,7 @@ Panel {
|
||||
|
||||
Text {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "BORN"
|
||||
text: I18n.tr("BORN")
|
||||
color: Qt.darker(root.contentForeground, 1.5)
|
||||
font.family: root.contentFontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
@@ -394,7 +392,7 @@ Panel {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: 0
|
||||
leftPadding: Style.space(6)
|
||||
text: "LIVE TO"
|
||||
text: I18n.tr("LIVE TO")
|
||||
color: Qt.darker(root.contentForeground, 1.5)
|
||||
font.family: root.contentFontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
@@ -481,7 +479,7 @@ Panel {
|
||||
id: lifeLabel
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "LIFE"
|
||||
text: I18n.tr("LIFE")
|
||||
color: Qt.darker(root.contentForeground, 1.5)
|
||||
font.family: root.contentFontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
@@ -600,7 +598,7 @@ Panel {
|
||||
|
||||
PanelToolTip {
|
||||
visible: weekStartMouse.containsMouse
|
||||
text: "Start weeks on " + root.nextWeekStartLabel
|
||||
text: I18n.tr("Start weeks on %1", root.nextWeekStartLabel)
|
||||
fontFamily: root.contentFontFamily
|
||||
}
|
||||
}
|
||||
@@ -723,7 +721,7 @@ Panel {
|
||||
// "MAY 2026" and a "SEPTEMBER 2026".
|
||||
width: Style.space(130)
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: Qt.formatDate(root.viewDate, "MMMM yyyy").toUpperCase()
|
||||
text: I18n.chinese ? root.labelLocale.toString(root.viewDate, "yyyy年M月") : Qt.formatDate(root.viewDate, "MMMM yyyy").toUpperCase()
|
||||
color: Qt.darker(root.contentForeground, 1.4)
|
||||
font.family: root.contentFontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
@@ -737,7 +735,7 @@ Panel {
|
||||
anchors.leftMargin: -Style.space(8)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconText: ""
|
||||
tooltipText: "Previous month"
|
||||
tooltipText: I18n.tr("Previous month")
|
||||
foreground: root.contentForeground
|
||||
fontFamily: root.contentFontFamily
|
||||
onClicked: root.moveMonth(-1)
|
||||
@@ -748,7 +746,7 @@ Panel {
|
||||
anchors.rightMargin: -Style.space(8)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconText: ""
|
||||
tooltipText: "Next month"
|
||||
tooltipText: I18n.tr("Next month")
|
||||
foreground: root.contentForeground
|
||||
fontFamily: root.contentFontFamily
|
||||
onClicked: root.moveMonth(1)
|
||||
|
||||
@@ -117,7 +117,7 @@ Item {
|
||||
}
|
||||
|
||||
if (!root.expectedStop && exitCode !== 0) {
|
||||
root.error = root.stderrText || "Disk speed test failed"
|
||||
root.error = root.stderrText || I18n.tr("Disk speed test failed")
|
||||
root.phase = ""
|
||||
root.running = false
|
||||
return
|
||||
@@ -133,10 +133,10 @@ Item {
|
||||
fontFamily: Style.font.family
|
||||
layerNamespace: "omarchy-disk-speedtest"
|
||||
title: root.diskName
|
||||
leftLabel: "READ"
|
||||
rightLabel: "WRITE"
|
||||
leftLabel: I18n.tr("READ")
|
||||
rightLabel: I18n.tr("WRITE")
|
||||
unit: "MB/s"
|
||||
runAgainTooltip: "Measure again"
|
||||
runAgainTooltip: I18n.tr("Measure again")
|
||||
running: root.running
|
||||
leftValue: root.toRate(root.readMBps)
|
||||
rightValue: root.toRate(root.writeMBps)
|
||||
|
||||
@@ -20,16 +20,16 @@ Panel {
|
||||
property int phraseIndex: 0
|
||||
|
||||
readonly property var activePhrases: [
|
||||
"Filing files",
|
||||
"Distributing data",
|
||||
"Shuffling folders",
|
||||
"Boxing bytes",
|
||||
"Sorting stuff",
|
||||
"Syncing secrets",
|
||||
"Packing packets",
|
||||
"Moving memories",
|
||||
"Wrangling revisions",
|
||||
"Cataloging chaos"
|
||||
I18n.tr("Filing files"),
|
||||
I18n.tr("Distributing data"),
|
||||
I18n.tr("Shuffling folders"),
|
||||
I18n.tr("Boxing bytes"),
|
||||
I18n.tr("Sorting stuff"),
|
||||
I18n.tr("Syncing secrets"),
|
||||
I18n.tr("Packing packets"),
|
||||
I18n.tr("Moving memories"),
|
||||
I18n.tr("Wrangling revisions"),
|
||||
I18n.tr("Cataloging chaos")
|
||||
]
|
||||
readonly property string heroPhraseText: activePhrases[phraseIndex % activePhrases.length]
|
||||
readonly property color foreground: bar ? bar.foreground : Color.foreground
|
||||
@@ -37,7 +37,7 @@ Panel {
|
||||
readonly property color dim: Qt.darker(foreground, 1.55)
|
||||
readonly property string fontFamily: bar ? bar.fontFamily : Style.font.family
|
||||
readonly property color iconColor: dropbox.authenticated && dropbox.active ? foreground : dim
|
||||
readonly property string toggleHint: dropbox.active ? "Pause syncing" : "Resume syncing"
|
||||
readonly property string toggleHint: dropbox.active ? I18n.tr("Pause syncing") : I18n.tr("Resume syncing")
|
||||
readonly property color barIconColor: dropbox.authenticated && dropbox.active ? barForeground : Qt.darker(barForeground, 1.55)
|
||||
// Only claim the header cursor when the switch is actually on screen —
|
||||
// "header" stays navigable, but an absent CLI leaves nothing to highlight.
|
||||
@@ -244,7 +244,7 @@ Panel {
|
||||
id: hero
|
||||
width: parent.width
|
||||
title: "Dropbox"
|
||||
meta: dropbox.active ? root.heroPhraseText : "Syncing paused"
|
||||
meta: dropbox.active ? root.heroPhraseText : I18n.tr("Syncing paused")
|
||||
foreground: root.foreground
|
||||
fontFamily: root.fontFamily
|
||||
iconOpacity: dropbox.active ? 1.0 : 0.5
|
||||
@@ -304,7 +304,7 @@ Panel {
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Style.spacing.labelGap
|
||||
InfoPair { label: "Stored"; value: Model.usageText(dropbox.usedBytes, dropbox.quotaBytes, dropbox.quotaKnown) }
|
||||
InfoPair { label: I18n.tr("Stored"); value: Model.usageText(dropbox.usedBytes, dropbox.quotaBytes, dropbox.quotaKnown) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,7 +319,7 @@ Panel {
|
||||
spacing: Style.space(10)
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "RECENT FILES"
|
||||
text: I18n.tr("RECENT FILES")
|
||||
foreground: root.foreground
|
||||
fontFamily: root.fontFamily
|
||||
}
|
||||
@@ -327,7 +327,7 @@ Panel {
|
||||
Text {
|
||||
visible: dropbox.files.length === 0
|
||||
width: parent.width
|
||||
text: "No synced files found."
|
||||
text: I18n.tr("No synced files found.")
|
||||
color: root.dim
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
@@ -424,7 +424,7 @@ Panel {
|
||||
Text {
|
||||
textFormat: Text.PlainText
|
||||
Layout.fillWidth: true
|
||||
text: dropbox.installed ? "Login to Dropbox" : "Dropbox CLI is not installed"
|
||||
text: dropbox.installed ? I18n.tr("Login to Dropbox") : I18n.tr("Dropbox CLI is not installed")
|
||||
color: root.foreground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
@@ -434,7 +434,7 @@ Panel {
|
||||
Text {
|
||||
textFormat: Text.PlainText
|
||||
Layout.fillWidth: true
|
||||
text: dropbox.installed ? "Start the authentication flow" : "Install Dropbox from the service menu"
|
||||
text: dropbox.installed ? I18n.tr("Start the authentication flow") : I18n.tr("Install Dropbox from the service menu")
|
||||
color: root.dim
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.caption
|
||||
@@ -457,7 +457,7 @@ Panel {
|
||||
id: fileRow
|
||||
property var file: null
|
||||
property int rowIndex: 0
|
||||
readonly property string fileName: file ? String(file.name || "Untitled") : "Untitled"
|
||||
readonly property string fileName: file ? String(file.name || I18n.tr("Untitled")) : I18n.tr("Untitled")
|
||||
|
||||
hasCursor: root.cursorActive && root.focusSection === "files" && root.fileIndex === rowIndex
|
||||
foreground: root.foreground
|
||||
|
||||
@@ -73,7 +73,7 @@ Column {
|
||||
width: parent.width
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
topPadding: Style.spacing.md
|
||||
text: list.citySearch.loading ? list.citySearch.loadingText : "No matches"
|
||||
text: list.citySearch.loading ? list.citySearch.loadingText : I18n.tr("No matches")
|
||||
color: list.citySearch.fainter
|
||||
}
|
||||
|
||||
|
||||
@@ -300,7 +300,7 @@ Rectangle {
|
||||
color: Model.mix(Color.popups.background, row.foreground, _hot ? 0.24 : 0.14)
|
||||
borderSpec: Border.flat(Model.mix(Color.popups.background, row.foreground, _hot ? 0.65 : 0.35), Style.space(1))
|
||||
iconText: "\u00d7"
|
||||
tooltipText: "Remove"
|
||||
tooltipText: I18n.tr("Remove")
|
||||
foreground: row.dim
|
||||
hoverColor: row.foreground
|
||||
fontFamily: row.fontFamily
|
||||
|
||||
@@ -17,7 +17,7 @@ Column {
|
||||
property var offsetLabel: function(zoneId) { return "" }
|
||||
property bool loading: false
|
||||
property string buttonText: "+ Add a city"
|
||||
property string placeholderText: "Search cities\u2026"
|
||||
property string placeholderText: I18n.tr("Search cities…")
|
||||
property string loadingText: "Loading zones\u2026"
|
||||
property real fontSize: Style.font.bodySmall
|
||||
property color foreground: Color.foreground
|
||||
|
||||
@@ -65,8 +65,8 @@ Item {
|
||||
var out = []
|
||||
var up = Sun.eventMark(sun.riseMinutes)
|
||||
var down = Sun.eventMark(sun.setMinutes)
|
||||
if (up !== null) out.push({ x: up, minutes: sun.riseMinutes, rising: true, name: "Sunrise" })
|
||||
if (down !== null) out.push({ x: down, minutes: sun.setMinutes, rising: false, name: "Sunset" })
|
||||
if (up !== null) out.push({ x: up, minutes: sun.riseMinutes, rising: true, name: I18n.tr("Sunrise") })
|
||||
if (down !== null) out.push({ x: down, minutes: sun.setMinutes, rising: false, name: I18n.tr("Sunset") })
|
||||
return out
|
||||
}
|
||||
|
||||
|
||||
@@ -584,7 +584,7 @@ Item {
|
||||
limit: 5
|
||||
inlineResults: false
|
||||
loading: root.jumpOptions.length === 0
|
||||
buttonText: "Jump to a city"
|
||||
buttonText: I18n.tr("Jump to a city")
|
||||
loadingText: "Loading cities\u2026"
|
||||
offsetLabel: function(zoneId) { return Model.utcOffsetLabel(root.offsets[zoneId]) }
|
||||
foreground: root.foreground
|
||||
|
||||
@@ -72,7 +72,7 @@ Column {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: Style.space(9)
|
||||
|
||||
TitleWord { text: "World" }
|
||||
TitleWord { text: I18n.tr("World") }
|
||||
|
||||
Item {
|
||||
id: heroIcon
|
||||
@@ -150,7 +150,7 @@ Column {
|
||||
}
|
||||
}
|
||||
|
||||
TitleWord { text: "Clock" }
|
||||
TitleWord { text: I18n.tr("Clock") }
|
||||
}
|
||||
|
||||
component TitleWord: Text {
|
||||
|
||||
@@ -334,7 +334,7 @@ Panel {
|
||||
}
|
||||
|
||||
// No full stop: a period hanging off the arc reads as a speck.
|
||||
readonly property string hereLine: "It's " + localTime + " here"
|
||||
readonly property string hereLine: I18n.tr("It's %1 here", localTime)
|
||||
+ (homeCity !== "" ? " in " + homeCity : "")
|
||||
+ (scrubLabel !== "" ? " " + scrubLabel : "")
|
||||
|
||||
@@ -714,7 +714,7 @@ Panel {
|
||||
bar: root.bar
|
||||
slotSize: Style.bar.statusSlot
|
||||
text: ""
|
||||
tooltipText: "World clock"
|
||||
tooltipText: I18n.tr("World clock")
|
||||
onPressed: function(buttonCode) {
|
||||
if (buttonCode === Qt.RightButton || buttonCode === Qt.MiddleButton) root.refresh()
|
||||
else root.toggle()
|
||||
|
||||
@@ -555,7 +555,7 @@ Panel {
|
||||
spacing: Style.space(2)
|
||||
|
||||
Text {
|
||||
text: "Display"
|
||||
text: I18n.tr("Display")
|
||||
color: root.bar.foreground
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.title
|
||||
@@ -569,9 +569,9 @@ Panel {
|
||||
textFormat: Text.PlainText
|
||||
text: {
|
||||
if (root.brightnessAvailable) {
|
||||
return root.brightnessName(brightnessSlider.dragging ? brightnessSlider.liveValue : root.brightnessPercent).toUpperCase()
|
||||
return I18n.tr(root.brightnessName(brightnessSlider.dragging ? brightnessSlider.liveValue : root.brightnessPercent)).toUpperCase()
|
||||
}
|
||||
return "FIXED BRIGHTNESS"
|
||||
return I18n.tr("FIXED BRIGHTNESS")
|
||||
}
|
||||
color: Qt.darker(root.bar.foreground, 1.4)
|
||||
font.family: root.bar.fontFamily
|
||||
@@ -601,7 +601,7 @@ Panel {
|
||||
|
||||
PanelSectionHeader {
|
||||
id: brightnessHeader
|
||||
text: "BRIGHTNESS"
|
||||
text: I18n.tr("BRIGHTNESS")
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
anchors.left: parent.left
|
||||
@@ -674,7 +674,7 @@ Panel {
|
||||
|
||||
PanelSectionHeader {
|
||||
id: textSizeHeader
|
||||
text: "TEXT SIZE"
|
||||
text: I18n.tr("TEXT SIZE")
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
anchors.left: parent.left
|
||||
@@ -746,7 +746,7 @@ Panel {
|
||||
|
||||
PanelSectionHeader {
|
||||
id: scaleHeader
|
||||
text: "SCALE"
|
||||
text: I18n.tr("SCALE")
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
anchors.left: parent.left
|
||||
@@ -808,7 +808,7 @@ Panel {
|
||||
visible: root.displays.length > 1
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "DISPLAYS"
|
||||
text: I18n.tr("DISPLAYS")
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
}
|
||||
|
||||
@@ -56,13 +56,13 @@ Panel {
|
||||
readonly property bool hasTransferStats: info.rx_bytes !== undefined
|
||||
property int connectionPhraseIndex: 0
|
||||
readonly property var connectionPhrases: [
|
||||
"Wiring bits",
|
||||
"Handling packets",
|
||||
"Sorting frames",
|
||||
"Hauling bytes",
|
||||
"Routing crumbs",
|
||||
"Counting collisions",
|
||||
"Bending light",
|
||||
I18n.tr("Wiring bits"),
|
||||
I18n.tr("Handling packets"),
|
||||
I18n.tr("Sorting frames"),
|
||||
I18n.tr("Hauling bytes"),
|
||||
I18n.tr("Routing crumbs"),
|
||||
I18n.tr("Counting collisions"),
|
||||
I18n.tr("Bending light"),
|
||||
]
|
||||
readonly property string connectionPhrase: connectionPhrases[connectionPhraseIndex % connectionPhrases.length]
|
||||
readonly property bool networkManagerAvailable: Networking.backend === NetworkBackendType.NetworkManager
|
||||
@@ -137,7 +137,7 @@ Panel {
|
||||
readonly property bool qrHeaderHasCursor: cursorActive && focusSection === "header" && headerIndex === qrHeaderIndex
|
||||
readonly property bool speedHeaderHasCursor: cursorActive && focusSection === "header" && headerIndex === speedHeaderIndex
|
||||
readonly property bool toggleHeaderHasCursor: cursorActive && focusSection === "header" && headerIndex === toggleHeaderIndex
|
||||
readonly property string toggleHint: Networking.wifiEnabled ? "Turn Wi-Fi off" : "Turn Wi-Fi on"
|
||||
readonly property string toggleHint: Networking.wifiEnabled ? I18n.tr("Turn Wi-Fi off") : I18n.tr("Turn Wi-Fi on")
|
||||
readonly property var dnsProviders: ["DHCP", "Cloudflare", "Google", "Custom"]
|
||||
property int dnsIndex: 0
|
||||
// ["2.4", "5", ...], or empty when there is nothing to choose between.
|
||||
@@ -159,7 +159,10 @@ Panel {
|
||||
// Under Automatic there is nothing to pick, so the pills collapse away and
|
||||
// the header states the live band instead.
|
||||
readonly property bool bandPillsVisible: canSelectBand && bandPinned
|
||||
readonly property string bandSectionTitle: Model.bandSectionTitle(bandEffective, bandCurrent)
|
||||
readonly property string bandSectionTitle: {
|
||||
var title = Model.bandSectionTitle(bandEffective, bandCurrent)
|
||||
return title.indexOf("WI-FI BAND: ") === 0 ? I18n.tr("WI-FI BAND: %1", title.slice(12)) : I18n.tr(title)
|
||||
}
|
||||
readonly property bool bandBusy: pendingBand !== ""
|
||||
// The speed test needs an interface to test, so its hero action only
|
||||
// appears once there is one.
|
||||
@@ -279,11 +282,12 @@ Panel {
|
||||
}
|
||||
|
||||
function bandLabel(band) {
|
||||
return Model.bandLabel(band)
|
||||
return I18n.tr(Model.bandLabel(band))
|
||||
}
|
||||
|
||||
function bandTooltip(band) {
|
||||
return Model.bandTooltip(band)
|
||||
var tip = Model.bandTooltip(band)
|
||||
return tip.indexOf("Stay on ") === 0 ? I18n.tr("Stay on %1", bandLabel(band)) : I18n.tr(tip)
|
||||
}
|
||||
|
||||
// Single cursor model: exactly one highlighted spot across the whole
|
||||
@@ -662,7 +666,7 @@ Panel {
|
||||
}
|
||||
|
||||
function formatPingLatency(ms) {
|
||||
return Model.formatPingLatency(ms, hasInternetPing)
|
||||
return I18n.tr(Model.formatPingLatency(ms, hasInternetPing))
|
||||
}
|
||||
|
||||
function formatPacketLoss(percent) {
|
||||
@@ -709,7 +713,7 @@ Panel {
|
||||
}
|
||||
|
||||
function wifiSectionTitle(index) {
|
||||
return Model.wifiSectionTitle(wifiNetworks, index)
|
||||
return I18n.tr(Model.wifiSectionTitle(wifiNetworks, index))
|
||||
}
|
||||
|
||||
function wifiIconFor(strength) {
|
||||
@@ -754,7 +758,7 @@ Panel {
|
||||
cancelPasswordPrompt()
|
||||
var connection = ""
|
||||
if (info.type === "wifi") connection = info.ssid || "Wi-Fi"
|
||||
else if (info.type === "ethernet") connection = "Ethernet"
|
||||
else if (info.type === "ethernet") connection = I18n.tr("Ethernet")
|
||||
bar.shell.summon("omarchy.speedtest", connection ? JSON.stringify({ connection: connection }) : "{}")
|
||||
}
|
||||
|
||||
@@ -835,7 +839,7 @@ Panel {
|
||||
if (!network || actionKind === "" || actionSsid !== (network.name || "")) return
|
||||
actionTimeout.stop()
|
||||
failureSsid = actionSsid
|
||||
failureReason = networkFailureReason(reason, requiresCredentials(network.security))
|
||||
failureReason = I18n.tr(networkFailureReason(reason, requiresCredentials(network.security)))
|
||||
actionSsid = ""
|
||||
actionKind = ""
|
||||
refresh()
|
||||
@@ -1062,9 +1066,9 @@ Panel {
|
||||
onTriggered: {
|
||||
if (!root.actionKind) return
|
||||
var reason
|
||||
if (root.actionKind === "connect") reason = "Timed out connecting"
|
||||
else if (root.actionKind === "disconnect") reason = "Timed out disconnecting"
|
||||
else reason = "Timed out forgetting"
|
||||
if (root.actionKind === "connect") reason = I18n.tr("Timed out connecting")
|
||||
else if (root.actionKind === "disconnect") reason = I18n.tr("Timed out disconnecting")
|
||||
else reason = I18n.tr("Timed out forgetting")
|
||||
root.failureSsid = root.actionSsid
|
||||
root.failureReason = reason
|
||||
root.actionSsid = ""
|
||||
@@ -1079,8 +1083,8 @@ Panel {
|
||||
bar: root.bar
|
||||
text: root.icon
|
||||
active: root.restricted
|
||||
tooltipText: root.hasCaptivePortal ? "Sign in to this network"
|
||||
: (root.restricted ? "Limited internet access" : "")
|
||||
tooltipText: root.hasCaptivePortal ? I18n.tr("Sign in to this network")
|
||||
: (root.restricted ? I18n.tr("Limited internet access") : "")
|
||||
|
||||
onPressed: function(b) {
|
||||
if (root.opened) root.close()
|
||||
@@ -1249,7 +1253,7 @@ Panel {
|
||||
id: qrAction
|
||||
visible: root.canShareWifi
|
||||
iconText: ""
|
||||
tooltipText: "Show QR code"
|
||||
tooltipText: I18n.tr("Show QR code")
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
iconSize: Style.font.subtitle * 1.5
|
||||
@@ -1265,7 +1269,7 @@ Panel {
|
||||
id: speedAction
|
||||
visible: root.canRunSpeedTest
|
||||
iconText: ""
|
||||
tooltipText: "Run a speed test"
|
||||
tooltipText: I18n.tr("Run a speed test")
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
iconSize: Style.font.subtitle * 1.5
|
||||
@@ -1316,8 +1320,8 @@ Panel {
|
||||
// SSID even before route/details polling has returned anything.
|
||||
if (root.kind === "wifi" && root.connectedWifiNetwork) return root.connectedWifiNetwork.name || "Wi-Fi"
|
||||
if (root.info.type === "wifi") return root.info.ssid || "Wi-Fi"
|
||||
if (root.info.type === "ethernet") return "Ethernet"
|
||||
return root.info.iface || (root.kind === "disconnected" ? "Disconnected" : "No connection")
|
||||
if (root.info.type === "ethernet") return I18n.tr("Ethernet")
|
||||
return root.info.iface || (root.kind === "disconnected" ? I18n.tr("Disconnected") : I18n.tr("No connection"))
|
||||
}
|
||||
readonly property string detail: root.headerDetail()
|
||||
|
||||
@@ -1334,15 +1338,15 @@ Panel {
|
||||
textFormat: Text.PlainText
|
||||
width: parent.width
|
||||
text: {
|
||||
if (root.hasCaptivePortal) return "SIGN-IN REQUIRED"
|
||||
if (root.restricted) return "LIMITED INTERNET ACCESS"
|
||||
if (root.hasCaptivePortal) return I18n.tr("SIGN-IN REQUIRED")
|
||||
if (root.restricted) return I18n.tr("LIMITED INTERNET ACCESS")
|
||||
if (root.info.type === "wifi") {
|
||||
if (root.canDisconnect) return root.connectionPhrase.toUpperCase()
|
||||
if (root.kind === "disconnected") return "NOT CONNECTED"
|
||||
if (root.kind === "disconnected") return I18n.tr("NOT CONNECTED")
|
||||
return ""
|
||||
}
|
||||
if (root.info.type === "ethernet") return root.connectionPhrase.toUpperCase()
|
||||
if (root.kind === "disconnected") return "NOT CONNECTED"
|
||||
if (root.kind === "disconnected") return I18n.tr("NOT CONNECTED")
|
||||
return ""
|
||||
}
|
||||
visible: text !== ""
|
||||
@@ -1365,7 +1369,7 @@ Panel {
|
||||
Button {
|
||||
id: portalAction
|
||||
width: parent.width
|
||||
text: "Open Captive Portal"
|
||||
text: I18n.tr("Open Captive Portal")
|
||||
iconText: ""
|
||||
foreground: root.bar.urgent
|
||||
accent: root.bar.urgent
|
||||
@@ -1384,7 +1388,7 @@ Panel {
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
text: "Sign in or accept this network’s terms to access the internet."
|
||||
text: I18n.tr("Sign in or accept this network’s terms to access the internet.")
|
||||
textFormat: Text.PlainText
|
||||
wrapMode: Text.WordWrap
|
||||
color: root.bar.foreground
|
||||
@@ -1410,38 +1414,38 @@ Panel {
|
||||
// opened, once the first probe returned, shoving everything below
|
||||
// them down. They now hold their place and read "--" until there is
|
||||
// a sample.
|
||||
InfoLabel { text: "Ping" }
|
||||
InfoLabel { text: I18n.tr("Ping") }
|
||||
DetailValue {
|
||||
text: root.formatPingLatency(root.internetPingLatency)
|
||||
color: root.internetPingPacketLoss > 0 ? root.bar.urgent : root.bar.foreground
|
||||
}
|
||||
InfoLabel { text: "Packet Loss" }
|
||||
InfoLabel { text: I18n.tr("Packet Loss") }
|
||||
DetailValue {
|
||||
text: root.formatPacketLoss(root.internetPingPacketLoss)
|
||||
color: root.internetPingPacketLoss > 0 ? root.bar.urgent : root.bar.foreground
|
||||
}
|
||||
|
||||
InfoLabel { text: "Receiving" }
|
||||
InfoLabel { text: I18n.tr("Receiving") }
|
||||
DetailValue { text: root.hasTransferStats ? root.formatRate(root.downloadRate) : "--" }
|
||||
InfoLabel { text: "Sending" }
|
||||
InfoLabel { text: I18n.tr("Sending") }
|
||||
DetailValue { text: root.hasTransferStats ? root.formatRate(root.uploadRate) : "--" }
|
||||
|
||||
InfoLabel { text: "Downloaded" }
|
||||
InfoLabel { text: I18n.tr("Downloaded") }
|
||||
DetailValue { text: root.hasTransferStats ? root.formatBytes(parseFloat(root.info.rx_bytes || "0")) : "--" }
|
||||
InfoLabel { text: "Uploaded" }
|
||||
InfoLabel { text: I18n.tr("Uploaded") }
|
||||
DetailValue { text: root.hasTransferStats ? root.formatBytes(parseFloat(root.info.tx_bytes || "0")) : "--" }
|
||||
|
||||
InfoLabel { text: "IP Address" }
|
||||
InfoLabel { text: I18n.tr("IP Address") }
|
||||
DetailValue {
|
||||
text: root.info.ip || "--"
|
||||
copyable: !!root.info.ip
|
||||
tooltipText: "Copy IP"
|
||||
tooltipText: I18n.tr("Copy IP")
|
||||
}
|
||||
InfoLabel { text: "Gateway" }
|
||||
InfoLabel { text: I18n.tr("Gateway") }
|
||||
DetailValue {
|
||||
text: root.info.gateway || "--"
|
||||
copyable: !!root.info.gateway
|
||||
tooltipText: "Copy gateway"
|
||||
tooltipText: I18n.tr("Copy gateway")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1482,7 +1486,7 @@ Panel {
|
||||
|
||||
PanelSectionHeader {
|
||||
id: bandAutoLabel
|
||||
text: "AUTOMATIC"
|
||||
text: I18n.tr("AUTOMATIC")
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -1515,8 +1519,8 @@ Panel {
|
||||
PanelToolTip {
|
||||
visible: bandAutoSwitch.containsMouse
|
||||
text: root.bandPinned
|
||||
? "Let Wi-Fi pick the band"
|
||||
: "Stay on " + root.bandLabel(root.bandCurrent)
|
||||
? I18n.tr("Let Wi-Fi pick the band")
|
||||
: I18n.tr("Stay on %1", root.bandLabel(root.bandCurrent))
|
||||
fontFamily: root.bar.fontFamily
|
||||
}
|
||||
}
|
||||
@@ -1587,7 +1591,7 @@ Panel {
|
||||
spacing: Style.space(10)
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "DNS PROVIDER"
|
||||
text: I18n.tr("DNS PROVIDER")
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
}
|
||||
@@ -1603,7 +1607,7 @@ Panel {
|
||||
DnsProviderPill {
|
||||
provider: "DHCP"
|
||||
index: 0
|
||||
tooltipText: "Use DNS from DHCP"
|
||||
tooltipText: I18n.tr("Use DNS from DHCP")
|
||||
width: dnsRow.cellWidth
|
||||
onClicked: root.setDns(provider)
|
||||
}
|
||||
@@ -1611,7 +1615,7 @@ Panel {
|
||||
DnsProviderPill {
|
||||
provider: "Cloudflare"
|
||||
index: 1
|
||||
tooltipText: "Set DNS to Cloudflare"
|
||||
tooltipText: I18n.tr("Set DNS to %1", "Cloudflare")
|
||||
width: dnsRow.cellWidth
|
||||
onClicked: root.setDns(provider)
|
||||
}
|
||||
@@ -1619,7 +1623,7 @@ Panel {
|
||||
DnsProviderPill {
|
||||
provider: "Google"
|
||||
index: 2
|
||||
tooltipText: "Set DNS to Google"
|
||||
tooltipText: I18n.tr("Set DNS to %1", "Google")
|
||||
width: dnsRow.cellWidth
|
||||
onClicked: root.setDns(provider)
|
||||
}
|
||||
@@ -1627,7 +1631,7 @@ Panel {
|
||||
DnsProviderPill {
|
||||
provider: "Custom"
|
||||
index: 3
|
||||
tooltipText: "Set custom DNS servers"
|
||||
tooltipText: I18n.tr("Set custom DNS servers")
|
||||
width: dnsRow.cellWidth
|
||||
onClicked: root.setDns(provider)
|
||||
}
|
||||
@@ -1643,7 +1647,7 @@ Panel {
|
||||
|
||||
PanelSectionHeader {
|
||||
visible: root.wifiStationAvailable && root.scanning
|
||||
text: "SCANNING WI-FI…"
|
||||
text: I18n.tr("SCANNING WI-FI…")
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
}
|
||||
@@ -1746,7 +1750,7 @@ Panel {
|
||||
required property string provider
|
||||
required property int index
|
||||
|
||||
text: provider
|
||||
text: I18n.tr(provider)
|
||||
fontSize: Style.font.bodySmall
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
@@ -1829,12 +1833,12 @@ Panel {
|
||||
readonly property string statusText: {
|
||||
if (!net) return ""
|
||||
if (isPasswordOpen) return ""
|
||||
if (isBusy && root.actionKind === "connect") return "Connecting…"
|
||||
if (isBusy && root.actionKind === "disconnect") return "Disconnecting…"
|
||||
if (isBusy && root.actionKind === "forget") return "Forgetting…"
|
||||
if (isFailed) return root.failureReason || "Failed"
|
||||
if (isConnected && root.kind === "wifi" && root.hasCaptivePortal) return "Sign-in required"
|
||||
if (isConnected) return "Connected"
|
||||
if (isBusy && root.actionKind === "connect") return I18n.tr("Connecting…")
|
||||
if (isBusy && root.actionKind === "disconnect") return I18n.tr("Disconnecting…")
|
||||
if (isBusy && root.actionKind === "forget") return I18n.tr("Forgetting…")
|
||||
if (isFailed) return root.failureReason || I18n.tr("Failed")
|
||||
if (isConnected && root.kind === "wifi" && root.hasCaptivePortal) return I18n.tr("Sign-in required")
|
||||
if (isConnected) return I18n.tr("Connected")
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -1951,7 +1955,7 @@ Panel {
|
||||
|
||||
PanelToolTip {
|
||||
visible: rightMouse.containsMouse || row.forgetFocused
|
||||
text: "Forget network"
|
||||
text: I18n.tr("Forget network")
|
||||
fontFamily: root.bar.fontFamily
|
||||
}
|
||||
}
|
||||
@@ -1967,7 +1971,7 @@ Panel {
|
||||
|
||||
Text {
|
||||
textFormat: Text.PlainText
|
||||
text: row.net ? (row.net.ssid || "Hidden") : ""
|
||||
text: row.net ? (row.net.ssid || I18n.tr("Hidden")) : ""
|
||||
color: root.bar.foreground
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
@@ -2027,7 +2031,7 @@ Panel {
|
||||
anchors.right: connectPwBtn.left
|
||||
anchors.top: parent.top
|
||||
anchors.rightMargin: Style.space(6)
|
||||
placeholderText: "Identity (user@domain)"
|
||||
placeholderText: I18n.tr("Identity (user@domain)")
|
||||
font.family: Style.font.family
|
||||
font.pixelSize: Style.font.body
|
||||
foreground: root.bar.foreground
|
||||
@@ -2053,7 +2057,7 @@ Panel {
|
||||
anchors.bottomMargin: Style.spacing.rowGap / 2
|
||||
anchors.rightMargin: Style.space(6)
|
||||
password: true
|
||||
placeholderText: "Passphrase"
|
||||
placeholderText: I18n.tr("Passphrase")
|
||||
font.family: Style.font.family
|
||||
font.pixelSize: Style.font.body
|
||||
foreground: root.bar.foreground
|
||||
@@ -2086,7 +2090,7 @@ Panel {
|
||||
anchors.fill: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
text: row.isFailed ? "Wrong password" : "Connecting..."
|
||||
text: row.isFailed ? I18n.tr("Wrong password") : I18n.tr("Connecting...")
|
||||
color: row.isFailed ? root.bar.urgent : root.bar.foreground
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
@@ -2103,7 +2107,7 @@ Panel {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
enabled: row.net && pwField.text.length > 0 && (!row.isEnterprise || idField.text.length > 0)
|
||||
iconText: ""
|
||||
tooltipText: "Connect"
|
||||
tooltipText: I18n.tr("Connect")
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
onClicked: row.submitCredentials()
|
||||
@@ -2114,7 +2118,7 @@ Panel {
|
||||
|
||||
component DetailValue: InfoValue {
|
||||
property bool copyable: false
|
||||
property string tooltipText: "Copy to clipboard"
|
||||
property string tooltipText: I18n.tr("Copy to clipboard")
|
||||
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignRight
|
||||
|
||||
@@ -54,7 +54,7 @@ Panel {
|
||||
|
||||
function modeLabel() {
|
||||
var device = UPower.displayDevice
|
||||
return Model.modeLabel(device, root.discharging, upowerStates())
|
||||
return I18n.tr(Model.modeLabel(device, root.discharging, upowerStates()))
|
||||
}
|
||||
|
||||
function profileIcon(name) {
|
||||
@@ -94,26 +94,26 @@ Panel {
|
||||
// Cute agent-flavored phrases shown in the hero status line, rotated on a
|
||||
// timer so the panel feels alive when current is flowing (either direction).
|
||||
readonly property var chargingPhrases: [
|
||||
"Pumping power",
|
||||
"Injecting electrons",
|
||||
"Pouring juice",
|
||||
"Amassing watts",
|
||||
"Hoarding joules",
|
||||
"Sucking volts",
|
||||
"Topping reserves",
|
||||
"Soaking amps",
|
||||
"Inhaling kilowatts"
|
||||
I18n.tr("Pumping power"),
|
||||
I18n.tr("Injecting electrons"),
|
||||
I18n.tr("Pouring juice"),
|
||||
I18n.tr("Amassing watts"),
|
||||
I18n.tr("Hoarding joules"),
|
||||
I18n.tr("Sucking volts"),
|
||||
I18n.tr("Topping reserves"),
|
||||
I18n.tr("Soaking amps"),
|
||||
I18n.tr("Inhaling kilowatts")
|
||||
]
|
||||
readonly property var onBatteryPhrases: [
|
||||
"Slurping power",
|
||||
"Spending joules",
|
||||
"Draining watts",
|
||||
"Burning electrons",
|
||||
"Sipping juice",
|
||||
"Spending coulombs",
|
||||
"Bleeding amps",
|
||||
"Guzzling volts",
|
||||
"Munching reserves"
|
||||
I18n.tr("Slurping power"),
|
||||
I18n.tr("Spending joules"),
|
||||
I18n.tr("Draining watts"),
|
||||
I18n.tr("Burning electrons"),
|
||||
I18n.tr("Sipping juice"),
|
||||
I18n.tr("Spending coulombs"),
|
||||
I18n.tr("Bleeding amps"),
|
||||
I18n.tr("Guzzling volts"),
|
||||
I18n.tr("Munching reserves")
|
||||
]
|
||||
property int phraseIndex: 0
|
||||
|
||||
@@ -127,7 +127,7 @@ Panel {
|
||||
readonly property bool rotatingPhrases: activePhrases.length > 0
|
||||
|
||||
readonly property string heroStatusText: {
|
||||
if (fullyCharged) return "Fully charged"
|
||||
if (fullyCharged) return I18n.tr("Fully charged")
|
||||
if (rotatingPhrases) return activePhrases[phraseIndex % activePhrases.length]
|
||||
return modeLabel()
|
||||
}
|
||||
@@ -346,7 +346,7 @@ Panel {
|
||||
spacing: Style.space(2)
|
||||
|
||||
Text {
|
||||
text: "Battery"
|
||||
text: I18n.tr("Battery")
|
||||
color: root.bar.foreground
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.title
|
||||
@@ -433,20 +433,20 @@ Panel {
|
||||
Column {
|
||||
width: (parent.width - parent.spacing) / 2
|
||||
spacing: Style.spacing.labelGap
|
||||
InfoPair { label: "Battery size"; value: root.batteryInfo.size || "" }
|
||||
InfoPair { label: "Charge cycles"; value: root.batteryInfo.cycles || "—" }
|
||||
InfoPair { label: I18n.tr("Battery size"); value: root.batteryInfo.size || "" }
|
||||
InfoPair { label: I18n.tr("Charge cycles"); value: root.batteryInfo.cycles || "—" }
|
||||
}
|
||||
|
||||
Column {
|
||||
width: (parent.width - parent.spacing) / 2
|
||||
spacing: Style.spacing.labelGap
|
||||
InfoPair {
|
||||
label: root.chargeThresholdActive ? "Charge limit" : (root.discharging ? "Time left" : "Time to full")
|
||||
label: root.chargeThresholdActive ? I18n.tr("Charge limit") : (root.discharging ? I18n.tr("Time left") : I18n.tr("Time to full"))
|
||||
value: root.chargeThresholdActive ? (root.batteryInfo.threshold || "-") : (root.batteryFlowIdle ? "-" : (root.batteryInfo.time || "—"))
|
||||
}
|
||||
InfoPair {
|
||||
label: root.chargeThresholdActive ? "Battery state" : (root.discharging ? "Discharging" : "Charging")
|
||||
value: root.chargeThresholdActive ? "Holding" : (root.batteryFull ? "-" : (root.batteryInfo.rate || ""))
|
||||
label: root.chargeThresholdActive ? I18n.tr("Battery state") : (root.discharging ? I18n.tr("Discharging") : I18n.tr("Charging"))
|
||||
value: root.chargeThresholdActive ? I18n.tr("Holding") : (root.batteryFull ? "-" : (root.batteryInfo.rate || ""))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -461,7 +461,7 @@ Panel {
|
||||
spacing: Style.space(10)
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "POWER PROFILE"
|
||||
text: I18n.tr("POWER PROFILE")
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ Item {
|
||||
}
|
||||
|
||||
if (!root.expectedStop && exitCode !== 0) {
|
||||
root.error = root.stderrText || "Speed test failed"
|
||||
root.error = root.stderrText || I18n.tr("Speed test failed")
|
||||
root.phase = ""
|
||||
root.running = false
|
||||
return
|
||||
@@ -177,7 +177,7 @@ Item {
|
||||
onStreamFinished: {
|
||||
var fields = String(text || "").trim().split("\t")
|
||||
if (fields[0] === "wifi") root.connectionName = fields[1] || "Wi-Fi"
|
||||
else if (fields[0] === "ethernet") root.connectionName = "Ethernet"
|
||||
else if (fields[0] === "ethernet") root.connectionName = I18n.tr("Ethernet")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,9 +186,9 @@ Item {
|
||||
fontFamily: Style.font.family
|
||||
layerNamespace: "omarchy-network-speedtest"
|
||||
title: root.connectionName
|
||||
leftLabel: "DOWNLOAD"
|
||||
rightLabel: "UPLOAD"
|
||||
runAgainTooltip: "Measure again via fast.com"
|
||||
leftLabel: I18n.tr("DOWNLOAD")
|
||||
rightLabel: I18n.tr("UPLOAD")
|
||||
runAgainTooltip: I18n.tr("Measure again via fast.com")
|
||||
running: root.running
|
||||
leftValue: root.downloadValue
|
||||
rightValue: root.uploadValue
|
||||
|
||||
@@ -24,16 +24,16 @@ Panel {
|
||||
property string mullvadQuery: ""
|
||||
property int phraseIndex: 0
|
||||
readonly property var activePhrases: [
|
||||
"Encrypting connections",
|
||||
"Sending secrets",
|
||||
"Guarding wires",
|
||||
"Braiding packets",
|
||||
"Polishing tunnels",
|
||||
"Hiding routes",
|
||||
"Sealing ports",
|
||||
"Sorting tailnets",
|
||||
"Shuffling keys",
|
||||
"Watching machines"
|
||||
I18n.tr("Encrypting connections"),
|
||||
I18n.tr("Sending secrets"),
|
||||
I18n.tr("Guarding wires"),
|
||||
I18n.tr("Braiding packets"),
|
||||
I18n.tr("Polishing tunnels"),
|
||||
I18n.tr("Hiding routes"),
|
||||
I18n.tr("Sealing ports"),
|
||||
I18n.tr("Sorting tailnets"),
|
||||
I18n.tr("Shuffling keys"),
|
||||
I18n.tr("Watching machines")
|
||||
]
|
||||
readonly property string heroPhraseText: activePhrases[phraseIndex % activePhrases.length]
|
||||
|
||||
@@ -52,7 +52,7 @@ Panel {
|
||||
// "header" stays navigable, but an absent CLI leaves nothing to highlight.
|
||||
readonly property bool headerHasCursor: cursorActive && focusSection === "header" && tailscale.installed
|
||||
readonly property color iconColor: tailscale.active ? foreground : dim
|
||||
readonly property string toggleHint: tailscale.active ? "Turn Tailscale off" : (tailscale.needsLogin ? "Authorize this device" : "Turn Tailscale on")
|
||||
readonly property string toggleHint: tailscale.active ? I18n.tr("Turn Tailscale off") : (tailscale.needsLogin ? I18n.tr("Authorize this device") : I18n.tr("Turn Tailscale on"))
|
||||
readonly property color barIconColor: tailscale.active ? barForeground : Qt.darker(barForeground, 1.55)
|
||||
readonly property color hoverFill: bar ? Style.hoverFillFor(bar.foreground, Color.accent) : "transparent"
|
||||
readonly property color selectedFill: bar ? Style.selectedFillFor(bar.foreground, Color.accent) : "transparent"
|
||||
@@ -76,7 +76,7 @@ Panel {
|
||||
var nodes = []
|
||||
for (var i = 0; i < tailscale.tailnetExitNodes.length; i++) nodes.push(tailscale.tailnetExitNodes[i])
|
||||
for (var j = 0; j < recentMullvadExitNodes.length; j++) nodes.push(recentMullvadExitNodes[j])
|
||||
if (tailscale.mullvadRegions.length > 0) nodes.push({ id: "mullvad:add", AddMullvad: true, DisplayName: "Choose Mullvad region" })
|
||||
if (tailscale.mullvadRegions.length > 0) nodes.push({ id: "mullvad:add", AddMullvad: true, DisplayName: I18n.tr("Choose Mullvad region") })
|
||||
return nodes
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ Panel {
|
||||
}
|
||||
|
||||
function mullvadRegionTitle(peer) {
|
||||
if (!peer) return "Unknown"
|
||||
if (!peer) return I18n.tr("Unknown")
|
||||
var city = String(peer.City || "").trim()
|
||||
var country = String(peer.Country || "").trim()
|
||||
if (city === "" || city === "Any") return country || String(peer.DisplayName || "Unknown")
|
||||
@@ -458,7 +458,7 @@ Panel {
|
||||
id: hero
|
||||
width: parent.width
|
||||
title: tailscale.installed ? (tailscale.selfName || "Tailscale") : "Tailscale"
|
||||
meta: tailscale.active ? root.heroPhraseText : "Tailscale is disconnected"
|
||||
meta: tailscale.active ? root.heroPhraseText : I18n.tr("Tailscale is disconnected")
|
||||
foreground: root.foreground
|
||||
fontFamily: root.fontFamily
|
||||
iconOpacity: tailscale.active ? 1.0 : 0.5
|
||||
@@ -501,7 +501,7 @@ Panel {
|
||||
textFormat: Text.PlainText
|
||||
visible: tailscale.actionStatus !== "" || tailscale.lastError !== ""
|
||||
width: parent.width
|
||||
text: tailscale.actionStatus !== "" ? tailscale.actionStatus : tailscale.lastError
|
||||
text: I18n.tr(tailscale.actionStatus !== "" ? tailscale.actionStatus : tailscale.lastError)
|
||||
color: tailscale.lastError !== "" && tailscale.actionStatus === "" ? root.urgent : root.dim
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
@@ -520,7 +520,7 @@ Panel {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.margins: Style.space(12)
|
||||
text: "Tailscale CLI is not installed or not on PATH."
|
||||
text: I18n.tr("Tailscale CLI is not installed or not on PATH.")
|
||||
color: root.dim
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
@@ -539,7 +539,7 @@ Panel {
|
||||
spacing: Style.space(10)
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "CONNECTIONS"
|
||||
text: I18n.tr("CONNECTIONS")
|
||||
foreground: root.foreground
|
||||
fontFamily: root.fontFamily
|
||||
}
|
||||
@@ -572,7 +572,7 @@ Panel {
|
||||
spacing: Style.space(10)
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "EXIT NODES"
|
||||
text: I18n.tr("EXIT NODES")
|
||||
foreground: root.foreground
|
||||
fontFamily: root.fontFamily
|
||||
}
|
||||
@@ -602,7 +602,7 @@ Panel {
|
||||
id: mullvadSearch
|
||||
width: parent.width
|
||||
foreground: root.foreground
|
||||
placeholderText: "Search regions"
|
||||
placeholderText: I18n.tr("Search regions")
|
||||
text: root.mullvadQuery
|
||||
onTextChanged: {
|
||||
root.mullvadQuery = text
|
||||
@@ -638,7 +638,7 @@ Panel {
|
||||
Text {
|
||||
visible: root.filteredMullvadRegions.length === 0
|
||||
width: parent.width
|
||||
text: "No Mullvad regions found."
|
||||
text: I18n.tr("No Mullvad regions found.")
|
||||
color: root.dim
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
@@ -676,7 +676,7 @@ Panel {
|
||||
spacing: Style.space(10)
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "MACHINES"
|
||||
text: I18n.tr("MACHINES")
|
||||
foreground: root.foreground
|
||||
fontFamily: root.fontFamily
|
||||
}
|
||||
@@ -684,7 +684,7 @@ Panel {
|
||||
Text {
|
||||
visible: tailscale.installed && tailscale.active && tailscale.peers.length === 0
|
||||
width: parent.width
|
||||
text: "No machines found on this tailnet."
|
||||
text: I18n.tr("No machines found on this tailnet.")
|
||||
color: root.dim
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
@@ -777,7 +777,7 @@ Panel {
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: "Authorize Tailscale operator"
|
||||
text: I18n.tr("Authorize Tailscale operator")
|
||||
color: root.foreground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
@@ -786,7 +786,7 @@ Panel {
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: "Allow this user to operate this Tailscale profile"
|
||||
text: I18n.tr("Allow this user to operate this Tailscale profile")
|
||||
color: root.dim
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.caption
|
||||
@@ -803,7 +803,7 @@ Panel {
|
||||
property int rowIndex: 0
|
||||
readonly property bool selectedAccount: account && account.selected === true
|
||||
readonly property bool switchingAccount: account && tailscale.switchingAccountId === String(account.id || "")
|
||||
readonly property string accountText: account ? tailscale.accountLabel(account) : "Account"
|
||||
readonly property string accountText: account ? tailscale.accountLabel(account) : I18n.tr("Account")
|
||||
|
||||
hasCursor: root.cursorActive && root.focusSection === "accounts" && root.accountIndex === rowIndex
|
||||
current: selectedAccount
|
||||
@@ -867,7 +867,7 @@ Panel {
|
||||
id: peerRow
|
||||
property var peer: null
|
||||
property int rowIndex: 0
|
||||
readonly property string peerName: peer ? String(peer.DisplayName || peer.HostName || "Unknown") : "Unknown"
|
||||
readonly property string peerName: peer ? String(peer.DisplayName || peer.HostName || I18n.tr("Unknown")) : I18n.tr("Unknown")
|
||||
readonly property string peerIp: peer && peer.TailscaleIPs && peer.TailscaleIPs.length > 0 ? String(peer.TailscaleIPs[0]) : ""
|
||||
readonly property string peerIpv6: {
|
||||
if (!peer || !peer.TailscaleIPv6 || peer.TailscaleIPv6.length === 0) return ""
|
||||
@@ -978,7 +978,7 @@ Panel {
|
||||
id: sendButton
|
||||
visible: tailscale.canSendFiles(peerRow.peer)
|
||||
iconText: ""
|
||||
tooltipText: "Send files"
|
||||
tooltipText: I18n.tr("Send files")
|
||||
foreground: root.foreground
|
||||
fontFamily: root.fontFamily
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
@@ -1118,8 +1118,8 @@ Panel {
|
||||
readonly property bool addMullvad: peer && peer.AddMullvad === true
|
||||
readonly property bool activeExitNode: peer && peer.ExitNode === true
|
||||
readonly property bool settingExitNode: peer && tailscale.settingExitNodeId === String(peer.id || "")
|
||||
readonly property string peerName: peer ? String(peer.DisplayName || peer.HostName || "Unknown") : "Unknown"
|
||||
readonly property string actionTooltip: addMullvad ? "" : (activeExitNode ? "Disconnect" : "Connect")
|
||||
readonly property string peerName: peer ? String(peer.DisplayName || peer.HostName || I18n.tr("Unknown")) : I18n.tr("Unknown")
|
||||
readonly property string actionTooltip: addMullvad ? "" : (activeExitNode ? I18n.tr("Disconnect") : I18n.tr("Connect"))
|
||||
|
||||
hasCursor: root.cursorActive && root.focusSection === "exitNodes" && root.exitNodeIndex === rowIndex
|
||||
current: activeExitNode || settingExitNode || (addMullvad && root.mullvadPickerOpen)
|
||||
@@ -1199,7 +1199,7 @@ Panel {
|
||||
readonly property bool activeExitNode: peer && peer.ExitNode === true
|
||||
readonly property bool settingExitNode: peer && tailscale.settingExitNodeId === String(peer.id || "")
|
||||
readonly property bool selectedRegion: root.mullvadPickerOpen && root.mullvadRegionIndex === rowIndex
|
||||
readonly property string actionTooltip: activeExitNode ? "Disconnect" : "Connect"
|
||||
readonly property string actionTooltip: activeExitNode ? I18n.tr("Disconnect") : I18n.tr("Connect")
|
||||
|
||||
foreground: root.foreground
|
||||
fill: root.hoverFill
|
||||
|
||||
@@ -616,7 +616,7 @@ Panel {
|
||||
id: locationField
|
||||
width: Style.space(190)
|
||||
enabled: !root.savingLocation
|
||||
placeholderText: "Search city"
|
||||
placeholderText: I18n.tr("Search city")
|
||||
foreground: root.bar.foreground
|
||||
font.family: root.bar.fontFamily
|
||||
|
||||
@@ -683,7 +683,7 @@ Panel {
|
||||
Column {
|
||||
spacing: Style.space(5)
|
||||
Text {
|
||||
text: "FEELS"
|
||||
text: I18n.tr("FEELS")
|
||||
color: Qt.darker(root.bar.foreground, 1.5)
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
@@ -701,7 +701,7 @@ Panel {
|
||||
Column {
|
||||
spacing: Style.space(5)
|
||||
Text {
|
||||
text: "WIND"
|
||||
text: I18n.tr("WIND")
|
||||
color: Qt.darker(root.bar.foreground, 1.5)
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
@@ -719,7 +719,7 @@ Panel {
|
||||
Column {
|
||||
spacing: Style.space(5)
|
||||
Text {
|
||||
text: "HUMID"
|
||||
text: I18n.tr("HUMID")
|
||||
color: Qt.darker(root.bar.foreground, 1.5)
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
@@ -792,7 +792,7 @@ Panel {
|
||||
|
||||
Text {
|
||||
visible: !root.current
|
||||
text: "Fetching forecast…"
|
||||
text: I18n.tr("Fetching forecast…")
|
||||
color: Qt.darker(root.bar.foreground, 1.5)
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
|
||||
@@ -183,7 +183,7 @@ Item {
|
||||
if (exitCode !== 0 || root.qrSize === 0) {
|
||||
root.qrSize = 0
|
||||
root.qrRows = []
|
||||
if (root.error === "") root.error = "Could not generate the Wi-Fi QR code"
|
||||
if (root.error === "") root.error = I18n.tr("Could not generate the Wi-Fi QR code")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -206,7 +206,7 @@ Item {
|
||||
if (root.pwExpectedStop) return
|
||||
if (!root.opened) return
|
||||
if (exitCode === 0 && root.password !== "") root.passwordVisible = true
|
||||
else root.passwordError = "Could not read the Wi-Fi password"
|
||||
else root.passwordError = I18n.tr("Could not read the Wi-Fi password")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,7 +305,7 @@ Item {
|
||||
|
||||
Text {
|
||||
visible: root.loading
|
||||
text: "Generating QR code…"
|
||||
text: I18n.tr("Generating QR code…")
|
||||
color: root.onScrimDim
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
@@ -328,7 +328,7 @@ Item {
|
||||
|
||||
Text {
|
||||
visible: root.showingQr
|
||||
text: "Scan to join this network"
|
||||
text: I18n.tr("Scan to join this network")
|
||||
color: root.onScrimDim
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
@@ -341,7 +341,7 @@ Item {
|
||||
visible: root.showingQr && root.secured
|
||||
text: root.passwordError !== "" ? root.passwordError
|
||||
: root.passwordVisible ? root.password
|
||||
: "Show password"
|
||||
: I18n.tr("Show password")
|
||||
color: root.passwordError !== "" ? root.onScrimUrgent : root.onScrim
|
||||
opacity: root.passwordVisible || root.passwordError !== "" ? 1 : 0.6
|
||||
font.family: root.fontFamily
|
||||
|
||||
@@ -78,7 +78,7 @@ Item {
|
||||
var flow = polkitAgent.flow
|
||||
if (!flow) return
|
||||
|
||||
currentMessage = String(flow.message || "Authentication is needed...")
|
||||
currentMessage = String(flow.message || I18n.tr("Authentication is needed..."))
|
||||
currentPrompt = String(flow.inputPrompt || "")
|
||||
currentSupplementary = String(flow.supplementaryMessage || "")
|
||||
responseRequired = !!flow.isResponseRequired
|
||||
@@ -336,7 +336,7 @@ Item {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: root.errorFlash ? "Wrong" : (root.submitted ? "Checking..." : "Enter password")
|
||||
text: root.errorFlash ? I18n.tr("Wrong") : (root.submitted ? I18n.tr("Checking...") : I18n.tr("Enter password"))
|
||||
color: root.errorFlash ? Color.polkit.textError : root.foreground
|
||||
opacity: root.errorFlash ? 1 : 0.36
|
||||
font.family: root.fontFamily
|
||||
|
||||
@@ -28,7 +28,7 @@ Item {
|
||||
property int headerHeight: Math.max(Style.space(34), Style.font.title + Style.spacing.controlPaddingY * 2)
|
||||
property int cardWidth: Math.min(Style.space(300), panel.width - Style.gapsOut * 2)
|
||||
property int cardHeight: Math.min(contentMargin * 2 + headerHeight, panel.height - Style.gapsOut * 2)
|
||||
readonly property string promptText: root.step === "message" ? "Reminder message" : "Remind in minutes"
|
||||
readonly property string promptText: root.step === "message" ? I18n.tr("Reminder message") : I18n.tr("Remind in minutes")
|
||||
|
||||
function open(payloadJson) {
|
||||
var payload = ({})
|
||||
|
||||
@@ -411,7 +411,7 @@ Item {
|
||||
handled = true
|
||||
}
|
||||
} else if (action === "playPause") {
|
||||
actionLabel = player && player.isPlaying ? "Pause" : "Play"
|
||||
actionLabel = player && player.isPlaying ? I18n.tr("Pause") : I18n.tr("Play")
|
||||
iconName = player && player.isPlaying ? "media-pause" : "media-play"
|
||||
if (player && player.isPlaying && player.canPause) {
|
||||
player.pause()
|
||||
|
||||
@@ -225,7 +225,7 @@ assert(/readonly property int lifeExpectancy: Model\.parseLifeExpectancy\(settin
|
||||
assert(/id: expectancyField/.test(panelSource) && /id: bornField/.test(panelSource), 'calendar offers both inputs')
|
||||
assert(/visible: root\.editingLife\s*\n\s*anchors\.horizontalCenter: parent\.horizontalCenter/.test(panelSource), 'calendar centers the inputs over the bar they replace')
|
||||
assert(/visible: root\.birthYear > 0/.test(panelSource), 'calendar hides the life bar until a birth year is known')
|
||||
assert(/text: "LIFE"/.test(panelSource) && /root\.lifeDone/.test(panelSource), 'calendar shows the life bar')
|
||||
assert(/text: I18n\.tr\("LIFE"\)/.test(panelSource) && /root\.lifeDone/.test(panelSource), 'calendar shows the life bar')
|
||||
assert(/text: "Memento Mori"/.test(panelSource), 'calendar names the life bar on hover')
|
||||
assert(/onDoubleTapped: root\.clearLife\(\)/.test(panelSource), 'calendar puts the life bar away when it is double-tapped')
|
||||
assert(/persistSettings\(\{ birthYear: 0 \}\)/.test(panelSource), 'calendar clears the birth year to hide the life bar')
|
||||
|
||||
@@ -39,6 +39,7 @@ console.log('ok - Chinese labels replace only the label, and user extensions sti
|
||||
JS
|
||||
|
||||
grep -qF 'root.localeMenuItems.concat(root.userMenuItems)' "$ROOT/shell/plugins/menu/Menu.qml" &&
|
||||
grep -qF 'startsWith("zh_CN") ? omarchyPath + "/default/omarchy/omarchy-menu.zh-cn.jsonc"' "$ROOT/shell/plugins/menu/Menu.qml" ||
|
||||
grep -qF 'I18n.chinese ? omarchyPath + "/default/omarchy/omarchy-menu.zh-cn.jsonc"' "$ROOT/shell/plugins/menu/Menu.qml" &&
|
||||
grep -qF 'var chinese = Qt.locale().name.indexOf("zh_CN") === 0' "$ROOT/shell/Commons/Tr.js" ||
|
||||
fail "the menu loads the Chinese overlay under zh_CN, beneath the user extension"
|
||||
pass "the menu loads the Chinese overlay under zh_CN, beneath the user extension"
|
||||
@@ -137,7 +137,7 @@ assertEqual(
|
||||
// belongs in the logic module, where the strip runs after it.
|
||||
const cardQml = fs.readFileSync(path.join(root, 'shell/plugins/notifications/components/NotificationCard.qml'), 'utf8')
|
||||
assert(
|
||||
/readonly property string styledBody: NotificationLogic\.styledBody\(body, app, appIcon\)/.test(cardQml),
|
||||
/readonly property string styledBody: NotificationLogic\.styledBody\(I18n\.tr\(body\), app, appIcon\)/.test(cardQml),
|
||||
'the notification card renders the body that was stripped after the newline rewrite'
|
||||
)
|
||||
assert(
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||
|
||||
run_node_test <<'JS'
|
||||
const fs = require('fs')
|
||||
const vm = require('vm')
|
||||
|
||||
const source = fs.readFileSync(path.join(root, 'shell/Commons/I18nZh.js'), 'utf8').replace('.pragma library', '')
|
||||
const context = {}
|
||||
vm.runInNewContext(source + '\nthis.table = table', context)
|
||||
const table = context.table
|
||||
|
||||
const files = []
|
||||
const walk = dir => {
|
||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name)
|
||||
if (entry.isDirectory()) walk(full)
|
||||
else if (/\.(qml|js)$/.test(entry.name)) files.push(full)
|
||||
}
|
||||
}
|
||||
walk(path.join(root, 'shell'))
|
||||
|
||||
const missing = []
|
||||
let calls = 0
|
||||
for (const file of files) {
|
||||
const text = fs.readFileSync(file, 'utf8')
|
||||
for (const match of text.matchAll(/I18n\.tr\(\s*"((?:[^"\\]|\\.)*)"/g)) {
|
||||
calls++
|
||||
const key = JSON.parse('"' + match[1] + '"')
|
||||
if (!(key in table)) missing.push(`${path.relative(root, file)}: ${key}`)
|
||||
}
|
||||
}
|
||||
assert(calls > 300, 'the shell routes its text through I18n.tr', `found ${calls} calls`)
|
||||
assert(missing.length === 0, 'every I18n.tr text has a Chinese entry', missing.join('\n'))
|
||||
|
||||
const placeholders = s => (s.match(/%\d/g) || []).sort().join(',')
|
||||
const mismatched = Object.keys(table).filter(key => placeholders(key) !== placeholders(table[key]))
|
||||
assert(mismatched.length === 0, 'every Chinese entry keeps its English placeholders', mismatched.join('\n'))
|
||||
JS
|
||||
Reference in new issue
Block a user