Add Simplified Chinese as a system language
The installer asks for the language before the keyboard, and Setup > Language (omarchy language en|zh) switches it later. Under zh_CN the menu reads its labels from omarchy-menu.zh-cn.jsonc, the shell from I18nZh.js through I18n.tr, and notifications are worded by their sender through omarchy-text, which reads the same table. User text is never translated.
This commit is contained in:
138 files changed
+1986
-558
No files matched your search
@@ -200,7 +200,7 @@ Panel {
|
||||
readonly property bool hasOutput: !!(volumeSink && volumeSink.audio)
|
||||
readonly property bool hasInput: !!(source && source.audio) || (inputViaWpctl && UntypedInput.known)
|
||||
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, Commons.Color.accent)
|
||||
@@ -458,7 +458,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) {
|
||||
@@ -813,7 +813,8 @@ Panel {
|
||||
spacing: Style.space(2)
|
||||
|
||||
Text {
|
||||
text: "Audio"
|
||||
textFormat: Text.PlainText
|
||||
text: I18n.tr("Audio")
|
||||
color: root.bar.foreground
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.title
|
||||
@@ -855,7 +856,7 @@ Panel {
|
||||
|
||||
PanelSectionHeader {
|
||||
id: outputHeader
|
||||
text: "OUTPUT"
|
||||
text: I18n.tr("OUTPUT")
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
anchors.left: parent.left
|
||||
@@ -942,7 +943,7 @@ Panel {
|
||||
|
||||
PanelSectionHeader {
|
||||
id: microphoneHeader
|
||||
text: "INPUT"
|
||||
text: I18n.tr("INPUT")
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
anchors.left: parent.left
|
||||
@@ -1050,7 +1051,7 @@ Panel {
|
||||
visible: root.displayAudioStreams.length > 0
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "SOURCES"
|
||||
text: I18n.tr("SOURCES")
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
}
|
||||
|
||||
@@ -65,19 +65,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]
|
||||
}
|
||||
|
||||
@@ -102,7 +102,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, Commons.Color.accent)
|
||||
@@ -184,7 +184,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() {
|
||||
@@ -739,7 +739,8 @@ Panel {
|
||||
spacing: Style.space(2)
|
||||
|
||||
Text {
|
||||
text: "Bluetooth"
|
||||
textFormat: Text.PlainText
|
||||
text: I18n.tr("Bluetooth")
|
||||
color: root.bar.foreground
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.title
|
||||
@@ -776,7 +777,7 @@ Panel {
|
||||
spacing: Style.space(10)
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "CONNECTED"
|
||||
text: I18n.tr("CONNECTED")
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
}
|
||||
@@ -868,9 +869,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
|
||||
@@ -895,9 +896,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
|
||||
@@ -912,13 +913,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 ""
|
||||
}
|
||||
@@ -995,7 +996,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
|
||||
@@ -1020,7 +1021,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()
|
||||
|
||||
@@ -67,10 +67,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)
|
||||
@@ -318,7 +316,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, Commons.Color.accent)
|
||||
: root.contentForeground
|
||||
@@ -341,7 +339,7 @@ Panel {
|
||||
|
||||
PanelToolTip {
|
||||
visible: heroMouse.containsMouse
|
||||
text: "Back to today"
|
||||
text: I18n.tr("Back to today")
|
||||
fontFamily: root.contentFontFamily
|
||||
}
|
||||
}
|
||||
@@ -374,7 +372,8 @@ Panel {
|
||||
|
||||
Text {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "BORN"
|
||||
textFormat: Text.PlainText
|
||||
text: I18n.tr("BORN")
|
||||
color: Qt.darker(root.contentForeground, 1.5)
|
||||
font.family: root.contentFontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
@@ -385,7 +384,7 @@ Panel {
|
||||
id: bornField
|
||||
width: Style.space(70)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
placeholderText: "year"
|
||||
placeholderText: I18n.tr("year")
|
||||
foreground: root.contentForeground
|
||||
font.family: root.contentFontFamily
|
||||
inputMethodHints: Qt.ImhDigitsOnly
|
||||
@@ -397,7 +396,8 @@ Panel {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: 0
|
||||
leftPadding: Style.space(6)
|
||||
text: "LIVE TO"
|
||||
textFormat: Text.PlainText
|
||||
text: I18n.tr("LIVE TO")
|
||||
color: Qt.darker(root.contentForeground, 1.5)
|
||||
font.family: root.contentFontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
@@ -484,7 +484,8 @@ Panel {
|
||||
id: lifeLabel
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "LIFE"
|
||||
textFormat: Text.PlainText
|
||||
text: I18n.tr("LIFE")
|
||||
color: Qt.darker(root.contentForeground, 1.5)
|
||||
font.family: root.contentFontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
@@ -603,7 +604,7 @@ Panel {
|
||||
|
||||
PanelToolTip {
|
||||
visible: weekStartMouse.containsMouse
|
||||
text: "Start weeks on " + root.nextWeekStartLabel
|
||||
text: I18n.tr("Start weeks on %1", root.nextWeekStartLabel)
|
||||
fontFamily: root.contentFontFamily
|
||||
}
|
||||
}
|
||||
@@ -726,7 +727,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
|
||||
@@ -740,7 +741,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)
|
||||
@@ -751,7 +752,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)
|
||||
|
||||
@@ -21,16 +21,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 : Commons.Color.foreground
|
||||
@@ -38,7 +38,14 @@ 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"
|
||||
// Model.fileMeta reads "5m ago · Folder"; the age is translated, the folder name kept.
|
||||
function fileMetaText(file) {
|
||||
var parts = Model.fileMeta(file).split(" · ")
|
||||
parts[0] = I18n.trMatch(parts[0], ["Just now", "Unknown time", "%1mo ago", "%1m ago", "%1h ago", "%1d ago", "%1y ago"])
|
||||
return parts.join(" · ")
|
||||
}
|
||||
|
||||
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.
|
||||
@@ -247,7 +254,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
|
||||
@@ -287,7 +294,7 @@ Panel {
|
||||
textFormat: Text.PlainText
|
||||
visible: dropbox.actionStatus !== "" || dropbox.lastError !== ""
|
||||
width: parent.width
|
||||
text: dropbox.actionStatus !== "" ? dropbox.actionStatus : dropbox.lastError
|
||||
text: I18n.tr(dropbox.actionStatus !== "" ? dropbox.actionStatus : dropbox.lastError)
|
||||
color: dropbox.lastError !== "" && dropbox.actionStatus === "" ? root.urgent : root.dim
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
@@ -307,7 +314,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: I18n.trMatch(Model.usageText(dropbox.usedBytes, dropbox.quotaBytes, dropbox.quotaKnown), ["%1 of %2"]) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,7 +329,7 @@ Panel {
|
||||
spacing: Style.space(10)
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "RECENT FILES"
|
||||
text: I18n.tr("RECENT FILES")
|
||||
foreground: root.foreground
|
||||
fontFamily: root.fontFamily
|
||||
}
|
||||
@@ -330,7 +337,8 @@ Panel {
|
||||
Text {
|
||||
visible: dropbox.files.length === 0
|
||||
width: parent.width
|
||||
text: "No synced files found."
|
||||
textFormat: Text.PlainText
|
||||
text: I18n.tr("No synced files found.")
|
||||
color: root.dim
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
@@ -427,7 +435,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
|
||||
@@ -437,7 +445,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
|
||||
@@ -460,7 +468,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
|
||||
@@ -510,7 +518,7 @@ Panel {
|
||||
Text {
|
||||
textFormat: Text.PlainText
|
||||
Layout.fillWidth: true
|
||||
text: Model.fileMeta(fileRow.file)
|
||||
text: root.fileMetaText(fileRow.file)
|
||||
color: root.dim
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.caption
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,16 @@ import "Greetings.js" as Greet
|
||||
Rectangle {
|
||||
id: row
|
||||
|
||||
// Model.formatDate reads "Tue Oct 6"; a Chinese session reads 10月6日 周二.
|
||||
function dateText(date) {
|
||||
var match = String(date).match(/^(\w{3}) (\w{3}) (\d+)$/)
|
||||
if (!I18n.chinese || !match) return date
|
||||
var month = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"].indexOf(match[2]) + 1
|
||||
var weekday = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].indexOf(match[1])
|
||||
if (month < 1 || weekday < 0) return date
|
||||
return month + "月" + match[3] + "日 " + ["周日", "周一", "周二", "周三", "周四", "周五", "周六"][weekday]
|
||||
}
|
||||
|
||||
required property var modelData
|
||||
required property int index
|
||||
// The owning Panel: the row reads its clock, facts and drag state.
|
||||
@@ -223,7 +233,7 @@ Rectangle {
|
||||
Behavior on opacity { NumberAnimation { duration: Style.duration(110) } }
|
||||
|
||||
Caption {
|
||||
text: row.ready ? row.rowData.date : "\u2026"
|
||||
text: row.ready ? row.dateText(row.rowData.date) : "\u2026"
|
||||
color: row.ready && row.rowData.dayLabel !== "" ? row.dim : row.fainter
|
||||
}
|
||||
|
||||
@@ -234,7 +244,7 @@ Rectangle {
|
||||
}
|
||||
|
||||
Caption {
|
||||
text: row.ready ? row.rowData.dayLabel : ""
|
||||
text: row.ready ? I18n.trMatch(row.rowData.dayLabel, ["Tomorrow", "Yesterday", "+%1 days", "%1 days"]) : ""
|
||||
visible: text !== ""
|
||||
}
|
||||
}
|
||||
@@ -301,7 +311,7 @@ Rectangle {
|
||||
color: Model.mix(Commons.Color.popups.background, row.foreground, _hot ? 0.24 : 0.14)
|
||||
borderSpec: Border.flat(Model.mix(Commons.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,9 +17,9 @@ Column {
|
||||
// Zone id -> offset text; read inside a binding, so its dependencies track.
|
||||
property var offsetLabel: function(zoneId) { return "" }
|
||||
property bool loading: false
|
||||
property string buttonText: "+ Add a city"
|
||||
property string placeholderText: "Search cities\u2026"
|
||||
property string loadingText: "Loading zones\u2026"
|
||||
property string buttonText: "+ " + I18n.tr("Add a city")
|
||||
property string placeholderText: I18n.tr("Search cities…")
|
||||
property string loadingText: I18n.tr("Loading zones…")
|
||||
property real fontSize: Style.font.bodySmall
|
||||
property color foreground: Commons.Color.foreground
|
||||
property color dim: Qt.darker(foreground, 1.55)
|
||||
|
||||
@@ -66,8 +66,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
|
||||
}
|
||||
|
||||
|
||||
@@ -585,8 +585,8 @@ Item {
|
||||
limit: 5
|
||||
inlineResults: false
|
||||
loading: root.jumpOptions.length === 0
|
||||
buttonText: "Jump to a city"
|
||||
loadingText: "Loading cities\u2026"
|
||||
buttonText: I18n.tr("Jump to a city")
|
||||
loadingText: I18n.tr("Loading cities…")
|
||||
offsetLabel: function(zoneId) { return Model.utcOffsetLabel(root.offsets[zoneId]) }
|
||||
foreground: root.foreground
|
||||
dim: root.dim
|
||||
|
||||
@@ -73,7 +73,7 @@ Column {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: Style.space(9)
|
||||
|
||||
TitleWord { text: "World" }
|
||||
TitleWord { text: I18n.tr("World") }
|
||||
|
||||
Item {
|
||||
id: heroIcon
|
||||
@@ -151,7 +151,7 @@ Column {
|
||||
}
|
||||
}
|
||||
|
||||
TitleWord { text: "Clock" }
|
||||
TitleWord { text: I18n.tr("Clock") }
|
||||
}
|
||||
|
||||
component TitleWord: Text {
|
||||
|
||||
@@ -335,8 +335,8 @@ Panel {
|
||||
}
|
||||
|
||||
// No full stop: a period hanging off the arc reads as a speck.
|
||||
readonly property string hereLine: "It's " + localTime + " here"
|
||||
+ (homeCity !== "" ? " in " + homeCity : "")
|
||||
readonly property string hereLine: I18n.tr("It's %1 here", localTime)
|
||||
+ (homeCity !== "" ? I18n.tr(" in %1", homeCity) : "")
|
||||
+ (scrubLabel !== "" ? " " + scrubLabel : "")
|
||||
|
||||
// A capital's line box has empty space above it; the row's top pad drops by it.
|
||||
@@ -715,7 +715,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()
|
||||
|
||||
@@ -556,7 +556,8 @@ Panel {
|
||||
spacing: Style.space(2)
|
||||
|
||||
Text {
|
||||
text: "Display"
|
||||
textFormat: Text.PlainText
|
||||
text: I18n.tr("Display")
|
||||
color: root.bar.foreground
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.title
|
||||
@@ -570,9 +571,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
|
||||
@@ -602,7 +603,7 @@ Panel {
|
||||
|
||||
PanelSectionHeader {
|
||||
id: brightnessHeader
|
||||
text: "BRIGHTNESS"
|
||||
text: I18n.tr("BRIGHTNESS")
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
anchors.left: parent.left
|
||||
@@ -675,7 +676,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
|
||||
@@ -747,7 +748,7 @@ Panel {
|
||||
|
||||
PanelSectionHeader {
|
||||
id: scaleHeader
|
||||
text: "SCALE"
|
||||
text: I18n.tr("SCALE")
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
anchors.left: parent.left
|
||||
@@ -809,7 +810,7 @@ Panel {
|
||||
visible: root.displays.length > 1
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "DISPLAYS"
|
||||
text: I18n.tr("DISPLAYS")
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
}
|
||||
@@ -900,7 +901,7 @@ Panel {
|
||||
|
||||
Text {
|
||||
textFormat: Text.PlainText
|
||||
text: monitorRow.display.name + (monitorRow.display.focused ? " · focused" : "")
|
||||
text: monitorRow.display.name + (monitorRow.display.focused ? " · " + I18n.tr("focused") : "")
|
||||
color: root.bar.foreground
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
|
||||
@@ -57,13 +57,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
|
||||
@@ -138,7 +138,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.
|
||||
@@ -160,7 +160,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.
|
||||
@@ -280,11 +283,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
|
||||
@@ -663,7 +667,7 @@ Panel {
|
||||
}
|
||||
|
||||
function formatPingLatency(ms) {
|
||||
return Model.formatPingLatency(ms, hasInternetPing)
|
||||
return I18n.tr(Model.formatPingLatency(ms, hasInternetPing))
|
||||
}
|
||||
|
||||
function formatPacketLoss(percent) {
|
||||
@@ -710,7 +714,7 @@ Panel {
|
||||
}
|
||||
|
||||
function wifiSectionTitle(index) {
|
||||
return Model.wifiSectionTitle(wifiNetworks, index)
|
||||
return I18n.tr(Model.wifiSectionTitle(wifiNetworks, index))
|
||||
}
|
||||
|
||||
function wifiIconFor(strength) {
|
||||
@@ -755,7 +759,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 }) : "{}")
|
||||
}
|
||||
|
||||
@@ -836,7 +840,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()
|
||||
@@ -1063,9 +1067,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 = ""
|
||||
@@ -1080,8 +1084,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()
|
||||
@@ -1250,7 +1254,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
|
||||
fontSize: Style.font.subtitle * 1.5
|
||||
@@ -1264,7 +1268,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
|
||||
fontSize: Style.font.subtitle * 1.5
|
||||
@@ -1313,8 +1317,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()
|
||||
|
||||
@@ -1331,15 +1335,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 !== ""
|
||||
@@ -1362,7 +1366,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
|
||||
@@ -1381,7 +1385,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
|
||||
@@ -1407,38 +1411,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")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1479,7 +1483,7 @@ Panel {
|
||||
|
||||
PanelSectionHeader {
|
||||
id: bandAutoLabel
|
||||
text: "AUTOMATIC"
|
||||
text: I18n.tr("AUTOMATIC")
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -1512,8 +1516,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
|
||||
}
|
||||
}
|
||||
@@ -1584,7 +1588,7 @@ Panel {
|
||||
spacing: Style.space(10)
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "DNS PROVIDER"
|
||||
text: I18n.tr("DNS PROVIDER")
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
}
|
||||
@@ -1600,7 +1604,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)
|
||||
}
|
||||
@@ -1608,7 +1612,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)
|
||||
}
|
||||
@@ -1616,7 +1620,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)
|
||||
}
|
||||
@@ -1624,7 +1628,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)
|
||||
}
|
||||
@@ -1640,7 +1644,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
|
||||
}
|
||||
@@ -1743,7 +1747,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
|
||||
@@ -1826,12 +1830,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 ""
|
||||
}
|
||||
|
||||
@@ -1948,7 +1952,7 @@ Panel {
|
||||
|
||||
PanelToolTip {
|
||||
visible: rightMouse.containsMouse || row.forgetFocused
|
||||
text: "Forget network"
|
||||
text: I18n.tr("Forget network")
|
||||
fontFamily: root.bar.fontFamily
|
||||
}
|
||||
}
|
||||
@@ -1964,7 +1968,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
|
||||
@@ -2024,7 +2028,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
|
||||
@@ -2050,7 +2054,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
|
||||
@@ -2083,7 +2087,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
|
||||
@@ -2100,7 +2104,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()
|
||||
@@ -2111,7 +2115,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
|
||||
|
||||
@@ -55,7 +55,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) {
|
||||
@@ -95,26 +95,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
|
||||
|
||||
@@ -128,7 +128,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,8 @@ Panel {
|
||||
spacing: Style.space(2)
|
||||
|
||||
Text {
|
||||
text: "Battery"
|
||||
textFormat: Text.PlainText
|
||||
text: I18n.tr("Battery")
|
||||
color: root.bar.foreground
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.title
|
||||
@@ -433,20 +434,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 +462,7 @@ Panel {
|
||||
spacing: Style.space(10)
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "POWER PROFILE"
|
||||
text: I18n.tr("POWER PROFILE")
|
||||
foreground: root.bar.foreground
|
||||
fontFamily: root.bar.fontFamily
|
||||
}
|
||||
@@ -483,7 +484,7 @@ Panel {
|
||||
width: profileRow.cellWidth
|
||||
iconText: root.profileIcon(String(modelData))
|
||||
iconSize: Style.font.title
|
||||
text: String(modelData).charAt(0).toUpperCase() + String(modelData).slice(1)
|
||||
text: I18n.tr(String(modelData).charAt(0).toUpperCase() + String(modelData).slice(1))
|
||||
fontSize: Style.font.bodySmall
|
||||
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
|
||||
|
||||
@@ -25,16 +25,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]
|
||||
|
||||
@@ -53,7 +53,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, Commons.Color.accent) : "transparent"
|
||||
readonly property color selectedFill: bar ? Style.selectedFillFor(bar.foreground, Commons.Color.accent) : "transparent"
|
||||
@@ -77,7 +77,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
|
||||
}
|
||||
|
||||
@@ -133,7 +133,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")
|
||||
@@ -460,7 +460,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
|
||||
@@ -503,7 +503,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
|
||||
@@ -522,7 +522,8 @@ Panel {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.margins: Style.space(12)
|
||||
text: "Tailscale CLI is not installed or not on PATH."
|
||||
textFormat: Text.PlainText
|
||||
text: I18n.tr("Tailscale CLI is not installed or not on PATH.")
|
||||
color: root.dim
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
@@ -541,7 +542,7 @@ Panel {
|
||||
spacing: Style.space(10)
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "CONNECTIONS"
|
||||
text: I18n.tr("CONNECTIONS")
|
||||
foreground: root.foreground
|
||||
fontFamily: root.fontFamily
|
||||
}
|
||||
@@ -574,7 +575,7 @@ Panel {
|
||||
spacing: Style.space(10)
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "EXIT NODES"
|
||||
text: I18n.tr("EXIT NODES")
|
||||
foreground: root.foreground
|
||||
fontFamily: root.fontFamily
|
||||
}
|
||||
@@ -604,7 +605,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
|
||||
@@ -640,7 +641,8 @@ Panel {
|
||||
Text {
|
||||
visible: root.filteredMullvadRegions.length === 0
|
||||
width: parent.width
|
||||
text: "No Mullvad regions found."
|
||||
textFormat: Text.PlainText
|
||||
text: I18n.tr("No Mullvad regions found.")
|
||||
color: root.dim
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
@@ -678,7 +680,7 @@ Panel {
|
||||
spacing: Style.space(10)
|
||||
|
||||
PanelSectionHeader {
|
||||
text: "MACHINES"
|
||||
text: I18n.tr("MACHINES")
|
||||
foreground: root.foreground
|
||||
fontFamily: root.fontFamily
|
||||
}
|
||||
@@ -686,7 +688,8 @@ Panel {
|
||||
Text {
|
||||
visible: tailscale.installed && tailscale.active && tailscale.peers.length === 0
|
||||
width: parent.width
|
||||
text: "No machines found on this tailnet."
|
||||
textFormat: Text.PlainText
|
||||
text: I18n.tr("No machines found on this tailnet.")
|
||||
color: root.dim
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
@@ -779,7 +782,8 @@ Panel {
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: "Authorize Tailscale operator"
|
||||
textFormat: Text.PlainText
|
||||
text: I18n.tr("Authorize Tailscale operator")
|
||||
color: root.foreground
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.body
|
||||
@@ -788,7 +792,8 @@ Panel {
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: "Allow this user to operate this Tailscale profile"
|
||||
textFormat: Text.PlainText
|
||||
text: I18n.tr("Allow this user to operate this Tailscale profile")
|
||||
color: root.dim
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.caption
|
||||
@@ -805,7 +810,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
|
||||
@@ -869,7 +874,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 ""
|
||||
@@ -980,7 +985,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
|
||||
@@ -1120,8 +1125,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)
|
||||
@@ -1201,7 +1206,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
|
||||
|
||||
@@ -309,7 +309,7 @@ Panel {
|
||||
}
|
||||
|
||||
function dayName(dateString) {
|
||||
return Model.dayName(dateString, function(date) { return Qt.formatDate(date, "dddd") })
|
||||
return Model.dayName(dateString, function(date) { return I18n.chinese ? Qt.locale().toString(date, "dddd") : Qt.formatDate(date, "dddd") })
|
||||
}
|
||||
|
||||
// Bare degree value (no unit letter), used in the forecast row.
|
||||
@@ -617,7 +617,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
|
||||
|
||||
@@ -684,7 +684,8 @@ Panel {
|
||||
Column {
|
||||
spacing: Style.space(5)
|
||||
Text {
|
||||
text: "FEELS"
|
||||
textFormat: Text.PlainText
|
||||
text: I18n.tr("FEELS")
|
||||
color: Qt.darker(root.bar.foreground, 1.5)
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
@@ -702,7 +703,8 @@ Panel {
|
||||
Column {
|
||||
spacing: Style.space(5)
|
||||
Text {
|
||||
text: "WIND"
|
||||
textFormat: Text.PlainText
|
||||
text: I18n.tr("WIND")
|
||||
color: Qt.darker(root.bar.foreground, 1.5)
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
@@ -720,7 +722,8 @@ Panel {
|
||||
Column {
|
||||
spacing: Style.space(5)
|
||||
Text {
|
||||
text: "HUMID"
|
||||
textFormat: Text.PlainText
|
||||
text: I18n.tr("HUMID")
|
||||
color: Qt.darker(root.bar.foreground, 1.5)
|
||||
font.family: root.bar.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
@@ -793,7 +796,8 @@ Panel {
|
||||
|
||||
Text {
|
||||
visible: !root.current
|
||||
text: "Fetching forecast…"
|
||||
textFormat: Text.PlainText
|
||||
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,8 @@ Item {
|
||||
|
||||
Text {
|
||||
visible: root.loading
|
||||
text: "Generating QR code…"
|
||||
textFormat: Text.PlainText
|
||||
text: I18n.tr("Generating QR code…")
|
||||
color: root.onScrimDim
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
@@ -328,7 +329,8 @@ Item {
|
||||
|
||||
Text {
|
||||
visible: root.showingQr
|
||||
text: "Scan to join this network"
|
||||
textFormat: Text.PlainText
|
||||
text: I18n.tr("Scan to join this network")
|
||||
color: root.onScrimDim
|
||||
font.family: root.fontFamily
|
||||
font.pixelSize: Style.font.bodySmall
|
||||
@@ -341,7 +343,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
|
||||
|
||||
Reference in new issue
Block a user