Compare commits

...

5 Commits

Author SHA1 Message Date
henrygd
0f5b504f23 release 0.15.2 2025-10-29 01:18:15 -04:00
henrygd
365d291393 improve smart device detection (#1345)
also fix virtual device filtering
2025-10-29 01:16:58 -04:00
henrygd
3dbab24c0f improve identification of smart drive types (#1345) 2025-10-28 22:37:47 -04:00
henrygd
1f67fb7c8d release 0.15.1 2025-10-28 19:30:36 -04:00
henrygd
219e09fc78 update language files 2025-10-28 18:41:39 -04:00
33 changed files with 714 additions and 472 deletions

View File

@@ -6,7 +6,6 @@ import (
"errors"
"fmt"
"os/exec"
"slices"
"strconv"
"strings"
"sync"
@@ -40,6 +39,11 @@ type DeviceInfo struct {
Type string `json:"type"`
InfoName string `json:"info_name"`
Protocol string `json:"protocol"`
// typeVerified reports whether we have already parsed SMART data for this device
// with the stored parserType. When true we can skip re-running the detection logic.
typeVerified bool
// parserType holds the parser type (nvme, sat, scsi) that last succeeded.
parserType string
}
var errNoValidSmartData = fmt.Errorf("no valid SMART data found") // Error for missing data
@@ -136,6 +140,7 @@ func (sm *SmartManager) ScanDevices(force bool) error {
return nil
}
sm.lastScanTime = time.Now()
currentDevices := sm.devicesSnapshot()
var configuredDevices []*DeviceInfo
if configuredRaw, ok := GetEnv("SMART_DEVICES"); ok {
@@ -173,7 +178,7 @@ func (sm *SmartManager) ScanDevices(force bool) error {
}
}
finalDevices := mergeDeviceLists(scannedDevices, configuredDevices)
finalDevices := mergeDeviceLists(currentDevices, scannedDevices, configuredDevices)
sm.updateSmartDevices(finalDevices)
if len(finalDevices) == 0 {
@@ -221,62 +226,140 @@ func (sm *SmartManager) parseConfiguredDevices(config string) ([]*DeviceInfo, er
return devices, nil
}
// detectDeviceType extracts the device type reported in smartctl JSON output.
func detectDeviceType(output []byte) string {
var payload struct {
Device struct {
Type string `json:"type"`
} `json:"device"`
// detectSmartOutputType inspects sections that are unique to each smartctl
// JSON schema (NVMe, ATA/SATA, SCSI) to determine which parser should be used
// when the reported device type is ambiguous or missing.
func detectSmartOutputType(output []byte) string {
var hints struct {
AtaSmartAttributes json.RawMessage `json:"ata_smart_attributes"`
NVMeSmartHealthInformationLog json.RawMessage `json:"nvme_smart_health_information_log"`
ScsiErrorCounterLog json.RawMessage `json:"scsi_error_counter_log"`
}
if err := json.Unmarshal(output, &payload); err != nil {
if err := json.Unmarshal(output, &hints); err != nil {
return ""
}
return strings.ToLower(payload.Device.Type)
switch {
case hasJSONValue(hints.NVMeSmartHealthInformationLog):
return "nvme"
case hasJSONValue(hints.AtaSmartAttributes):
return "sat"
case hasJSONValue(hints.ScsiErrorCounterLog):
return "scsi"
default:
return "sat"
}
}
// hasJSONValue reports whether a JSON payload contains a concrete value. The
// smartctl output often emits "null" for sections that do not apply, so we
// only treat non-null content as a hint.
func hasJSONValue(raw json.RawMessage) bool {
if len(raw) == 0 {
return false
}
trimmed := strings.TrimSpace(string(raw))
return trimmed != "" && trimmed != "null"
}
func normalizeParserType(value string) string {
switch strings.ToLower(strings.TrimSpace(value)) {
case "nvme", "sntasmedia", "sntrealtek":
return "nvme"
case "sat", "ata":
return "sat"
case "scsi":
return "scsi"
default:
return strings.ToLower(strings.TrimSpace(value))
}
}
// parseSmartOutput attempts each SMART parser, optionally detecting the type when
// it is not provided, and updates the device info when a parser succeeds.
func (sm *SmartManager) parseSmartOutput(deviceInfo *DeviceInfo, output []byte) bool {
deviceType := strings.ToLower(deviceInfo.Type)
if deviceType == "" {
if detected := detectDeviceType(output); detected != "" {
deviceType = detected
deviceInfo.Type = detected
}
}
parsers := []struct {
Type string
Parse func([]byte) (bool, int)
Alias []string
}{
{Type: "nvme", Parse: sm.parseSmartForNvme, Alias: []string{"sntasmedia", "sntrealtek"}},
{Type: "sat", Parse: sm.parseSmartForSata, Alias: []string{"ata"}},
{Type: "nvme", Parse: sm.parseSmartForNvme},
{Type: "sat", Parse: sm.parseSmartForSata},
{Type: "scsi", Parse: sm.parseSmartForScsi},
}
for _, parser := range parsers {
if deviceType != "" && deviceType != parser.Type {
aliasMatched := slices.Contains(parser.Alias, deviceType)
if !aliasMatched {
continue
}
}
hasData, _ := parser.Parse(output)
if hasData {
if deviceInfo.Type == "" {
deviceInfo.Type = parser.Type
}
return true
} else {
slog.Debug("parser failed", "device", deviceInfo.Name, "parser", parser.Type)
deviceType := normalizeParserType(deviceInfo.parserType)
if deviceType == "" {
deviceType = normalizeParserType(deviceInfo.Type)
}
if deviceInfo.parserType == "" {
switch deviceType {
case "nvme", "sat", "scsi":
deviceInfo.parserType = deviceType
}
}
// Only run the type detection when we do not yet know which parser works
// or the previous attempt failed.
needsDetection := deviceType == "" || !deviceInfo.typeVerified
if needsDetection {
structureType := detectSmartOutputType(output)
if deviceType != structureType {
deviceType = structureType
deviceInfo.parserType = structureType
deviceInfo.typeVerified = false
}
if deviceInfo.Type == "" || strings.EqualFold(deviceInfo.Type, structureType) {
deviceInfo.Type = structureType
}
}
// Try the most likely parser first, but keep the remaining parsers in reserve
// so an incorrect hint never leaves the device unparsed.
selectedParsers := make([]struct {
Type string
Parse func([]byte) (bool, int)
}, 0, len(parsers))
if deviceType != "" {
for _, parser := range parsers {
if parser.Type == deviceType {
selectedParsers = append(selectedParsers, parser)
break
}
}
}
for _, parser := range parsers {
alreadySelected := false
for _, selected := range selectedParsers {
if selected.Type == parser.Type {
alreadySelected = true
break
}
}
if alreadySelected {
continue
}
selectedParsers = append(selectedParsers, parser)
}
// Try the selected parsers in order until we find one that succeeds.
for _, parser := range selectedParsers {
hasData, _ := parser.Parse(output)
if hasData {
deviceInfo.parserType = parser.Type
if deviceInfo.Type == "" || strings.EqualFold(deviceInfo.Type, parser.Type) {
deviceInfo.Type = parser.Type
}
// Remember that this parser is valid so future refreshes can bypass
// detection entirely.
deviceInfo.typeVerified = true
return true
}
slog.Debug("parser failed", "device", deviceInfo.Name, "parser", parser.Type)
}
// Leave verification false so the next pass will attempt detection again.
deviceInfo.typeVerified = false
slog.Debug("parsing failed", "device", deviceInfo.Name)
return false
}
@@ -335,8 +418,12 @@ func (sm *SmartManager) CollectSmart(deviceInfo *DeviceInfo) error {
func (sm *SmartManager) smartctlArgs(deviceInfo *DeviceInfo, includeStandby bool) []string {
args := make([]string, 0, 7)
if deviceInfo != nil && deviceInfo.Type != "" {
args = append(args, "-d", deviceInfo.Type)
if deviceInfo != nil {
deviceType := strings.ToLower(deviceInfo.Type)
// types sometimes misidentified in scan; see github.com/henrygd/beszel/issues/1345
if deviceType != "" && deviceType != "scsi" && deviceType != "ata" {
args = append(args, "-d", deviceInfo.Type)
}
}
args = append(args, "-aj")
@@ -395,42 +482,84 @@ func (sm *SmartManager) parseScan(output []byte) ([]*DeviceInfo, bool) {
// mergeDeviceLists combines scanned and configured SMART devices, preferring
// configured SMART_DEVICES when both sources reference the same device.
func mergeDeviceLists(scanned, configured []*DeviceInfo) []*DeviceInfo {
func mergeDeviceLists(existing, scanned, configured []*DeviceInfo) []*DeviceInfo {
if len(scanned) == 0 && len(configured) == 0 {
return nil
return existing
}
// preserveVerifiedType copies the verified type/parser metadata from an existing
// device record so that subsequent scans/config updates never downgrade a
// previously verified device.
preserveVerifiedType := func(target, prev *DeviceInfo) {
if prev == nil || !prev.typeVerified {
return
}
target.Type = prev.Type
target.typeVerified = true
target.parserType = prev.parserType
}
existingIndex := make(map[string]*DeviceInfo, len(existing))
for _, dev := range existing {
if dev == nil || dev.Name == "" {
continue
}
existingIndex[dev.Name] = dev
}
finalDevices := make([]*DeviceInfo, 0, len(scanned)+len(configured))
deviceIndex := make(map[string]*DeviceInfo, len(scanned)+len(configured))
// Start with the newly scanned devices so we always surface fresh metadata,
// but ensure we retain any previously verified parser assignment.
for _, dev := range scanned {
if dev == nil || dev.Name == "" {
continue
}
// Work on a copy so we can safely adjust metadata without mutating the
// input slices that may be reused elsewhere.
copyDev := *dev
if prev := existingIndex[copyDev.Name]; prev != nil {
preserveVerifiedType(&copyDev, prev)
}
finalDevices = append(finalDevices, &copyDev)
deviceIndex[copyDev.Name] = finalDevices[len(finalDevices)-1]
}
// Merge configured devices on top so users can override scan results (except
// for verified type information).
for _, dev := range configured {
if dev == nil || dev.Name == "" {
continue
}
if existing, ok := deviceIndex[dev.Name]; ok {
if dev.Type != "" {
existing.Type = dev.Type
if existingDev, ok := deviceIndex[dev.Name]; ok {
// Only update the type if it has not been verified yet; otherwise we
// keep the existing verified metadata intact.
if dev.Type != "" && !existingDev.typeVerified {
newType := strings.TrimSpace(dev.Type)
existingDev.Type = newType
existingDev.typeVerified = false
existingDev.parserType = normalizeParserType(newType)
}
if dev.InfoName != "" {
existing.InfoName = dev.InfoName
existingDev.InfoName = dev.InfoName
}
if dev.Protocol != "" {
existing.Protocol = dev.Protocol
existingDev.Protocol = dev.Protocol
}
continue
}
copyDev := *dev
if prev := existingIndex[copyDev.Name]; prev != nil {
preserveVerifiedType(&copyDev, prev)
} else if copyDev.Type != "" {
copyDev.parserType = normalizeParserType(copyDev.Type)
}
finalDevices = append(finalDevices, &copyDev)
deviceIndex[copyDev.Name] = finalDevices[len(finalDevices)-1]
}
@@ -478,21 +607,40 @@ func (sm *SmartManager) isVirtualDevice(data *smart.SmartInfoForSata) bool {
productUpper := strings.ToUpper(data.ScsiProduct)
modelUpper := strings.ToUpper(data.ModelName)
switch {
case strings.Contains(vendorUpper, "IET"), // iSCSI Enterprise Target
strings.Contains(productUpper, "VIRTUAL"),
strings.Contains(productUpper, "QEMU"),
strings.Contains(productUpper, "VBOX"),
strings.Contains(productUpper, "VMWARE"),
strings.Contains(vendorUpper, "MSFT"), // Microsoft Hyper-V
strings.Contains(modelUpper, "VIRTUAL"),
strings.Contains(modelUpper, "QEMU"),
strings.Contains(modelUpper, "VBOX"),
strings.Contains(modelUpper, "VMWARE"):
return true
default:
return false
return sm.isVirtualDeviceFromStrings(vendorUpper, productUpper, modelUpper)
}
// isVirtualDeviceNvme checks if an NVMe device is a virtual disk that should be filtered out
func (sm *SmartManager) isVirtualDeviceNvme(data *smart.SmartInfoForNvme) bool {
modelUpper := strings.ToUpper(data.ModelName)
return sm.isVirtualDeviceFromStrings(modelUpper)
}
// isVirtualDeviceScsi checks if a SCSI device is a virtual disk that should be filtered out
func (sm *SmartManager) isVirtualDeviceScsi(data *smart.SmartInfoForScsi) bool {
vendorUpper := strings.ToUpper(data.ScsiVendor)
productUpper := strings.ToUpper(data.ScsiProduct)
modelUpper := strings.ToUpper(data.ScsiModelName)
return sm.isVirtualDeviceFromStrings(vendorUpper, productUpper, modelUpper)
}
// isVirtualDeviceFromStrings checks if any of the provided strings indicate a virtual device
func (sm *SmartManager) isVirtualDeviceFromStrings(fields ...string) bool {
for _, field := range fields {
fieldUpper := strings.ToUpper(field)
switch {
case strings.Contains(fieldUpper, "IET"), // iSCSI Enterprise Target
strings.Contains(fieldUpper, "VIRTUAL"),
strings.Contains(fieldUpper, "QEMU"),
strings.Contains(fieldUpper, "VBOX"),
strings.Contains(fieldUpper, "VMWARE"),
strings.Contains(fieldUpper, "MSFT"): // Microsoft Hyper-V
return true
}
}
return false
}
// parseSmartForSata parses the output of smartctl --all -j for SATA/ATA devices and updates the SmartDataMap
@@ -579,6 +727,12 @@ func (sm *SmartManager) parseSmartForScsi(output []byte) (bool, int) {
return false, data.Smartctl.ExitStatus
}
// Skip virtual devices (e.g., Kubernetes PVCs, QEMU, VirtualBox, etc.)
if sm.isVirtualDeviceScsi(&data) {
slog.Debug("skipping smart", "device", data.Device.Name, "model", data.ScsiModelName)
return false, data.Smartctl.ExitStatus
}
sm.Lock()
defer sm.Unlock()
@@ -661,6 +815,12 @@ func (sm *SmartManager) parseSmartForNvme(output []byte) (bool, int) {
return false, data.Smartctl.ExitStatus
}
// Skip virtual devices (e.g., Kubernetes PVCs, QEMU, VirtualBox, etc.)
if sm.isVirtualDeviceNvme(data) {
slog.Debug("skipping smart", "device", data.Device.Name, "model", data.ModelName)
return false, data.Smartctl.ExitStatus
}
sm.Lock()
defer sm.Unlock()

View File

@@ -344,7 +344,7 @@ func TestMergeDeviceListsPrefersConfigured(t *testing.T) {
{Name: "/dev/sdb", Type: "sat"},
}
merged := mergeDeviceLists(scanned, configured)
merged := mergeDeviceLists(nil, scanned, configured)
require.Len(t, merged, 3)
byName := make(map[string]*DeviceInfo, len(merged))
@@ -363,6 +363,79 @@ func TestMergeDeviceListsPrefersConfigured(t *testing.T) {
assert.Equal(t, "sat", byName["/dev/sdb"].Type)
}
func TestMergeDeviceListsPreservesVerification(t *testing.T) {
existing := []*DeviceInfo{
{Name: "/dev/sda", Type: "sat+megaraid", parserType: "sat", typeVerified: true},
}
scanned := []*DeviceInfo{
{Name: "/dev/sda", Type: "nvme"},
}
merged := mergeDeviceLists(existing, scanned, nil)
require.Len(t, merged, 1)
device := merged[0]
assert.True(t, device.typeVerified)
assert.Equal(t, "sat", device.parserType)
assert.Equal(t, "sat+megaraid", device.Type)
}
func TestMergeDeviceListsUpdatesTypeWhenUnverified(t *testing.T) {
existing := []*DeviceInfo{
{Name: "/dev/sda", Type: "sat", parserType: "sat", typeVerified: false},
}
scanned := []*DeviceInfo{
{Name: "/dev/sda", Type: "nvme"},
}
merged := mergeDeviceLists(existing, scanned, nil)
require.Len(t, merged, 1)
device := merged[0]
assert.False(t, device.typeVerified)
assert.Equal(t, "nvme", device.Type)
assert.Equal(t, "", device.parserType)
}
func TestParseSmartOutputMarksVerified(t *testing.T) {
fixturePath := filepath.Join("test-data", "smart", "nvme0.json")
data, err := os.ReadFile(fixturePath)
require.NoError(t, err)
sm := &SmartManager{SmartDataMap: make(map[string]*smart.SmartData)}
device := &DeviceInfo{Name: "/dev/nvme0"}
require.True(t, sm.parseSmartOutput(device, data))
assert.Equal(t, "nvme", device.Type)
assert.Equal(t, "nvme", device.parserType)
assert.True(t, device.typeVerified)
}
func TestParseSmartOutputKeepsCustomType(t *testing.T) {
fixturePath := filepath.Join("test-data", "smart", "sda.json")
data, err := os.ReadFile(fixturePath)
require.NoError(t, err)
sm := &SmartManager{SmartDataMap: make(map[string]*smart.SmartData)}
device := &DeviceInfo{Name: "/dev/sda", Type: "sat+megaraid"}
require.True(t, sm.parseSmartOutput(device, data))
assert.Equal(t, "sat+megaraid", device.Type)
assert.Equal(t, "sat", device.parserType)
assert.True(t, device.typeVerified)
}
func TestParseSmartOutputResetsVerificationOnFailure(t *testing.T) {
sm := &SmartManager{SmartDataMap: make(map[string]*smart.SmartData)}
device := &DeviceInfo{Name: "/dev/sda", Type: "sat", parserType: "sat", typeVerified: true}
assert.False(t, sm.parseSmartOutput(device, []byte("not json")))
assert.False(t, device.typeVerified)
assert.Equal(t, "sat", device.parserType)
}
func assertAttrValue(t *testing.T, attributes []*smart.SmartAttribute, name string, expected uint64) {
t.Helper()
attr := findAttr(attributes, name)
@@ -382,3 +455,93 @@ func findAttr(attributes []*smart.SmartAttribute, name string) *smart.SmartAttri
}
return nil
}
func TestIsVirtualDevice(t *testing.T) {
sm := &SmartManager{}
tests := []struct {
name string
vendor string
product string
model string
expected bool
}{
{"regular drive", "SEAGATE", "ST1000DM003", "ST1000DM003-1CH162", false},
{"qemu virtual", "QEMU", "QEMU HARDDISK", "QEMU HARDDISK", true},
{"virtualbox virtual", "VBOX", "HARDDISK", "VBOX HARDDISK", true},
{"vmware virtual", "VMWARE", "Virtual disk", "VMWARE Virtual disk", true},
{"virtual in model", "ATA", "VIRTUAL", "VIRTUAL DISK", true},
{"iet virtual", "IET", "VIRTUAL-DISK", "VIRTUAL-DISK", true},
{"hyper-v virtual", "MSFT", "VIRTUAL HD", "VIRTUAL HD", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
data := &smart.SmartInfoForSata{
ScsiVendor: tt.vendor,
ScsiProduct: tt.product,
ModelName: tt.model,
}
result := sm.isVirtualDevice(data)
assert.Equal(t, tt.expected, result)
})
}
}
func TestIsVirtualDeviceNvme(t *testing.T) {
sm := &SmartManager{}
tests := []struct {
name string
model string
expected bool
}{
{"regular nvme", "Samsung SSD 970 EVO Plus 1TB", false},
{"qemu virtual", "QEMU NVMe Ctrl", true},
{"virtualbox virtual", "VBOX NVMe", true},
{"vmware virtual", "VMWARE NVMe", true},
{"virtual in model", "Virtual NVMe Device", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
data := &smart.SmartInfoForNvme{
ModelName: tt.model,
}
result := sm.isVirtualDeviceNvme(data)
assert.Equal(t, tt.expected, result)
})
}
}
func TestIsVirtualDeviceScsi(t *testing.T) {
sm := &SmartManager{}
tests := []struct {
name string
vendor string
product string
model string
expected bool
}{
{"regular scsi", "SEAGATE", "ST1000DM003", "ST1000DM003-1CH162", false},
{"qemu virtual", "QEMU", "QEMU HARDDISK", "QEMU HARDDISK", true},
{"virtualbox virtual", "VBOX", "HARDDISK", "VBOX HARDDISK", true},
{"vmware virtual", "VMWARE", "Virtual disk", "VMWARE Virtual disk", true},
{"virtual in model", "ATA", "VIRTUAL", "VIRTUAL DISK", true},
{"iet virtual", "IET", "VIRTUAL-DISK", "VIRTUAL-DISK", true},
{"hyper-v virtual", "MSFT", "VIRTUAL HD", "VIRTUAL HD", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
data := &smart.SmartInfoForScsi{
ScsiVendor: tt.vendor,
ScsiProduct: tt.product,
ScsiModelName: tt.model,
}
result := sm.isVirtualDeviceScsi(data)
assert.Equal(t, tt.expected, result)
})
}
}

View File

@@ -6,7 +6,7 @@ import "github.com/blang/semver"
const (
// Version is the current version of the application.
Version = "0.15.0"
Version = "0.15.2"
// AppName is the name of the application.
AppName = "beszel"
)

View File

@@ -1,12 +1,12 @@
{
"name": "beszel",
"version": "0.15.0",
"version": "0.15.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "beszel",
"version": "0.15.0",
"version": "0.15.2",
"dependencies": {
"@henrygd/queue": "^1.0.7",
"@henrygd/semaphore": "^0.0.2",

View File

@@ -1,7 +1,7 @@
{
"name": "beszel",
"private": true,
"version": "0.15.0",
"version": "0.15.2",
"type": "module",
"scripts": {
"dev": "vite --host",

View File

@@ -362,7 +362,7 @@ export default memo(function SystemDetail({ id }: { id: string }) {
let uptime: string
if (system.info.u < 3600) {
uptime = secondsToString(system.info.u, "minute")
} else if (system.info.u * 360000) {
} else if (system.info.u < 360000) {
uptime = secondsToString(system.info.u, "hour")
} else {
uptime = secondsToString(system.info.u, "day")

View File

@@ -18,27 +18,24 @@ msgstr ""
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 32\n"
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# يوم} other {# أيام}}"
#. placeholder {0}: Math.trunc(system.info.u / 3600)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# hour} other {# hours}}"
msgstr "{0, plural, one {# ساعة} other {# ساعات}}"
#. placeholder {0}: Math.trunc(system.info.u / 60)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
msgstr "{0, plural, one {# دقيقة} few {# دقائق} many {# دقيقة} other {# دقيقة}}"
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
#. placeholder {1}: table.getFilteredRowModel().rows.length
#: src/components/routes/settings/alerts-history-data-table.tsx
msgid "{0} of {1} row(s) selected."
msgstr "تم تحديد {0} من {1} صف"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
msgstr "{count, plural, one {{countString} يوم} other {{countString} أيام}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
msgstr "{count, plural, one {{countString} ساعة} other {{countString} ساعات}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
msgstr "{count, plural, one {{countString} دقيقة} few {{countString} دقائق} many {{countString} دقيقة} other {{countString} دقيقة}}"
#: src/lib/utils.ts
msgid "1 hour"
msgstr "1 ساعة"

View File

@@ -18,27 +18,24 @@ msgstr ""
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 32\n"
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# ден} other {# дни}}"
#. placeholder {0}: Math.trunc(system.info.u / 3600)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# hour} other {# hours}}"
msgstr "{0, plural, one {# час} other {# часа}}"
#. placeholder {0}: Math.trunc(system.info.u / 60)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
msgstr "{0, plural, one {# минута} few {# минути} many {# минути} other {# минути}}"
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
#. placeholder {1}: table.getFilteredRowModel().rows.length
#: src/components/routes/settings/alerts-history-data-table.tsx
msgid "{0} of {1} row(s) selected."
msgstr "{0} от {1} селектирани."
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
msgstr "{count, plural, one {{countString} ден} other {{countString} дни}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
msgstr "{count, plural, one {{countString} час} other {{countString} часа}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
msgstr "{count, plural, one {{countString} минута} few {{countString} минути} many {{countString} минути} other {{countString} минути}}"
#: src/lib/utils.ts
msgid "1 hour"
msgstr "1 час"

View File

@@ -18,27 +18,24 @@ msgstr ""
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 32\n"
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# den} few {# dny} other {# dní}}"
#. placeholder {0}: Math.trunc(system.info.u / 3600)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# hour} other {# hours}}"
msgstr "{0, plural, one {# Hodina} few {# Hodiny} many {# Hodin} other {# Hodin}}"
#. placeholder {0}: Math.trunc(system.info.u / 60)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
msgstr "{0, plural, one {# minuta} few {# minuty} many {# minut} other {# minut}}"
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
#. placeholder {1}: table.getFilteredRowModel().rows.length
#: src/components/routes/settings/alerts-history-data-table.tsx
msgid "{0} of {1} row(s) selected."
msgstr "{0} z {1} vybraných řádků."
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
msgstr "{count, plural, one {{countString} den} few {{countString} dny} other {{countString} dní}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
msgstr "{count, plural, one {{countString} Hodina} few {{countString} Hodiny} many {{countString} Hodin} other {{countString} Hodin}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
msgstr "{count, plural, one {{countString} minuta} few {{countString} minuty} many {{countString} minut} other {{countString} minut}}"
#: src/lib/utils.ts
msgid "1 hour"
msgstr "1 hodina"

View File

@@ -18,27 +18,24 @@ msgstr ""
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 32\n"
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# dag} other {# dage}}"
#. placeholder {0}: Math.trunc(system.info.u / 3600)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# hour} other {# hours}}"
msgstr "{0, plural, one {# time} other {# timer}}"
#. placeholder {0}: Math.trunc(system.info.u / 60)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
msgstr "{0, plural, one {# minut} other {# minutter}}"
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
#. placeholder {1}: table.getFilteredRowModel().rows.length
#: src/components/routes/settings/alerts-history-data-table.tsx
msgid "{0} of {1} row(s) selected."
msgstr "{0} af {1} række(r) valgt."
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
msgstr "{count, plural, one {{countString} dag} other {{countString} dage}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
msgstr "{count, plural, one {{countString} time} other {{countString} timer}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
msgstr "{count, plural, one {{countString} minut} other {{countString} minutter}}"
#: src/lib/utils.ts
msgid "1 hour"
msgstr "1 time"

View File

@@ -18,27 +18,24 @@ msgstr ""
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 32\n"
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# Tag} other {# Tage}}"
#. placeholder {0}: Math.trunc(system.info.u / 3600)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# hour} other {# hours}}"
msgstr "{0, plural, one {# Stunde} other {# Stunden}}"
#. placeholder {0}: Math.trunc(system.info.u / 60)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
msgstr "{0, plural, one {# Minute} other {# Minuten}}"
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
#. placeholder {1}: table.getFilteredRowModel().rows.length
#: src/components/routes/settings/alerts-history-data-table.tsx
msgid "{0} of {1} row(s) selected."
msgstr "{0} von {1} Zeile(n) ausgewählt."
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
msgstr "{count, plural, one {{countString} Tag} other {{countString} Tage}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
msgstr "{count, plural, one {{countString} Stunde} other {{countString} Stunden}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
msgstr "{count, plural, one {{countString} Minute} other {{countString} Minuten}}"
#: src/lib/utils.ts
msgid "1 hour"
msgstr "1 Stunde"

View File

@@ -13,27 +13,24 @@ msgstr ""
"Language-Team: \n"
"Plural-Forms: \n"
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# day} other {# days}}"
#. placeholder {0}: Math.trunc(system.info.u / 3600)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# hour} other {# hours}}"
msgstr "{0, plural, one {# hour} other {# hours}}"
#. placeholder {0}: Math.trunc(system.info.u / 60)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
msgstr "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
#. placeholder {1}: table.getFilteredRowModel().rows.length
#: src/components/routes/settings/alerts-history-data-table.tsx
msgid "{0} of {1} row(s) selected."
msgstr "{0} of {1} row(s) selected."
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
msgstr "{count, plural, one {{countString} day} other {{countString} days}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
msgstr "{count, plural, one {{countString} hour} other {{countString} hours}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
msgstr "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
#: src/lib/utils.ts
msgid "1 hour"
msgstr "1 hour"

View File

@@ -18,27 +18,24 @@ msgstr ""
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 32\n"
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# día} other {# días}}"
#. placeholder {0}: Math.trunc(system.info.u / 3600)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# hour} other {# hours}}"
msgstr "{0, plural, one {# hora} other {# horas}}"
#. placeholder {0}: Math.trunc(system.info.u / 60)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
msgstr "{0, plural, one {# minuto} other {# minutos}}"
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
#. placeholder {1}: table.getFilteredRowModel().rows.length
#: src/components/routes/settings/alerts-history-data-table.tsx
msgid "{0} of {1} row(s) selected."
msgstr "{0} de {1} fila(s) seleccionada(s)."
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
msgstr "{count, plural, one {{countString} día} other {{countString} días}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
msgstr "{count, plural, one {{countString} hora} other {{countString} horas}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
msgstr "{count, plural, one {{countString} minuto} other {{countString} minutos}}"
#: src/lib/utils.ts
msgid "1 hour"
msgstr "1 hora"

View File

@@ -18,27 +18,24 @@ msgstr ""
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 32\n"
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# روز} other {# روز}}"
#. placeholder {0}: Math.trunc(system.info.u / 3600)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# hour} other {# hours}}"
msgstr "{0, plural, one {# ساعت} other {# ساعت}}"
#. placeholder {0}: Math.trunc(system.info.u / 60)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
msgstr "{0, plural, one {# دقیقه} few {# دقیقه} many {# دقیقه} other {# دقیقه}}"
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
#. placeholder {1}: table.getFilteredRowModel().rows.length
#: src/components/routes/settings/alerts-history-data-table.tsx
msgid "{0} of {1} row(s) selected."
msgstr "{0} از {1} ردیف انتخاب شده است."
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
msgstr "{count, plural, one {{countString} روز} other {{countString} روز}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
msgstr "{count, plural, one {{countString} ساعت} other {{countString} ساعت}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
msgstr "{count, plural, one {{countString} دقیقه} few {{countString} دقیقه} many {{countString} دقیقه} other {{countString} دقیقه}}"
#: src/lib/utils.ts
msgid "1 hour"
msgstr "۱ ساعت"

View File

@@ -18,27 +18,24 @@ msgstr ""
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 32\n"
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# jour} other {# jours}}"
#. placeholder {0}: Math.trunc(system.info.u / 3600)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# hour} other {# hours}}"
msgstr "{0, plural, one {# heure} other {# heures}}"
#. placeholder {0}: Math.trunc(system.info.u / 60)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
msgstr "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
#. placeholder {1}: table.getFilteredRowModel().rows.length
#: src/components/routes/settings/alerts-history-data-table.tsx
msgid "{0} of {1} row(s) selected."
msgstr "{0} sur {1} ligne(s) sélectionnée(s)."
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
msgstr "{count, plural, one {{countString} jour} other {{countString} jours}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
msgstr "{count, plural, one {{countString} heure} other {{countString} heures}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
msgstr "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
#: src/lib/utils.ts
msgid "1 hour"
msgstr "1 heure"

View File

@@ -18,27 +18,24 @@ msgstr ""
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 32\n"
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# dan} other {# dani}}"
#. placeholder {0}: Math.trunc(system.info.u / 3600)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# hour} other {# hours}}"
msgstr "{0, plural, one {# sat} other {# sati}}"
#. placeholder {0}: Math.trunc(system.info.u / 60)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
msgstr "{0, plural, one {# minuta} few {# minuta} many {# minuta} other {# minute}}"
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
#. placeholder {1}: table.getFilteredRowModel().rows.length
#: src/components/routes/settings/alerts-history-data-table.tsx
msgid "{0} of {1} row(s) selected."
msgstr "{0} od {1} redaka izabrano."
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
msgstr "{count, plural, one {{countString} dan} other {{countString} dani}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
msgstr "{count, plural, one {{countString} sat} other {{countString} sati}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
msgstr "{count, plural, one {{countString} minuta} few {{countString} minuta} many {{countString} minuta} other {{countString} minute}}"
#: src/lib/utils.ts
msgid "1 hour"
msgstr "1 sat"

View File

@@ -18,27 +18,24 @@ msgstr ""
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 32\n"
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# nap} other {# nap}}"
#. placeholder {0}: Math.trunc(system.info.u / 3600)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# hour} other {# hours}}"
msgstr "{0, plural, one {# óra} other {# óra}}"
#. placeholder {0}: Math.trunc(system.info.u / 60)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
msgstr "{0, plural, one {# perc} few {# perc} many {# perc} other {# perc}}"
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
#. placeholder {1}: table.getFilteredRowModel().rows.length
#: src/components/routes/settings/alerts-history-data-table.tsx
msgid "{0} of {1} row(s) selected."
msgstr "{0} a(z) {1} sorból kiválasztva."
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
msgstr "{count, plural, one {{countString} nap} other {{countString} nap}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
msgstr "{count, plural, one {{countString} óra} other {{countString} óra}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
msgstr "{count, plural, one {{countString} perc} few {{countString} perc} many {{countString} perc} other {{countString} perc}}"
#: src/lib/utils.ts
msgid "1 hour"
msgstr "1 óra"

View File

@@ -18,27 +18,24 @@ msgstr ""
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 32\n"
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# giorno} other {# giorni}}"
#. placeholder {0}: Math.trunc(system.info.u / 3600)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# hour} other {# hours}}"
msgstr "{0, plural, one {# ora} other {# ore}}"
#. placeholder {0}: Math.trunc(system.info.u / 60)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
msgstr "{0, plural, one {# minuto} other {# minuti}}"
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
#. placeholder {1}: table.getFilteredRowModel().rows.length
#: src/components/routes/settings/alerts-history-data-table.tsx
msgid "{0} of {1} row(s) selected."
msgstr "{0} di {1} righe selezionate."
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
msgstr "{count, plural, one {{countString} giorno} other {{countString} giorni}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
msgstr "{count, plural, one {{countString} ora} other {{countString} ore}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
msgstr "{count, plural, one {{countString} minuto} other {{countString} minuti}}"
#: src/lib/utils.ts
msgid "1 hour"
msgstr "1 ora"

View File

@@ -18,27 +18,24 @@ msgstr ""
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 32\n"
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# 日} other {# 日}}"
#. placeholder {0}: Math.trunc(system.info.u / 3600)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# hour} other {# hours}}"
msgstr "{0, plural, one {# 時間} other {# 時間}}"
#. placeholder {0}: Math.trunc(system.info.u / 60)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
msgstr "{0, plural, one {# 分} few {# 分} many {# 分} other {# 分}}"
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
#. placeholder {1}: table.getFilteredRowModel().rows.length
#: src/components/routes/settings/alerts-history-data-table.tsx
msgid "{0} of {1} row(s) selected."
msgstr "{1}行のうち{0}行が選択されました。"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
msgstr "{count, plural, one {{countString} 日} other {{countString} 日}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
msgstr "{count, plural, one {{countString} 時間} other {{countString} 時間}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
msgstr "{count, plural, one {{countString} 分} few {{countString} 分} many {{countString} 分} other {{countString} 分}}"
#: src/lib/utils.ts
msgid "1 hour"
msgstr "1時間"

View File

@@ -18,27 +18,24 @@ msgstr ""
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 32\n"
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# 일} other {# 일}}"
#. placeholder {0}: Math.trunc(system.info.u / 3600)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# hour} other {# hours}}"
msgstr "{0, plural, one {# 시간} other {# 시간}}"
#. placeholder {0}: Math.trunc(system.info.u / 60)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
msgstr "{0, plural, one {# 분} few {# 분} many {# 분} other {# 분}}"
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
#. placeholder {1}: table.getFilteredRowModel().rows.length
#: src/components/routes/settings/alerts-history-data-table.tsx
msgid "{0} of {1} row(s) selected."
msgstr "{1}개의 행 중 {0}개가 선택되었습니다."
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
msgstr "{count, plural, one {{countString} 일} other {{countString} 일}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
msgstr "{count, plural, one {{countString} 시간} other {{countString} 시간}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
msgstr "{count, plural, one {{countString} 분} few {{countString} 분} many {{countString} 분} other {{countString} 분}}"
#: src/lib/utils.ts
msgid "1 hour"
msgstr "1시간"

View File

@@ -18,27 +18,24 @@ msgstr ""
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 32\n"
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# dag} other {# dagen}}"
#. placeholder {0}: Math.trunc(system.info.u / 3600)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# hour} other {# hours}}"
msgstr "{0, plural, one {# uur} other {# uren}}"
#. placeholder {0}: Math.trunc(system.info.u / 60)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
msgstr "{0, plural, one {# minuut} other {# minuten}}"
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
#. placeholder {1}: table.getFilteredRowModel().rows.length
#: src/components/routes/settings/alerts-history-data-table.tsx
msgid "{0} of {1} row(s) selected."
msgstr "{0} van de {1} rij(en) geselecteerd."
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
msgstr "{count, plural, one {{countString} dag} other {{countString} dagen}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
msgstr "{count, plural, one {{countString} uur} other {{countString} uren}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
msgstr "{count, plural, one {{countString} minuut} other {{countString} minuten}}"
#: src/lib/utils.ts
msgid "1 hour"
msgstr "1 uur"

View File

@@ -18,27 +18,24 @@ msgstr ""
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 32\n"
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# dag} other {# dager}}"
#. placeholder {0}: Math.trunc(system.info.u / 3600)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# hour} other {# hours}}"
msgstr "{0, plural, one {# time} other {# timer}}"
#. placeholder {0}: Math.trunc(system.info.u / 60)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
msgstr "{0, plural, one {# minutt} other {# minutter}}"
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
#. placeholder {1}: table.getFilteredRowModel().rows.length
#: src/components/routes/settings/alerts-history-data-table.tsx
msgid "{0} of {1} row(s) selected."
msgstr "{0} av {1} rad(er) valgt."
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
msgstr "{count, plural, one {{countString} dag} other {{countString} dager}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
msgstr "{count, plural, one {{countString} time} other {{countString} timer}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
msgstr "{count, plural, one {{countString} minutt} other {{countString} minutter}}"
#: src/lib/utils.ts
msgid "1 hour"
msgstr "1 time"

View File

@@ -18,27 +18,24 @@ msgstr ""
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 32\n"
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# dzień} few {# dni} many {# dni} other {# dni}}"
#. placeholder {0}: Math.trunc(system.info.u / 3600)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# hour} other {# hours}}"
msgstr "{0, plural, one {godzinę} few {# godziny} many {# godzin} other {# godziny}}"
#. placeholder {0}: Math.trunc(system.info.u / 60)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
msgstr "{0, plural, one {# minuta} few {# minuty} many {# minut} other {# minut}}"
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
#. placeholder {1}: table.getFilteredRowModel().rows.length
#: src/components/routes/settings/alerts-history-data-table.tsx
msgid "{0} of {1} row(s) selected."
msgstr "{0} z {1} wybranych wierszy."
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
msgstr "{count, plural, one {{countString} dzień} few {{countString} dni} many {{countString} dni} other {{countString} dni}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
msgstr "{count, plural, one {godzinę} few {{countString} godziny} many {{countString} godzin} other {{countString} godziny}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
msgstr "{count, plural, one {{countString} minuta} few {{countString} minuty} many {{countString} minut} other {{countString} minut}}"
#: src/lib/utils.ts
msgid "1 hour"
msgstr "1 godzina"

View File

@@ -18,27 +18,24 @@ msgstr ""
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 32\n"
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# dia} other {# dias}}"
#. placeholder {0}: Math.trunc(system.info.u / 3600)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# hour} other {# hours}}"
msgstr "{0, plural, one {# hora} other {# horas}}"
#. placeholder {0}: Math.trunc(system.info.u / 60)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
msgstr "{0, plural, one {# minuto} other {# minutos}}"
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
#. placeholder {1}: table.getFilteredRowModel().rows.length
#: src/components/routes/settings/alerts-history-data-table.tsx
msgid "{0} of {1} row(s) selected."
msgstr "{0} de {1} linha(s) selecionada(s)."
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
msgstr "{count, plural, one {{countString} dia} other {{countString} dias}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
msgstr "{count, plural, one {{countString} hora} other {{countString} horas}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
msgstr "{count, plural, one {{countString} minuto} other {{countString} minutos}}"
#: src/lib/utils.ts
msgid "1 hour"
msgstr "1 hora"

View File

@@ -18,27 +18,24 @@ msgstr ""
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 32\n"
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# день} other {# дней}}"
#. placeholder {0}: Math.trunc(system.info.u / 3600)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# hour} other {# hours}}"
msgstr "{0, plural, one {# час} other {# часов}}"
#. placeholder {0}: Math.trunc(system.info.u / 60)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
msgstr "{0, plural, one {# минута} few {# минут} many {# минут} other {# минуты}}"
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
#. placeholder {1}: table.getFilteredRowModel().rows.length
#: src/components/routes/settings/alerts-history-data-table.tsx
msgid "{0} of {1} row(s) selected."
msgstr "Выбрано {0} из {1} строк."
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
msgstr "{count, plural, one {{countString} день} other {{countString} дней}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
msgstr "{count, plural, one {{countString} час} other {{countString} часов}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
msgstr "{count, plural, one {{countString} минута} few {{countString} минут} many {{countString} минут} other {{countString} минуты}}"
#: src/lib/utils.ts
msgid "1 hour"
msgstr "1 час"

View File

@@ -18,27 +18,24 @@ msgstr ""
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 32\n"
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# dan} two {# dneva} few {# dni} other {# dni}}"
#. placeholder {0}: Math.trunc(system.info.u / 3600)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# hour} other {# hours}}"
msgstr "{0, plural, one {# ura} two {# uri} few {# ur} other {# ur}}"
#. placeholder {0}: Math.trunc(system.info.u / 60)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
msgstr "{0, plural, one {# minuta} few {# minuti} many {# minut} other {# minut}}"
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
#. placeholder {1}: table.getFilteredRowModel().rows.length
#: src/components/routes/settings/alerts-history-data-table.tsx
msgid "{0} of {1} row(s) selected."
msgstr "{0} od {1} vrstic izbranih."
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
msgstr "{count, plural, one {{countString} dan} two {{countString} dneva} few {{countString} dni} other {{countString} dni}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
msgstr "{count, plural, one {{countString} ura} two {{countString} uri} few {{countString} ur} other {{countString} ur}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
msgstr "{count, plural, one {{countString} minuta} few {{countString} minuti} many {{countString} minut} other {{countString} minut}}"
#: src/lib/utils.ts
msgid "1 hour"
msgstr "1 ura"

View File

@@ -18,27 +18,24 @@ msgstr ""
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 32\n"
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# dag} other {# dagar}}"
#. placeholder {0}: Math.trunc(system.info.u / 3600)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# hour} other {# hours}}"
msgstr "{0, plural, one {# timme} other {# timmar}}"
#. placeholder {0}: Math.trunc(system.info.u / 60)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
msgstr "{0, plural, one {# minut} few {# minuter} many {# minuter} other {# minuter}}"
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
#. placeholder {1}: table.getFilteredRowModel().rows.length
#: src/components/routes/settings/alerts-history-data-table.tsx
msgid "{0} of {1} row(s) selected."
msgstr "{0} av {1} rad(er) valda."
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
msgstr "{count, plural, one {{countString} dag} other {{countString} dagar}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
msgstr "{count, plural, one {{countString} timme} other {{countString} timmar}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
msgstr "{count, plural, one {{countString} minut} few {{countString} minuter} many {{countString} minuter} other {{countString} minuter}}"
#: src/lib/utils.ts
msgid "1 hour"
msgstr "1 timme"

View File

@@ -18,27 +18,24 @@ msgstr ""
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 32\n"
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# gün} other {# gün}}"
#. placeholder {0}: Math.trunc(system.info.u / 3600)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# hour} other {# hours}}"
msgstr "{0, plural, one {# saat} other {# saat}}"
#. placeholder {0}: Math.trunc(system.info.u / 60)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
msgstr "{0, plural, one {# dakika} few {# dakika} many {# dakika} other {# dakika}}"
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
#. placeholder {1}: table.getFilteredRowModel().rows.length
#: src/components/routes/settings/alerts-history-data-table.tsx
msgid "{0} of {1} row(s) selected."
msgstr "{1} satırdan {0} tanesi seçildi."
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
msgstr "{count, plural, one {{countString} gün} other {{countString} gün}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
msgstr "{count, plural, one {{countString} saat} other {{countString} saat}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
msgstr "{count, plural, one {{countString} dakika} few {{countString} dakika} many {{countString} dakika} other {{countString} dakika}}"
#: src/lib/utils.ts
msgid "1 hour"
msgstr "1 saat"

View File

@@ -18,27 +18,24 @@ msgstr ""
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 32\n"
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# день} few {# дні} many {# днів} other {# дня}}"
#. placeholder {0}: Math.trunc(system.info.u / 3600)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# hour} other {# hours}}"
msgstr "{0, plural, one {# година} few {# години} many {# годин} other {# години}}"
#. placeholder {0}: Math.trunc(system.info.u / 60)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
msgstr "{0, plural, one {# хвилина} few {# хвилини} many {# хвилин} other {# хвилини}}"
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
#. placeholder {1}: table.getFilteredRowModel().rows.length
#: src/components/routes/settings/alerts-history-data-table.tsx
msgid "{0} of {1} row(s) selected."
msgstr "Вибрано {0} з {1} рядків."
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
msgstr "{count, plural, one {{countString} день} few {{countString} дні} many {{countString} днів} other {{countString} дня}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
msgstr "{count, plural, one {{countString} година} few {{countString} години} many {{countString} годин} other {{countString} години}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
msgstr "{count, plural, one {{countString} хвилина} few {{countString} хвилини} many {{countString} хвилин} other {{countString} хвилини}}"
#: src/lib/utils.ts
msgid "1 hour"
msgstr "1 година"

View File

@@ -18,27 +18,24 @@ msgstr ""
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 32\n"
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# ngày} other {# ngày}}"
#. placeholder {0}: Math.trunc(system.info.u / 3600)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# hour} other {# hours}}"
msgstr "{0, plural, one {# giờ} other {# giờ}}"
#. placeholder {0}: Math.trunc(system.info.u / 60)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
msgstr "{0, plural, one {# phút} few {# phút} many {# phút} other {# phút}}"
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
#. placeholder {1}: table.getFilteredRowModel().rows.length
#: src/components/routes/settings/alerts-history-data-table.tsx
msgid "{0} of {1} row(s) selected."
msgstr "Đã chọn {0} trên {1} hàng."
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
msgstr "{count, plural, one {{countString} ngày} other {{countString} ngày}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
msgstr "{count, plural, one {{countString} giờ} other {{countString} giờ}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
msgstr "{count, plural, one {{countString} phút} few {{countString} phút} many {{countString} phút} other {{countString} phút}}"
#: src/lib/utils.ts
msgid "1 hour"
msgstr "1 giờ"

View File

@@ -18,27 +18,24 @@ msgstr ""
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 32\n"
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# 天} other {# 天}}"
#. placeholder {0}: Math.trunc(system.info.u / 3600)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# hour} other {# hours}}"
msgstr "{0, plural, one {# 小时} other {# 小时}}"
#. placeholder {0}: Math.trunc(system.info.u / 60)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
msgstr "{0, plural, one {# 分钟} few {# 分钟} many {# 分钟} other {# 分钟}}"
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
#. placeholder {1}: table.getFilteredRowModel().rows.length
#: src/components/routes/settings/alerts-history-data-table.tsx
msgid "{0} of {1} row(s) selected."
msgstr "已选择 {0} / {1} 行"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
msgstr "{count, plural, one {{countString} 天} other {{countString} 天}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
msgstr "{count, plural, one {{countString} 小时} other {{countString} 小时}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
msgstr "{count, plural, one {{countString} 分钟} few {{countString} 分钟} many {{countString} 分钟} other {{countString} 分钟}}"
#: src/lib/utils.ts
msgid "1 hour"
msgstr "1 小时"

View File

@@ -18,27 +18,24 @@ msgstr ""
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 32\n"
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# 天} other {# 天}}"
#. placeholder {0}: Math.trunc(system.info.u / 3600)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# hour} other {# hours}}"
msgstr "{0, plural, one {# 小時} other {# 小時}}"
#. placeholder {0}: Math.trunc(system.info.u / 60)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
msgstr "{0, plural, one {# 分鐘} few {# 分鐘} many {# 分鐘} other {# 分鐘}}"
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
#. placeholder {1}: table.getFilteredRowModel().rows.length
#: src/components/routes/settings/alerts-history-data-table.tsx
msgid "{0} of {1} row(s) selected."
msgstr "已選擇 {1} 個項目中的 {0} 個"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
msgstr "{count, plural, one {{countString} 天} other {{countString} 天}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
msgstr "{count, plural, one {{countString} 小時} other {{countString} 小時}}"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
msgstr "{count, plural, one {{countString} 分鐘} few {{countString} 分鐘} many {{countString} 分鐘} other {{countString} 分鐘}}"
#: src/lib/utils.ts
msgid "1 hour"
msgstr "1小時"

View File

@@ -18,27 +18,24 @@ msgstr ""
"X-Crowdin-File: /main/internal/site/src/locales/en/en.po\n"
"X-Crowdin-File-ID: 32\n"
#. placeholder {0}: Math.trunc(system.info?.u / 86400)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# day} other {# days}}"
msgstr "{0, plural, one {# 天} other {# 天}}"
#. placeholder {0}: Math.trunc(system.info.u / 3600)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# hour} other {# hours}}"
msgstr "{0, plural, one {# 小時} other {# 小時}}"
#. placeholder {0}: Math.trunc(system.info.u / 60)
#: src/components/routes/system.tsx
msgid "{0, plural, one {# minute} few {# minutes} many {# minutes} other {# minutes}}"
msgstr "{0, plural, one {# 分鐘} few {# 分鐘} many {# 分鐘} other {# 分鐘}}"
#. placeholder {0}: table.getFilteredSelectedRowModel().rows.length
#. placeholder {1}: table.getFilteredRowModel().rows.length
#: src/components/routes/settings/alerts-history-data-table.tsx
msgid "{0} of {1} row(s) selected."
msgstr "已選取 {1} 個項目中的 {0} 個"
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} day} other {{countString} days}}"
msgstr ""
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} hour} other {{countString} hours}}"
msgstr ""
#: src/lib/utils.ts
msgid "{count, plural, one {{countString} minute} few {{countString} minutes} many {{countString} minutes} other {{countString} minutes}}"
msgstr ""
#: src/lib/utils.ts
msgid "1 hour"
msgstr "1小時"