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:
ZacharyZhang-NY committed 2026-10-12 02:56:40 -04:00
1 parent defbf90e89
commit a9b39710a2
138 files changed
+1986 -558

No files matched your search

+22 -9
View File
@@ -38,6 +38,7 @@ Item {
function refresh() {
defaultMenuFile.reload()
localeMenuFile.reload()
userMenuFile.reload()
return "ok"
}
@@ -50,7 +51,9 @@ 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: I18n.chinese ? omarchyPath + "/default/omarchy/omarchy-menu.zh-cn.jsonc" : ""
property var defaultMenuItems: []
property var localeMenuItems: []
property var userMenuItems: []
property bool opened: false
property string mode: "menu"
@@ -254,7 +257,7 @@ Item {
// on a per-key basis (so the user can tweak label/icon/action without
// re-declaring the whole row).
function rebuildItemsFromSources() {
var mergedMenu = MenuModel.mergeMenuSources(root.defaultMenuItems, root.userMenuItems)
var mergedMenu = MenuModel.mergeMenuSources(root.defaultMenuItems, root.localeMenuItems.concat(root.userMenuItems))
root.providerRevision += 1
root.providersLoaded = ({})
root.providerQueue = []
@@ -572,8 +575,8 @@ Item {
if (root.dmenuMultiple && root.dmenuOnChange.length === 0) {
displayModel.append({
itemId: "dmenu.apply", disabled: false, kind: "dmenu", icon: "", iconFont: "",
appIcon: "", appId: "", label: "Apply", target: "",
detail: root.dmenuSelected.length + " selected", path: "", childCount: 0,
appIcon: "", appId: "", label: I18n.tr("Apply"), target: "",
detail: I18n.tr("%1 selected", root.dmenuSelected.length), path: "", childCount: 0,
action: "", provider: "", score: -1, section: ""
})
}
@@ -624,7 +627,7 @@ Item {
for (var i = 0; i < displayModel.count; i++) {
var row = displayModel.get(i)
if (row.itemId === "dmenu.apply") {
displayModel.setProperty(i, "detail", root.dmenuSelected.length + " selected")
displayModel.setProperty(i, "detail", I18n.tr("%1 selected", root.dmenuSelected.length))
} else if (row.kind === "dmenu") {
var value = MenuModel.dmenuValue(root.dmenuOptions[Number(row.itemId.substring(6))])
displayModel.setProperty(i, "icon", root.dmenuSelected.indexOf(value) !== -1 ? "✓" : "○")
@@ -906,7 +909,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 : []
dmenuMultiple = mode === "select" && payload.multiple === true
dmenuOnChange = dmenuMultiple && Array.isArray(payload.onChange)
@@ -1084,6 +1087,16 @@ Item {
onFileChanged: reload()
}
FileView {
id: localeMenuFile
path: root.localeMenuPath
watchChanges: true
printErrors: false
onLoaded: { root.localeMenuItems = root.parseMenuJsonc(text()); root.rebuildItemsFromSources() }
onLoadFailed: { root.localeMenuItems = []; root.rebuildItemsFromSources() }
onFileChanged: reload()
}
FileView {
id: userMenuFile
path: root.userMenuPath
@@ -1279,8 +1292,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
@@ -1312,7 +1325,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
@@ -1578,7 +1591,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
+21 -5
View File
@@ -58,7 +58,9 @@ function parseMenuJsonc(raw) {
for (var id in source) {
var entry = source[id]
if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue
out.push(normalizeItem(id, entry))
var item = normalizeItem(id, entry)
item.declared = Object.keys(entry)
out.push(item)
}
return out
}
@@ -74,11 +76,24 @@ function mergeMenuSources(defaultItems, userItems) {
var entry = src[i]
if (!entry || !entry.id) continue
if (!nextItems[entry.id]) nextOrder.push(entry.id)
var prior = nextItems[entry.id] || {}
var prior = nextItems[entry.id]
var merged = {}
for (var k in prior) merged[k] = prior[k]
for (var k2 in entry) merged[k2] = entry[k2]
// An override replaces only the fields its file declared.
if (prior && entry.declared) {
for (var k in prior) merged[k] = prior[k]
for (var d = 0; d < entry.declared.length; d++) {
var key = entry.declared[d]
if (key in entry) merged[key] = entry[key]
}
if (entry.declared.indexOf("action") >= 0 || entry.declared.indexOf("target") >= 0)
merged.kind = merged.action ? "action" : (merged.target ? "link" : "menu")
} else {
prior = prior || {}
for (var k1 in prior) merged[k1] = prior[k1]
for (var k2 in entry) merged[k2] = entry[k2]
}
merged.id = entry.id
delete merged.declared
nextItems[entry.id] = merged
}
}
@@ -406,7 +421,8 @@ var GUARD_READERS = [
"omarchy-dns",
"omarchy-hyprland-corners",
"omarchy-hyprland-window-transparency",
"omarchy-hyprland-workspace-layout-default"
"omarchy-hyprland-workspace-layout-default",
"omarchy-language"
]
// Package and command presence account for most of what the guards ask, and