feat: initial localization support (#669)

* feat: WIP localization support

* feat: add strings for software and thermals pages

* feat: oc page localization strings

* feat: some header strings
This commit is contained in:
Ilya Zlobintsev
2025-07-24 21:14:52 +03:00
committed by GitHub
parent 9c81f284ac
commit 57ac91f9eb
29 changed files with 1338 additions and 230 deletions
Generated
+394 -1
View File
@@ -131,6 +131,12 @@ version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
[[package]]
name = "arc-swap"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
[[package]]
name = "async-broadcast"
version = "0.7.2"
@@ -192,6 +198,15 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "basic-toml"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a"
dependencies = [
"serde",
]
[[package]]
name = "bindgen"
version = "0.71.1"
@@ -224,6 +239,15 @@ version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
version = "3.19.0"
@@ -425,6 +449,15 @@ dependencies = [
"memchr",
]
[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.5.0"
@@ -440,6 +473,16 @@ version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "darling"
version = "0.20.11"
@@ -496,6 +539,27 @@ version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "displaydoc"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "divan"
version = "0.1.21"
@@ -673,6 +737,60 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "find-crate"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59a98bbaacea1c0eb6a0876280051b892eb73594fd90cf3b20e9c817029c57d2"
dependencies = [
"toml 0.5.11",
]
[[package]]
name = "fluent"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8137a6d5a2c50d6b0ebfcb9aaa91a28154e0a70605f112d30cb0cd4a78670477"
dependencies = [
"fluent-bundle",
"unic-langid",
]
[[package]]
name = "fluent-bundle"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01203cb8918f5711e73891b347816d932046f95f54207710bda99beaeb423bf4"
dependencies = [
"fluent-langneg",
"fluent-syntax",
"intl-memoizer",
"intl_pluralrules",
"rustc-hash",
"self_cell",
"smallvec",
"unic-langid",
]
[[package]]
name = "fluent-langneg"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c4ad0989667548f06ccd0e306ed56b61bd4d35458d54df5ec7587c0e8ed5e94"
dependencies = [
"unic-langid",
]
[[package]]
name = "fluent-syntax"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54f0d287c53ffd184d04d8677f590f4ac5379785529e5e08b1c8083acdd5c198"
dependencies = [
"memchr",
"thiserror 2.0.12",
]
[[package]]
name = "flume"
version = "0.11.1"
@@ -872,6 +990,16 @@ dependencies = [
"system-deps",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.2.16"
@@ -1134,6 +1262,73 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "i18n-config"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e06b90c8a0d252e203c94344b21e35a30f3a3a85dc7db5af8f8df9f3e0c63ef"
dependencies = [
"basic-toml",
"log",
"serde",
"serde_derive",
"thiserror 1.0.69",
"unic-langid",
]
[[package]]
name = "i18n-embed"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a217bbb075dcaefb292efa78897fc0678245ca67f265d12c351e42268fcb0305"
dependencies = [
"arc-swap",
"fluent",
"fluent-langneg",
"fluent-syntax",
"i18n-embed-impl",
"intl-memoizer",
"log",
"parking_lot",
"rust-embed",
"sys-locale",
"thiserror 1.0.69",
"unic-langid",
"walkdir",
]
[[package]]
name = "i18n-embed-fl"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e598ed73b67db92f61e04672e599eef2991a262a40e1666735b8a86d2e7e9f30"
dependencies = [
"find-crate",
"fluent",
"fluent-syntax",
"i18n-config",
"i18n-embed",
"proc-macro-error2",
"proc-macro2",
"quote",
"strsim",
"syn",
"unic-langid",
]
[[package]]
name = "i18n-embed-impl"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f2cc0e0523d1fe6fc2c6f66e5038624ea8091b3e7748b5e8e0c84b1698db6c2"
dependencies = [
"find-crate",
"i18n-config",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "iana-time-zone"
version = "0.1.63"
@@ -1207,6 +1402,25 @@ dependencies = [
"similar",
]
[[package]]
name = "intl-memoizer"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "310da2e345f5eb861e7a07ee182262e94975051db9e4223e909ba90f392f163f"
dependencies = [
"type-map",
"unic-langid",
]
[[package]]
name = "intl_pluralrules"
version = "7.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "078ea7b7c29a2b4df841a7f6ac8775ff6074020c6776d48491ce2268e068f972"
dependencies = [
"unic-langid",
]
[[package]]
name = "io-uring"
version = "0.7.8"
@@ -1354,6 +1568,8 @@ dependencies = [
"chrono",
"divan",
"gtk4",
"i18n-embed",
"i18n-embed-fl",
"indexmap",
"lact-client",
"lact-daemon",
@@ -1364,6 +1580,7 @@ dependencies = [
"pretty_assertions",
"relm4",
"relm4-components",
"rust-embed",
"serde",
"serde_json",
"serde_yml",
@@ -1379,7 +1596,10 @@ dependencies = [
"amdgpu-sysfs",
"anyhow",
"clap",
"i18n-embed",
"i18n-embed-fl",
"indexmap",
"rust-embed",
"serde",
"serde-error",
"serde_json",
@@ -1808,6 +2028,29 @@ version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
[[package]]
name = "parking_lot"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets 0.52.6",
]
[[package]]
name = "pciid-parser"
version = "0.8.0"
@@ -1898,6 +2141,28 @@ dependencies = [
"toml_edit",
]
[[package]]
name = "proc-macro-error-attr2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
dependencies = [
"proc-macro2",
"quote",
]
[[package]]
name = "proc-macro-error2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
dependencies = [
"proc-macro-error-attr2",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.95"
@@ -2033,6 +2298,40 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422"
[[package]]
name = "rust-embed"
version = "8.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "025908b8682a26ba8d12f6f2d66b987584a4a87bc024abc5bbc12553a8cd178a"
dependencies = [
"rust-embed-impl",
"rust-embed-utils",
"walkdir",
]
[[package]]
name = "rust-embed-impl"
version = "8.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6065f1a4392b71819ec1ea1df1120673418bf386f50de1d6f54204d836d4349c"
dependencies = [
"proc-macro2",
"quote",
"rust-embed-utils",
"syn",
"walkdir",
]
[[package]]
name = "rust-embed-utils"
version = "8.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6cc0c81648b20b70c491ff8cce00c1c3b223bb8ed2b5d41f0e54c6c4c0a3594"
dependencies = [
"sha2",
"walkdir",
]
[[package]]
name = "rustc-demangle"
version = "0.1.25"
@@ -2094,6 +2393,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "self_cell"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749"
[[package]]
name = "semver"
version = "1.0.26"
@@ -2204,6 +2509,17 @@ dependencies = [
"version_check",
]
[[package]]
name = "sha2"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "sharded-slab"
version = "0.1.7"
@@ -2288,6 +2604,15 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "sys-locale"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4"
dependencies = [
"libc",
]
[[package]]
name = "system-deps"
version = "7.0.5"
@@ -2297,7 +2622,7 @@ dependencies = [
"cfg-expr",
"heck",
"pkg-config",
"toml",
"toml 0.8.23",
"version-compare",
]
@@ -2446,6 +2771,16 @@ dependencies = [
"time-core",
]
[[package]]
name = "tinystr"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
dependencies = [
"displaydoc",
"zerovec",
]
[[package]]
name = "tokio"
version = "1.46.1"
@@ -2477,6 +2812,15 @@ dependencies = [
"syn",
]
[[package]]
name = "toml"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
dependencies = [
"serde",
]
[[package]]
name = "toml"
version = "0.8.23"
@@ -2592,6 +2936,21 @@ dependencies = [
"syn",
]
[[package]]
name = "type-map"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb30dbbd9036155e74adad6812e9898d03ec374946234fbcebd5dfc7b9187b90"
dependencies = [
"rustc-hash",
]
[[package]]
name = "typenum"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
[[package]]
name = "uds_windows"
version = "1.1.0"
@@ -2603,6 +2962,25 @@ dependencies = [
"winapi",
]
[[package]]
name = "unic-langid"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28ba52c9b05311f4f6e62d5d9d46f094bd6e84cb8df7b3ef952748d752a7d05"
dependencies = [
"unic-langid-impl",
]
[[package]]
name = "unic-langid-impl"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dce1bf08044d4b7a94028c93786f8566047edc11110595914de93362559bc658"
dependencies = [
"serde",
"tinystr",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
@@ -3104,6 +3482,21 @@ dependencies = [
"syn",
]
[[package]]
name = "zerofrom"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
[[package]]
name = "zerovec"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428"
dependencies = [
"zerofrom",
]
[[package]]
name = "zvariant"
version = "5.6.0"
+6
View File
@@ -28,6 +28,12 @@ indexmap = { version = "2.5.0", features = ["serde"] }
pretty_assertions = "1.4.0"
divan = "0.1"
serde_yml = "0.0.12"
i18n-embed = { version = "0.16.0", features = [
"fluent-system",
"desktop-requester",
] }
i18n-embed-fl = "0.10.0"
rust-embed = { version = "8.7.2", features = ["debug-embed"] }
[profile.release]
strip = "symbols"
+481
View File
@@ -206,6 +206,19 @@
"dest": "cargo/vendor/anyhow-1.0.98",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/arc-swap/arc-swap-1.7.1.crate",
"sha256": "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457",
"dest": "cargo/vendor/arc-swap-1.7.1"
},
{
"type": "inline",
"contents": "{\"package\": \"69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457\", \"files\": {}}",
"dest": "cargo/vendor/arc-swap-1.7.1",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
@@ -284,6 +297,19 @@
"dest": "cargo/vendor/base64-0.22.1",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/basic-toml/basic-toml-0.1.10.crate",
"sha256": "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a",
"dest": "cargo/vendor/basic-toml-0.1.10"
},
{
"type": "inline",
"contents": "{\"package\": \"ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a\", \"files\": {}}",
"dest": "cargo/vendor/basic-toml-0.1.10",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
@@ -323,6 +349,19 @@
"dest": "cargo/vendor/bitflags-2.9.1",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/block-buffer/block-buffer-0.10.4.crate",
"sha256": "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71",
"dest": "cargo/vendor/block-buffer-0.10.4"
},
{
"type": "inline",
"contents": "{\"package\": \"3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71\", \"files\": {}}",
"dest": "cargo/vendor/block-buffer-0.10.4",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
@@ -614,6 +653,19 @@
"dest": "cargo/vendor/core2-0.4.0",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/cpufeatures/cpufeatures-0.2.17.crate",
"sha256": "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280",
"dest": "cargo/vendor/cpufeatures-0.2.17"
},
{
"type": "inline",
"contents": "{\"package\": \"59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280\", \"files\": {}}",
"dest": "cargo/vendor/cpufeatures-0.2.17",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
@@ -640,6 +692,19 @@
"dest": "cargo/vendor/crossbeam-utils-0.8.21",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/crypto-common/crypto-common-0.1.6.crate",
"sha256": "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3",
"dest": "cargo/vendor/crypto-common-0.1.6"
},
{
"type": "inline",
"contents": "{\"package\": \"1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3\", \"files\": {}}",
"dest": "cargo/vendor/crypto-common-0.1.6",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
@@ -718,6 +783,32 @@
"dest": "cargo/vendor/diff-0.1.13",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/digest/digest-0.10.7.crate",
"sha256": "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292",
"dest": "cargo/vendor/digest-0.10.7"
},
{
"type": "inline",
"contents": "{\"package\": \"9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292\", \"files\": {}}",
"dest": "cargo/vendor/digest-0.10.7",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/displaydoc/displaydoc-0.2.5.crate",
"sha256": "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0",
"dest": "cargo/vendor/displaydoc-0.2.5"
},
{
"type": "inline",
"contents": "{\"package\": \"97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0\", \"files\": {}}",
"dest": "cargo/vendor/displaydoc-0.2.5",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
@@ -952,6 +1043,71 @@
"dest": "cargo/vendor/filetime-0.2.25",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/find-crate/find-crate-0.6.3.crate",
"sha256": "59a98bbaacea1c0eb6a0876280051b892eb73594fd90cf3b20e9c817029c57d2",
"dest": "cargo/vendor/find-crate-0.6.3"
},
{
"type": "inline",
"contents": "{\"package\": \"59a98bbaacea1c0eb6a0876280051b892eb73594fd90cf3b20e9c817029c57d2\", \"files\": {}}",
"dest": "cargo/vendor/find-crate-0.6.3",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/fluent/fluent-0.17.0.crate",
"sha256": "8137a6d5a2c50d6b0ebfcb9aaa91a28154e0a70605f112d30cb0cd4a78670477",
"dest": "cargo/vendor/fluent-0.17.0"
},
{
"type": "inline",
"contents": "{\"package\": \"8137a6d5a2c50d6b0ebfcb9aaa91a28154e0a70605f112d30cb0cd4a78670477\", \"files\": {}}",
"dest": "cargo/vendor/fluent-0.17.0",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/fluent-bundle/fluent-bundle-0.16.0.crate",
"sha256": "01203cb8918f5711e73891b347816d932046f95f54207710bda99beaeb423bf4",
"dest": "cargo/vendor/fluent-bundle-0.16.0"
},
{
"type": "inline",
"contents": "{\"package\": \"01203cb8918f5711e73891b347816d932046f95f54207710bda99beaeb423bf4\", \"files\": {}}",
"dest": "cargo/vendor/fluent-bundle-0.16.0",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/fluent-langneg/fluent-langneg-0.13.0.crate",
"sha256": "2c4ad0989667548f06ccd0e306ed56b61bd4d35458d54df5ec7587c0e8ed5e94",
"dest": "cargo/vendor/fluent-langneg-0.13.0"
},
{
"type": "inline",
"contents": "{\"package\": \"2c4ad0989667548f06ccd0e306ed56b61bd4d35458d54df5ec7587c0e8ed5e94\", \"files\": {}}",
"dest": "cargo/vendor/fluent-langneg-0.13.0",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/fluent-syntax/fluent-syntax-0.12.0.crate",
"sha256": "54f0d287c53ffd184d04d8677f590f4ac5379785529e5e08b1c8083acdd5c198",
"dest": "cargo/vendor/fluent-syntax-0.12.0"
},
{
"type": "inline",
"contents": "{\"package\": \"54f0d287c53ffd184d04d8677f590f4ac5379785529e5e08b1c8083acdd5c198\", \"files\": {}}",
"dest": "cargo/vendor/fluent-syntax-0.12.0",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
@@ -1186,6 +1342,19 @@
"dest": "cargo/vendor/gdk4-sys-0.9.6",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/generic-array/generic-array-0.14.7.crate",
"sha256": "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a",
"dest": "cargo/vendor/generic-array-0.14.7"
},
{
"type": "inline",
"contents": "{\"package\": \"85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a\", \"files\": {}}",
"dest": "cargo/vendor/generic-array-0.14.7",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
@@ -1472,6 +1641,58 @@
"dest": "cargo/vendor/hex-0.4.3",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/i18n-config/i18n-config-0.4.8.crate",
"sha256": "3e06b90c8a0d252e203c94344b21e35a30f3a3a85dc7db5af8f8df9f3e0c63ef",
"dest": "cargo/vendor/i18n-config-0.4.8"
},
{
"type": "inline",
"contents": "{\"package\": \"3e06b90c8a0d252e203c94344b21e35a30f3a3a85dc7db5af8f8df9f3e0c63ef\", \"files\": {}}",
"dest": "cargo/vendor/i18n-config-0.4.8",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/i18n-embed/i18n-embed-0.16.0.crate",
"sha256": "a217bbb075dcaefb292efa78897fc0678245ca67f265d12c351e42268fcb0305",
"dest": "cargo/vendor/i18n-embed-0.16.0"
},
{
"type": "inline",
"contents": "{\"package\": \"a217bbb075dcaefb292efa78897fc0678245ca67f265d12c351e42268fcb0305\", \"files\": {}}",
"dest": "cargo/vendor/i18n-embed-0.16.0",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/i18n-embed-fl/i18n-embed-fl-0.10.0.crate",
"sha256": "e598ed73b67db92f61e04672e599eef2991a262a40e1666735b8a86d2e7e9f30",
"dest": "cargo/vendor/i18n-embed-fl-0.10.0"
},
{
"type": "inline",
"contents": "{\"package\": \"e598ed73b67db92f61e04672e599eef2991a262a40e1666735b8a86d2e7e9f30\", \"files\": {}}",
"dest": "cargo/vendor/i18n-embed-fl-0.10.0",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/i18n-embed-impl/i18n-embed-impl-0.8.4.crate",
"sha256": "0f2cc0e0523d1fe6fc2c6f66e5038624ea8091b3e7748b5e8e0c84b1698db6c2",
"dest": "cargo/vendor/i18n-embed-impl-0.8.4"
},
{
"type": "inline",
"contents": "{\"package\": \"0f2cc0e0523d1fe6fc2c6f66e5038624ea8091b3e7748b5e8e0c84b1698db6c2\", \"files\": {}}",
"dest": "cargo/vendor/i18n-embed-impl-0.8.4",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
@@ -1563,6 +1784,32 @@
"dest": "cargo/vendor/insta-1.43.1",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/intl-memoizer/intl-memoizer-0.5.3.crate",
"sha256": "310da2e345f5eb861e7a07ee182262e94975051db9e4223e909ba90f392f163f",
"dest": "cargo/vendor/intl-memoizer-0.5.3"
},
{
"type": "inline",
"contents": "{\"package\": \"310da2e345f5eb861e7a07ee182262e94975051db9e4223e909ba90f392f163f\", \"files\": {}}",
"dest": "cargo/vendor/intl-memoizer-0.5.3",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/intl_pluralrules/intl_pluralrules-7.0.2.crate",
"sha256": "078ea7b7c29a2b4df841a7f6ac8775ff6074020c6776d48491ce2268e068f972",
"dest": "cargo/vendor/intl_pluralrules-7.0.2"
},
{
"type": "inline",
"contents": "{\"package\": \"078ea7b7c29a2b4df841a7f6ac8775ff6074020c6776d48491ce2268e068f972\", \"files\": {}}",
"dest": "cargo/vendor/intl_pluralrules-7.0.2",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
@@ -2231,6 +2478,32 @@
"dest": "cargo/vendor/parking-2.2.1",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/parking_lot/parking_lot-0.12.4.crate",
"sha256": "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13",
"dest": "cargo/vendor/parking_lot-0.12.4"
},
{
"type": "inline",
"contents": "{\"package\": \"70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13\", \"files\": {}}",
"dest": "cargo/vendor/parking_lot-0.12.4",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/parking_lot_core/parking_lot_core-0.9.11.crate",
"sha256": "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5",
"dest": "cargo/vendor/parking_lot_core-0.9.11"
},
{
"type": "inline",
"contents": "{\"package\": \"bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5\", \"files\": {}}",
"dest": "cargo/vendor/parking_lot_core-0.9.11",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
@@ -2374,6 +2647,32 @@
"dest": "cargo/vendor/proc-macro-crate-3.3.0",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/proc-macro-error-attr2/proc-macro-error-attr2-2.0.0.crate",
"sha256": "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5",
"dest": "cargo/vendor/proc-macro-error-attr2-2.0.0"
},
{
"type": "inline",
"contents": "{\"package\": \"96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5\", \"files\": {}}",
"dest": "cargo/vendor/proc-macro-error-attr2-2.0.0",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/proc-macro-error2/proc-macro-error2-2.0.1.crate",
"sha256": "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802",
"dest": "cargo/vendor/proc-macro-error2-2.0.1"
},
{
"type": "inline",
"contents": "{\"package\": \"11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802\", \"files\": {}}",
"dest": "cargo/vendor/proc-macro-error2-2.0.1",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
@@ -2569,6 +2868,45 @@
"dest": "cargo/vendor/rle-decode-fast-1.0.3",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/rust-embed/rust-embed-8.7.2.crate",
"sha256": "025908b8682a26ba8d12f6f2d66b987584a4a87bc024abc5bbc12553a8cd178a",
"dest": "cargo/vendor/rust-embed-8.7.2"
},
{
"type": "inline",
"contents": "{\"package\": \"025908b8682a26ba8d12f6f2d66b987584a4a87bc024abc5bbc12553a8cd178a\", \"files\": {}}",
"dest": "cargo/vendor/rust-embed-8.7.2",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/rust-embed-impl/rust-embed-impl-8.7.2.crate",
"sha256": "6065f1a4392b71819ec1ea1df1120673418bf386f50de1d6f54204d836d4349c",
"dest": "cargo/vendor/rust-embed-impl-8.7.2"
},
{
"type": "inline",
"contents": "{\"package\": \"6065f1a4392b71819ec1ea1df1120673418bf386f50de1d6f54204d836d4349c\", \"files\": {}}",
"dest": "cargo/vendor/rust-embed-impl-8.7.2",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/rust-embed-utils/rust-embed-utils-8.7.2.crate",
"sha256": "f6cc0c81648b20b70c491ff8cce00c1c3b223bb8ed2b5d41f0e54c6c4c0a3594",
"dest": "cargo/vendor/rust-embed-utils-8.7.2"
},
{
"type": "inline",
"contents": "{\"package\": \"f6cc0c81648b20b70c491ff8cce00c1c3b223bb8ed2b5d41f0e54c6c4c0a3594\", \"files\": {}}",
"dest": "cargo/vendor/rust-embed-utils-8.7.2",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
@@ -2673,6 +3011,19 @@
"dest": "cargo/vendor/scopeguard-1.2.0",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/self_cell/self_cell-1.2.0.crate",
"sha256": "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749",
"dest": "cargo/vendor/self_cell-1.2.0"
},
{
"type": "inline",
"contents": "{\"package\": \"0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749\", \"files\": {}}",
"dest": "cargo/vendor/self_cell-1.2.0",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
@@ -2803,6 +3154,19 @@
"dest": "cargo/vendor/serde_yml-0.0.12",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/sha2/sha2-0.10.9.crate",
"sha256": "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283",
"dest": "cargo/vendor/sha2-0.10.9"
},
{
"type": "inline",
"contents": "{\"package\": \"a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283\", \"files\": {}}",
"dest": "cargo/vendor/sha2-0.10.9",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
@@ -2946,6 +3310,19 @@
"dest": "cargo/vendor/syn-2.0.104",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/sys-locale/sys-locale-0.3.2.crate",
"sha256": "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4",
"dest": "cargo/vendor/sys-locale-0.3.2"
},
{
"type": "inline",
"contents": "{\"package\": \"8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4\", \"files\": {}}",
"dest": "cargo/vendor/sys-locale-0.3.2",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
@@ -3141,6 +3518,19 @@
"dest": "cargo/vendor/time-macros-0.2.22",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/tinystr/tinystr-0.8.1.crate",
"sha256": "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b",
"dest": "cargo/vendor/tinystr-0.8.1"
},
{
"type": "inline",
"contents": "{\"package\": \"5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b\", \"files\": {}}",
"dest": "cargo/vendor/tinystr-0.8.1",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
@@ -3167,6 +3557,19 @@
"dest": "cargo/vendor/tokio-macros-2.5.0",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/toml/toml-0.5.11.crate",
"sha256": "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234",
"dest": "cargo/vendor/toml-0.5.11"
},
{
"type": "inline",
"contents": "{\"package\": \"f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234\", \"files\": {}}",
"dest": "cargo/vendor/toml-0.5.11",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
@@ -3297,6 +3700,32 @@
"dest": "cargo/vendor/tracker-macros-0.2.2",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/type-map/type-map-0.5.1.crate",
"sha256": "cb30dbbd9036155e74adad6812e9898d03ec374946234fbcebd5dfc7b9187b90",
"dest": "cargo/vendor/type-map-0.5.1"
},
{
"type": "inline",
"contents": "{\"package\": \"cb30dbbd9036155e74adad6812e9898d03ec374946234fbcebd5dfc7b9187b90\", \"files\": {}}",
"dest": "cargo/vendor/type-map-0.5.1",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/typenum/typenum-1.18.0.crate",
"sha256": "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f",
"dest": "cargo/vendor/typenum-1.18.0"
},
{
"type": "inline",
"contents": "{\"package\": \"1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f\", \"files\": {}}",
"dest": "cargo/vendor/typenum-1.18.0",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
@@ -3310,6 +3739,32 @@
"dest": "cargo/vendor/uds_windows-1.1.0",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/unic-langid/unic-langid-0.9.6.crate",
"sha256": "a28ba52c9b05311f4f6e62d5d9d46f094bd6e84cb8df7b3ef952748d752a7d05",
"dest": "cargo/vendor/unic-langid-0.9.6"
},
{
"type": "inline",
"contents": "{\"package\": \"a28ba52c9b05311f4f6e62d5d9d46f094bd6e84cb8df7b3ef952748d752a7d05\", \"files\": {}}",
"dest": "cargo/vendor/unic-langid-0.9.6",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/unic-langid-impl/unic-langid-impl-0.9.6.crate",
"sha256": "dce1bf08044d4b7a94028c93786f8566047edc11110595914de93362559bc658",
"dest": "cargo/vendor/unic-langid-impl-0.9.6"
},
{
"type": "inline",
"contents": "{\"package\": \"dce1bf08044d4b7a94028c93786f8566047edc11110595914de93362559bc658\", \"files\": {}}",
"dest": "cargo/vendor/unic-langid-impl-0.9.6",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
@@ -4038,6 +4493,32 @@
"dest": "cargo/vendor/zerocopy-derive-0.8.26",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/zerofrom/zerofrom-0.1.6.crate",
"sha256": "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5",
"dest": "cargo/vendor/zerofrom-0.1.6"
},
{
"type": "inline",
"contents": "{\"package\": \"50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5\", \"files\": {}}",
"dest": "cargo/vendor/zerofrom-0.1.6",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/zerovec/zerovec-0.11.2.crate",
"sha256": "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428",
"dest": "cargo/vendor/zerovec-0.11.2"
},
{
"type": "inline",
"contents": "{\"package\": \"4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428\", \"files\": {}}",
"dest": "cargo/vendor/zerovec-0.11.2",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
+4
View File
@@ -32,6 +32,10 @@ adw = { package = "libadwaita", version = "0.7.1", features = [
relm4 = { version = "0.9.0", default-features = false }
relm4-components = "0.9.0"
i18n-embed = { workspace = true }
i18n-embed-fl = { workspace = true }
rust-embed = { workspace = true }
plotters = { version = "0.3.5", default-features = false, features = [
"line_series",
"full_palette",
+4
View File
@@ -0,0 +1,4 @@
fallback_language = "en"
[fluent]
assets_dir = "i18n"
+137
View File
@@ -0,0 +1,137 @@
info-page = Information
oc-page = OC
thermals-page = Thermals
software-page = Software
hardware-info = Hardware Information
system-section = System
lact-daemon = LACT Daemon
lact-gui = LACT GUI
kernel-version = Kernel Version
instance = Instance
device-name = Device Name
platform-name = Platform Name
api-version = API Version
version = Version
driver-name = Driver Name
driver-version = Driver Version
compute-units = Compute Units
cl-c-version = OpenCL C Version
workgroup-size = Workgroup Size
global-memory = Global Memory
local-memory = Local Memory
features = Features
extensions = Extensions
show-button = Show
device-not-found = {$kind} device not found
monitoring-section = Monitoring
fan-control-section = Fan Control
temperatures = Temperatures
oc-missing-fan-control-warning = Warning: Overclocking support is disabled, fan control functionality is not available.
fan-speed = Fan Speed
throttling = Throttling
auto-page = Automatic
curve-page = Curve
static-page = Static
target-temp = Target temperature (°C)
acoustic-limit = Acoustic Limit (RPM)
acoustic-target = Acoustic Target (RPM)
min-fan-speed = Minimum Fan Speed (%)
zero-rpm = Zero RPM
zero-rpm-stop-temp = Zero RPM stop temperature (°C)
static-speed = Static Speed (%)
reset-button = Reset
pmfw-reset-warning = Warning: this resets the fan firmware settings!
amd-oc-disabled =
AMD Overclocking support is not enabled!
You can still change basic settings, but the more advanced clocks and voltage control will not be available.
enable-amd-oc = Enable AMD Overclocking
enable-amd-oc-description = This will enable the overdrive feature of the amdgpu driver by creating a file at <b>{$path}</b> and updating the initramfs. Are you sure you want to do this?
disable-amd-oc = Disable AMD Overclocking
disable-amd-oc-description = This will disable AMD overclocking support (overdrive) on next reboot.
reset-config = Reset Configuration
reset-config-description = Are you sure you want to reset all GPU configuration?
power-cap = Power Usage Limit
watt = W
ghz = GHz
mhz = MHz
mebibyte = MiB
stats-section = Statistics
gpu-clock = GPU Core Clock
gpu-clock-avg = GPU Core Clock (Average)
gpu-clock-target = GPU Core Clock (Target)
gpu-voltage = GPU Voltage
gpu-temp = GPU Temperature
gpu-usage = GPU Usage
vram-clock = VRAM Clock
power-usage = Power Usage
no-throttling = No
unknown-throttling = Unknown
missing-stat = N/A
performance-level-auto = Automatic
performance-level-high = Highest Clocks
performance-level-low = Lowest Clocks
performance-level-manual = Manual
performance-level-auto-description = Automatically adjust GPU and VRAM clocks. (Default)
performance-level-high-description = Always use the highest clockspeeds for GPU and VRAM.
performance-level-low-description = Always use the lowest clockspeeds for GPU and VRAM.
performance-level-manual-description = Manual performance control.
power-profile-mode = Power Profile Mode:
manual-level-needed = Performance level has to be set to "manual" to use power states and modes
overclock-section = Clockspeed and Voltage
nvidia-oc-info = Nvidia Overclocking Information
nvidia-oc-description =
Overclocking functionality on Nvidia includes setting offsets for GPU/VRAM clockspeeds and limiting the potential range of clockspeeds using the "locked clocks" feature.
On many cards, the VRAM clockpeed offset will only affect the actual memory clockspeed by half of the offset value.
For example, a +1000MHz VRAM offset may only increase the measured VRAM speed by 500MHz.
This is normal, and is how Nvidia handles GDDR data rates. Adjust your overclock accordingly.
Direct voltage control is not supported, as it does not exist in the Nvidia Linux driver.
It is possible to achieve a pseudo-undervolt by combining the locked clocks option with a positive clockspeed offset.
This will force the GPU to run at a voltage that's constrained by the locked clocks, while achieving a higher clockspeed due to the offset.
This can cause system instability if pushed too high.
oc-warning = Warning: changing these values may lead to system instability and can potentially damage your hardware!
show-all-pstates = Show all P-States
enable-gpu-locked-clocks = Enable GPU Locked Clocks
enable-vram-locked-clocks = Enable VRAM Locked Clocks
pstate-list-description = <b>The following values are clock offsets for each P-State, going from highest to lowest.</b>
no-clocks-data = No clocks data available
reset-oc-tooltip = Warning: this resets all clock settings to defaults!
gpu-clock-offset = GPU Clock Offset (MHz)
max-gpu-clock = Maximum GPU Clock (MHz)
max-vram-clock = Maximum VRAM Clock (MHz)
max-gpu-voltage = Maximum GPU Voltage (mV)
min-gpu-clock = Minimum GPU Clock (MHz)
min-vram-clock = Minimum VRAM Clock (MHz)
min-gpu-voltage = Minimum GPU Voltage (mV)
gpu-voltage-offset = GPU voltage offset (mV)
gpu-pstate-clock-offset = GPU P-State {$pstate} Clock Offset (MHz)
vram-pstate-clock-offset = VRAM P-State {$pstate} Clock Offset (MHz)
pstates = Power States
gpu-pstates = GPU Power States
vram-pstates = VRAM Power States
pstates-manual-needed = Note: performance level must be set to 'manual' to toggle power states
enable-pstate-config = Enable power state configuration
show-historical-charts = Show historical charts
show-process-montor = Show process monitor
settings-profile = Settings Profile
auto-switch-profiles = Switch automatically
add-profile = Add new profile
import-profile = Import profile from file
+8
View File
@@ -0,0 +1,8 @@
info-page = Інформація
oc-page = Розгін
thermals-page = Температура
software-page = Програми
hardware-info = Інформація про обладнання
watt = Вт
+10 -9
View File
@@ -11,7 +11,7 @@ mod process_monitor;
use crate::{
app::process_monitor::{ProcessMonitorWindow, ProcessMonitorWindowMsg},
APP_ID, GUI_VERSION,
APP_ID, GUI_VERSION, I18N,
};
use anyhow::{anyhow, Context};
use apply_revealer::{ApplyRevealer, ApplyRevealerMsg};
@@ -31,6 +31,7 @@ use header::{
profile_rule_window::{profile_row::ProfileRuleRowMsg, ProfileRuleWindowMsg},
Header, HeaderMsg,
};
use i18n_embed_fl::fl;
use lact_client::{ConnectionStatusMsg, DaemonClient};
use lact_schema::{
args::GuiArgs,
@@ -130,10 +131,10 @@ impl AsyncComponent for AppModel {
add_binding: (&model.ui_sensitive, "sensitive"),
add_titled[Some("info_page"), "Information"] = model.info_page.widget(),
add_titled[Some("oc_page"), "OC"] = model.oc_page.widget(),
add_titled[Some("thermals_page"), "Thermals"] = model.thermals_page.widget(),
add_titled[Some("software_page"), "Software"] = model.software_page.widget(),
add_titled[Some("info_page"), &fl!(I18N, "info-page")] = model.info_page.widget(),
add_titled[Some("oc_page"), &fl!(I18N, "oc-page")] = model.oc_page.widget(),
add_titled[Some("thermals_page"), &fl!(I18N, "thermals-page")] = model.thermals_page.widget(),
add_titled[Some("software_page"), &fl!(I18N, "software-page")] = model.software_page.widget(),
},
model.apply_revealer.widget(),
@@ -1197,8 +1198,8 @@ fn register_actions(sender: &AsyncComponentSender<AppModel>) {
DisableOverdrive,
AppMsg::ask_confirmation(
AppMsg::DisableOverdrive,
"Disable Overclocking",
"This will disable AMD overclocking support (overdrive) on next reboot.",
fl!(I18N, "disable-amd-oc"),
fl!(I18N, "disable-amd-oc-description"),
gtk::ButtonsType::OkCancel,
)
),
@@ -1206,8 +1207,8 @@ fn register_actions(sender: &AsyncComponentSender<AppModel>) {
ResetConfig,
AppMsg::ask_confirmation(
AppMsg::ResetConfig,
"Reset configuration",
"Are you sure you want to reset all GPU configuration?",
fl!(I18N, "reset-config"),
fl!(I18N, "reset-config-description"),
gtk::ButtonsType::YesNo,
)
),
+2 -2
View File
@@ -3,7 +3,7 @@ use relm4::{ComponentParts, ComponentSender, SimpleComponent};
#[derive(Clone, Debug)]
pub struct ConfirmationOptions {
pub title: &'static str,
pub title: String,
pub message: String,
pub buttons_type: gtk::ButtonsType,
}
@@ -19,7 +19,7 @@ impl SimpleComponent for ConfirmationDialog {
view! {
gtk::MessageDialog {
set_transient_for: Some(&parent),
set_title: Some(options.title),
set_title: Some(&options.title),
set_use_markup: true,
connect_response[sender] => move |diag, response| {
+6 -5
View File
@@ -5,13 +5,14 @@ pub mod profile_rule_window;
use crate::{
app::{header::profile_rule_window::ProfileEditParams, ShowProcessMonitor, APP_BROKER},
CONFIG,
CONFIG, I18N,
};
use super::{AppMsg, DebugSnapshot, DisableOverdrive, DumpVBios, ResetConfig, ShowGraphsWindow};
use glib::clone;
use gtk::prelude::*;
use gtk::*;
use i18n_embed_fl::fl;
use lact_client::schema::DeviceListEntry;
use lact_schema::ProfilesInfo;
use new_profile_dialog::NewProfileDialog;
@@ -89,7 +90,7 @@ impl Component for Header {
},
gtk::Frame {
set_label: Some("Settings Profile"),
set_label: Some(&fl!(I18N, "settings-profile")),
set_label_align: 0.05,
set_margin_all: 5,
@@ -98,7 +99,7 @@ impl Component for Header {
set_spacing: 5,
gtk::CheckButton {
set_label: Some("Switch automatically"),
set_label: Some(&fl!(I18N, "auto-switch-profiles")),
set_margin_horizontal: 5,
#[watch]
#[block_signal(toggle_auto_profile_handler)]
@@ -125,13 +126,13 @@ impl Component for Header {
gtk::Button {
set_expand: true,
set_icon_name: "list-add",
set_tooltip: "Add new profile",
set_tooltip: &fl!(I18N, "add-profile"),
connect_clicked => HeaderMsg::CreateProfile,
},
gtk::Button {
set_icon_name: "document-import-symbolic",
set_tooltip: "Import profile from file",
set_tooltip: &fl!(I18N, "import-profile"),
set_expand: true,
connect_clicked => HeaderMsg::ImportProfile,
}
+1
View File
@@ -136,6 +136,7 @@ mod imp {
}
obj.bind_property("name", &name_label, "label")
.transform_to(|_, value: &str| Some(format!("{value}:")))
.sync_create()
.build();
+1 -1
View File
@@ -52,7 +52,7 @@ pub enum AppMsg {
impl AppMsg {
pub fn ask_confirmation(
inner: AppMsg,
title: &'static str,
title: String,
message: impl Into<String>,
buttons_type: gtk::ButtonsType,
) -> Self {
+6 -39
View File
@@ -1,6 +1,9 @@
use super::PageUpdate;
use crate::app::{info_row::InfoRow, page_section::PageSection};
use crate::app::info_row::InfoRowItem;
use crate::app::page_section::PageSection;
use crate::I18N;
use gtk::prelude::*;
use i18n_embed_fl::fl;
use lact_schema::{DeviceInfo, DeviceStats};
use relm4::{prelude::FactoryVecDeque, ComponentParts, ComponentSender, RelmWidgetExt};
use std::sync::Arc;
@@ -26,7 +29,7 @@ impl relm4::SimpleComponent for InformationPage {
set_spacing: 15,
set_margin_horizontal: 20,
PageSection::new("Hardware Information") {
PageSection::new(&fl!(I18N, "hardware-info")) {
append = &model.values_list.widget().clone() -> gtk::Box {
set_spacing: 10,
set_orientation: gtk::Orientation::Vertical,
@@ -79,45 +82,9 @@ impl InformationPage {
None
};
values_list.push_back(InfoRowItem {
name: format!("{name}:"),
value,
note,
});
values_list.push_back(InfoRowItem { name, value, note });
}
}
}
}
}
struct InfoRowItem {
name: String,
value: String,
note: Option<&'static str>,
}
#[relm4::factory]
impl relm4::factory::FactoryComponent for InfoRowItem {
type Init = Self;
type ParentWidget = gtk::Box;
type CommandOutput = ();
type Input = ();
type Output = ();
fn init_model(
init: Self::Init,
_index: &Self::Index,
_sender: relm4::FactorySender<Self>,
) -> Self {
init
}
view! {
InfoRow {
set_selectable: true,
set_name: self.name.clone(),
set_value: self.value.clone(),
set_info_text: self.note.unwrap_or_default(),
}
}
}
+9 -8
View File
@@ -5,7 +5,10 @@ mod power_cap_section;
mod power_states;
use super::PageUpdate;
use crate::app::{ext::RelmDefaultLauchable, msg::AppMsg};
use crate::{
app::{ext::RelmDefaultLauchable, msg::AppMsg},
I18N,
};
use amdgpu_sysfs::gpu_handle::{
power_profile_mode::PowerProfileModesTable, PerformanceLevel, PowerLevelKind,
};
@@ -15,6 +18,7 @@ use gtk::{
pango,
prelude::{BoxExt, ButtonExt, FrameExt, OrientableExt, WidgetExt},
};
use i18n_embed_fl::fl;
use indexmap::IndexMap;
use lact_daemon::BASE_MODULE_CONF_PATH;
use lact_schema::{request::SetClocksCommand, ClocksTable, DeviceInfo, PowerStates, SystemInfo};
@@ -24,9 +28,6 @@ use power_states::power_states_frame::{PowerStatesFrame, PowerStatesFrameMsg};
use relm4::{ComponentController, ComponentParts, ComponentSender, RelmWidgetExt};
use std::sync::Arc;
const OVERCLOCKING_DISABLED_TEXT: &str = "AMD Overclocking support is not enabled! \
You can still change basic settings, but the more advanced clocks and voltage control will not be available.";
pub struct OcPage {
stats_section: relm4::Controller<GpuStatsSection>,
system_info: SystemInfo,
@@ -80,20 +81,20 @@ impl relm4::Component for OcPage {
set_margin_all: 10,
gtk::Label {
set_markup: OVERCLOCKING_DISABLED_TEXT,
set_markup: &fl!(I18N, "amd-oc-disabled"),
set_wrap: true,
set_wrap_mode: pango::WrapMode::Word,
},
gtk::Button {
set_label: "Enable AMD Overclocking",
set_label: &fl!(I18N, "enable-amd-oc"),
set_halign: gtk::Align::End,
connect_clicked[sender] => move |_| {
sender.output(AppMsg::ask_confirmation(
AppMsg::EnableOverdrive,
"Enable AMD Overclocking",
format!("This will enable the overdrive feature of the amdgpu driver by creating a file at <b>{BASE_MODULE_CONF_PATH}</b> and updating the initramfs. Are you sure you want to do this?"),
fl!(I18N, "enable-amd-oc"),
fl!(I18N, "enable-amd-oc-description", path = BASE_MODULE_CONF_PATH),
gtk::ButtonsType::OkCancel,
)).expect("Channel closed");
}
+14 -27
View File
@@ -2,7 +2,7 @@ mod adjustment_row;
use crate::{
app::{msg::AppMsg, page_section::PageSection},
APP_BROKER,
APP_BROKER, I18N,
};
use adjustment_row::{ClockAdjustmentRow, ClockAdjustmentRowMsg, ClocksData};
use amdgpu_sysfs::gpu_handle::overdrive::{ClocksTable as _, ClocksTableGen as AmdClocksTable};
@@ -11,6 +11,7 @@ use gtk::{
pango,
prelude::{BoxExt, ButtonExt, CheckButtonExt, OrientableExt, WidgetExt},
};
use i18n_embed_fl::fl;
use lact_schema::{
request::{ClockspeedType, SetClocksCommand},
ClocksTable, IntelClocksTable, NvidiaClockOffset, NvidiaClocksTable,
@@ -22,20 +23,6 @@ use relm4::{
// This is only used on RDNA1 in practice
const DEFAULT_VOLTAGE_OFFSET_RANGE: i32 = 250;
const WARNING_TEXT: &str = "Warning: changing these values may lead to system instability and can potentially damage your hardware!";
const NVIDIA_OC_INFO_TEXT: &str = "\
Overclocking functionality on Nvidia includes setting offsets for GPU/VRAM clockspeeds and limiting the potential range of clockspeeds using the \"locked clocks\" feature.
On many cards, the VRAM clockpeed offset will only affect the actual memory clockspeed by half of the offset value. \
For example, a +1000MHz VRAM offset may only increase the measured VRAM speed by 500MHz. \
This is normal, and is how Nvidia handles GDDR data rates. Adjust your overclock accordingly.
Direct voltage control is not supported, as it does not exist in the Nvidia Linux driver.
It is possible to achieve a pseudo-undervolt by combining the locked clocks option with a positive clockspeed offset. \
This will force the GPU to run at a voltage that's constrained by the locked clocks, while achieving a higher clockspeed due to the offset. \
This can cause system instability if pushed too high.\
";
pub struct ClocksFrame {
clocks: FactoryHashMap<ClockspeedType, ClockAdjustmentRow>,
@@ -61,9 +48,9 @@ impl relm4::Component for ClocksFrame {
type CommandOutput = ();
view! {
PageSection::new("Clockspeed and Voltage") {
PageSection::new(&fl!(I18N, "overclock-section")) {
append = &gtk::Label {
set_label: WARNING_TEXT,
set_label: &fl!(I18N, "oc-warning"),
set_wrap_mode: pango::WrapMode::Word,
set_halign: gtk::Align::Start,
set_margin_horizontal: 5,
@@ -83,7 +70,7 @@ impl relm4::Component for ClocksFrame {
set_popover = &gtk::Popover {
gtk::Label {
set_margin_all: 5,
set_markup: NVIDIA_OC_INFO_TEXT,
set_markup: &fl!(I18N, "nvidia-oc-description"),
set_wrap: true,
set_max_width_chars: 75,
}
@@ -91,7 +78,7 @@ impl relm4::Component for ClocksFrame {
},
append = &gtk::Label {
set_label: "Nvidia Overclocking Information",
set_label: &fl!(I18N, "nvidia-oc-info"),
add_css_class: "heading",
},
},
@@ -109,12 +96,12 @@ impl relm4::Component for ClocksFrame {
set_hexpand: true,
append = &gtk::CheckButton {
set_label: Some("Show all P-States"),
set_label: Some(&fl!(I18N, "show-all-pstates")),
add_binding["active"]: &model.show_all_pstates,
},
append: gpu_locked_clocks_togglebutton = &gtk::CheckButton {
set_label: Some("Enable GPU Locked Clocks"),
set_label: Some(&fl!(I18N, "enable-gpu-locked-clocks")),
add_binding["active"]: &model.enable_gpu_locked_clocks,
connect_toggled => move |_| {
APP_BROKER.send(AppMsg::SettingsChanged);
@@ -122,7 +109,7 @@ impl relm4::Component for ClocksFrame {
},
append: vram_locked_clocks_togglebutton = &gtk::CheckButton {
set_label: Some("Enable VRAM Locked Clocks"),
set_label: Some(&fl!(I18N, "enable-vram-locked-clocks")),
add_binding["active"]: &model.enable_vram_locked_clocks,
connect_toggled => move |_| {
APP_BROKER.send(AppMsg::SettingsChanged);
@@ -134,7 +121,7 @@ impl relm4::Component for ClocksFrame {
add_binding["visible"]: &model.show_all_pstates,
set_margin_horizontal: 5,
set_markup: "<b>The following values are clock offsets for each P-State, going from highest to lowest.</b>",
set_markup: &fl!(I18N, "pstate-list-description"),
set_wrap_mode: pango::WrapMode::Word,
set_halign: gtk::Align::Start,
},
@@ -147,7 +134,7 @@ impl relm4::Component for ClocksFrame {
},
append = &gtk::Label {
set_label: "No clocks data available",
set_label: &fl!(I18N, "no-clocks-data"),
set_margin_horizontal: 10,
set_halign: gtk::Align::Start,
#[watch]
@@ -155,10 +142,10 @@ impl relm4::Component for ClocksFrame {
},
append = &gtk::Button {
set_label: "Reset",
set_label: &fl!(I18N, "reset-button"),
set_halign: gtk::Align::End,
set_margin_horizontal: 5,
set_tooltip_text: Some("Warning: this resets all clock settings to defaults!"),
set_tooltip_text: Some(&fl!(I18N, "reset-oc-tooltip")),
set_css_classes: &["destructive-action"],
#[watch]
set_visible: !model.clocks.is_empty(),
@@ -319,7 +306,7 @@ impl ClocksFrame {
current: sclk_offset,
min: sclk_offset_min,
max: sclk_offset_max,
custom_title: Some("Maximum GPU Clock Offset (MHz)"),
custom_title: Some(fl!(I18N, "gpu-clock-offset")),
is_secondary: false,
},
);
@@ -1,17 +1,18 @@
use crate::{
app::{msg::AppMsg, pages::oc_adjustment::OcAdjustment},
APP_BROKER,
APP_BROKER, I18N,
};
use gtk::{
glib::{object::ObjectExt, SignalHandlerId},
prelude::{AdjustmentExt, OrientableExt, RangeExt, ScaleExt, WidgetExt},
};
use i18n_embed_fl::fl;
use lact_schema::request::ClockspeedType;
use relm4::{prelude::FactoryComponent, RelmWidgetExt};
pub struct ClockAdjustmentRow {
clock_type: ClockspeedType,
custom_title: Option<&'static str>,
custom_title: Option<String>,
value_ratio: f64,
change_signal: SignalHandlerId,
adjustment: OcAdjustment,
@@ -22,7 +23,7 @@ pub struct ClocksData {
pub current: i32,
pub min: i32,
pub max: i32,
pub custom_title: Option<&'static str>,
pub custom_title: Option<String>,
pub is_secondary: bool,
}
@@ -64,19 +65,19 @@ impl FactoryComponent for ClockAdjustmentRow {
gtk::Label {
set_xalign: 0.0,
#[watch]
set_markup: &match self.custom_title {
Some(title) => title.to_owned(),
set_markup: &match &self.custom_title {
Some(title) => title.clone(),
None => {
match self.clock_type {
ClockspeedType::MaxCoreClock => "Maximum GPU Clock (MHz)".to_owned(),
ClockspeedType::MaxMemoryClock => "Maximum VRAM Clock (MHz)".to_owned(),
ClockspeedType::MaxVoltage => "Maximum GPU voltage (mV)".to_owned(),
ClockspeedType::MinCoreClock => "Minimum GPU Clock (MHz)".to_owned(),
ClockspeedType::MinMemoryClock => "Minimum VRAM Clock (MHz)".to_owned(),
ClockspeedType::MinVoltage => "Minimum GPU voltage (mV)".to_owned(),
ClockspeedType::VoltageOffset => "GPU voltage offset (mV)".to_owned(),
ClockspeedType::GpuClockOffset(pstate) => format!("GPU P-State {pstate} Clock Offset (MHz)"),
ClockspeedType::MemClockOffset(pstate) => format!("VRAM P-State {pstate} Clock Offset (MHz)"),
ClockspeedType::MaxCoreClock => fl!(I18N, "max-gpu-clock"),
ClockspeedType::MaxMemoryClock => fl!(I18N, "max-vram-clock"),
ClockspeedType::MaxVoltage => fl!(I18N, "max-gpu-voltage"),
ClockspeedType::MinCoreClock => fl!(I18N, "min-gpu-clock"),
ClockspeedType::MinMemoryClock => fl!(I18N, "min-vram-clock"),
ClockspeedType::MinVoltage => fl!(I18N, "min-gpu-voltage"),
ClockspeedType::VoltageOffset => fl!(I18N, "gpu-voltage-offset"),
ClockspeedType::GpuClockOffset(pstate) => fl!(I18N, "gpu-pstate-clock-offset", pstate = pstate),
ClockspeedType::MemClockOffset(pstate) => fl!(I18N, "vram-pstate-clock-offset", pstate = pstate),
ClockspeedType::Reset => unreachable!(),
}
}
@@ -1,7 +1,9 @@
use crate::app::{
ext::FlowBoxExt, info_row::InfoRow, page_section::PageSection, pages::PageUpdate,
};
use crate::I18N;
use gtk::prelude::{ActionableExt, BoxExt, ButtonExt, OrientableExt, WidgetExt};
use i18n_embed_fl::fl;
use lact_schema::{DeviceStats, PowerStats};
use relm4::{ComponentParts, ComponentSender};
use std::{fmt::Write, sync::Arc};
@@ -18,7 +20,7 @@ impl relm4::SimpleComponent for GpuStatsSection {
type Output = ();
view! {
PageSection::new("Statistics") {
PageSection::new(&fl!(I18N, "stats-section")) {
append = &gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
set_spacing: 5,
@@ -44,9 +46,10 @@ impl relm4::SimpleComponent for GpuStatsSection {
add_overlay = &gtk::Label {
#[watch]
set_label: &format!(
"{}/{} MiB",
"{}/{} {}",
model.stats.vram.used.unwrap_or(0) / 1024 / 1024,
model.stats.vram.total.unwrap_or(0) / 1024 / 1024,
fl!(I18N, "mebibyte")
),
}
},
@@ -60,7 +63,15 @@ impl relm4::SimpleComponent for GpuStatsSection {
set_selection_mode: gtk::SelectionMode::None,
append_child = &InfoRow {
set_name: "GPU Core Clock (Average):",
#[watch]
set_name: {
if model.stats.clockspeed.gpu_clockspeed.is_some()
&& model.stats.clockspeed.current_gfxclk.is_some() {
fl!(I18N, "gpu-clock-avg")
} else {
fl!(I18N, "gpu-clock")
}
},
#[watch]
set_value: format_clockspeed(model.stats.clockspeed.gpu_clockspeed, 1.0),
set_spacing: 40,
@@ -70,7 +81,7 @@ impl relm4::SimpleComponent for GpuStatsSection {
},
append_child = &InfoRow {
set_name: "GPU Core Clock (Target):",
set_name: fl!(I18N, "gpu-clock-target"),
#[watch]
set_value: format_current_gfxclk(model.stats.clockspeed.current_gfxclk),
set_spacing: 40,
@@ -80,7 +91,7 @@ impl relm4::SimpleComponent for GpuStatsSection {
},
append_child = &InfoRow {
set_name: "GPU Voltage:",
set_name: fl!(I18N, "gpu-voltage"),
#[watch]
set_value: format!("{:.3} V", model.stats.voltage.gpu.unwrap_or(0) as f64 / 1000f64),
set_spacing: 40,
@@ -90,15 +101,14 @@ impl relm4::SimpleComponent for GpuStatsSection {
},
append = &InfoRow {
#[watch]
set_name: "Temperature:",
set_name: fl!(I18N, "gpu-temp"),
#[watch]
set_value: temperature_text(&model.stats).unwrap_or_else(|| "N/A".to_owned()),
set_spacing: 40,
},
append_child = &InfoRow {
set_name: "GPU Memory Clock:",
set_name: fl!(I18N, "vram-clock"),
#[watch]
set_value: format_clockspeed(
model.stats.clockspeed.vram_clockspeed,
@@ -111,7 +121,7 @@ impl relm4::SimpleComponent for GpuStatsSection {
},
append_child = &InfoRow {
set_name: "GPU Usage:",
set_name: fl!(I18N, "gpu-usage"),
#[watch]
set_value: format!("{}%", model.stats.busy_percent.unwrap_or(0)),
set_spacing: 40,
@@ -121,7 +131,7 @@ impl relm4::SimpleComponent for GpuStatsSection {
},
append_child = &InfoRow {
set_name: "Power Usage:",
set_name: fl!(I18N, "power-usage"),
#[watch]
set_value: {
let PowerStats {
@@ -136,9 +146,10 @@ impl relm4::SimpleComponent for GpuStatsSection {
.or(power_average);
format!(
"<b>{:.1}/{} W</b>",
"<b>{:.1}/{} {}</b>",
power_current.unwrap_or(0.0),
power_cap_current.unwrap_or(0.0)
power_cap_current.unwrap_or(0.0),
fl!(I18N, "watt")
)
},
set_spacing: 40,
@@ -148,7 +159,7 @@ impl relm4::SimpleComponent for GpuStatsSection {
},
append = &InfoRow {
set_name: "Throttling:",
set_name: fl!(I18N, "throttling"),
#[watch]
set_value: throttling_text(&model.stats),
set_spacing: 40,
@@ -156,7 +167,7 @@ impl relm4::SimpleComponent for GpuStatsSection {
},
append = &gtk::Button {
set_label: "Show historical charts",
set_label: &fl!(I18N, "show-historical-charts"),
set_action_name: Some("app.show-graphs-window"),
},
}
@@ -190,7 +201,11 @@ impl relm4::SimpleComponent for GpuStatsSection {
}
fn format_clockspeed(value: Option<u64>, ratio: f64) -> String {
format!("{:.3} GHz", value.unwrap_or(0) as f64 / 1000.0 * ratio)
format!(
"{:.3} {}",
value.unwrap_or(0) as f64 / 1000.0 * ratio,
fl!(I18N, "ghz")
)
}
fn format_current_gfxclk(value: Option<u64>) -> String {
@@ -198,12 +213,12 @@ fn format_current_gfxclk(value: Option<u64>) -> String {
// if the APU/GPU dose not acually support current_gfxclk,
// the value will be `u16::MAX (65535)`
if v >= u16::MAX as u64 || v == 0 {
"N/A".to_string()
fl!(I18N, "missing-stat")
} else {
format_clockspeed(Some(v), 1.0)
}
} else {
"N/A".to_string()
fl!(I18N, "missing-stat")
}
}
@@ -211,7 +226,7 @@ pub fn throttling_text(stats: &DeviceStats) -> String {
match &stats.throttle_info {
Some(throttle_info) => {
if throttle_info.is_empty() {
"No".to_owned()
fl!(I18N, "no-throttling")
} else {
let type_text: Vec<String> = throttle_info
.iter()
@@ -227,7 +242,9 @@ pub fn throttling_text(stats: &DeviceStats) -> String {
type_text.join(", ")
}
}
None => "Unknown".to_owned(),
None => {
fl!(I18N, "unknown-throttling")
}
}
}
@@ -3,7 +3,7 @@ mod heuristics_list;
use super::OcPageMsg;
use crate::{
app::{msg::AppMsg, page_section::PageSection},
APP_BROKER,
APP_BROKER, I18N,
};
use amdgpu_sysfs::gpu_handle::{power_profile_mode::PowerProfileModesTable, PerformanceLevel};
use gtk::{
@@ -13,6 +13,7 @@ use gtk::{
StringObject,
};
use heuristics_list::PowerProfileHeuristicsList;
use i18n_embed_fl::fl;
use relm4::{Component, ComponentController, ComponentParts, ComponentSender, RelmWidgetExt};
const PERFORMANCE_LEVELS: [PerformanceLevel; 4] = [
@@ -58,18 +59,18 @@ impl relm4::Component for PerformanceFrame {
gtk::Label {
#[watch]
set_label: match model.performance_level {
Some(PerformanceLevel::Auto) => "Automatically adjust GPU and VRAM clocks. (Default)",
Some(PerformanceLevel::High) => "Always use the highest clockspeeds for GPU and VRAM.",
Some(PerformanceLevel::Low) => "Always use the lowest clockspeeds for GPU and VRAM.",
Some(PerformanceLevel::Manual) => "Manual performance control.",
_ => "",
set_label: &match model.performance_level {
Some(PerformanceLevel::Auto) => fl!(I18N, "performance-level-auto-description"),
Some(PerformanceLevel::High) => fl!(I18N, "performance-level-high-description"),
Some(PerformanceLevel::Low) => fl!(I18N, "performance-level-low-description"),
Some(PerformanceLevel::Manual) => fl!(I18N, "performance-level-manual-description"),
_ => String::new(),
},
set_hexpand: true,
set_halign: gtk::Align::End,
},
gtk::DropDown::from_strings(&PERFORMANCE_LEVELS.map(level_friendly_name)) {
gtk::DropDown::from_strings(&level_names_ref) {
#[watch]
#[block_signal(level_select_handler)]
set_selected: PERFORMANCE_LEVELS.iter().position(|level| model.performance_level == Some(*level)).unwrap_or(0) as u32,
@@ -90,7 +91,7 @@ impl relm4::Component for PerformanceFrame {
set_spacing: 10,
gtk::Label {
set_label: "Power Profile Mode:",
set_label: &fl!(I18N, "power-profile-mode"),
set_hexpand: true,
set_halign: gtk::Align::Start,
},
@@ -103,7 +104,7 @@ impl relm4::Component for PerformanceFrame {
#[wrap(Some)]
set_popover = &gtk::Popover {
gtk::Label {
set_label: "Performance level has to be set to \"manual\" to use power states and modes",
set_label: &fl!(I18N, "manual-level-needed"),
}
},
},
@@ -160,6 +161,12 @@ impl relm4::Component for PerformanceFrame {
_root: Self::Root,
sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
let level_names = PERFORMANCE_LEVELS.map(level_friendly_name);
let level_names_ref = level_names
.iter()
.map(|level| level.as_str())
.collect::<Vec<&str>>();
let model = Self {
performance_level: None,
power_profile_modes_table: None,
@@ -282,11 +289,11 @@ impl PerformanceFrame {
}
}
const fn level_friendly_name(level: PerformanceLevel) -> &'static str {
fn level_friendly_name(level: PerformanceLevel) -> String {
match level {
PerformanceLevel::Auto => "Automatic",
PerformanceLevel::Low => "Lowest Clocks",
PerformanceLevel::High => "Highest Clocks",
PerformanceLevel::Manual => "Manual",
PerformanceLevel::Auto => fl!(I18N, "performance-level-auto"),
PerformanceLevel::Low => fl!(I18N, "performance-level-low"),
PerformanceLevel::High => fl!(I18N, "performance-level-high"),
PerformanceLevel::Manual => fl!(I18N, "performance-level-manual"),
}
}
@@ -4,12 +4,13 @@ use crate::{
page_section::PageSection,
pages::{oc_adjustment::OcAdjustment, PageUpdate},
},
APP_BROKER,
APP_BROKER, I18N,
};
use gtk::{
glib::object::ObjectExt,
prelude::{AdjustmentExt, BoxExt, ButtonExt, OrientableExt, RangeExt, ScaleExt, WidgetExt},
};
use i18n_embed_fl::fl;
use lact_schema::PowerStats;
use relm4::{ComponentParts, ComponentSender, RelmWidgetExt};
use std::fmt::Write;
@@ -37,7 +38,7 @@ impl relm4::Component for PowerCapSection {
view! {
#[root]
PageSection::new("Power usage limit") {
PageSection::new(&fl!(I18N, "power-cap")) {
append = &gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
@@ -56,7 +57,7 @@ impl relm4::Component for PowerCapSection {
},
gtk::Button {
set_label: "Default",
set_label: &fl!(I18N, "reset-button"),
connect_clicked => PowerCapMsg::Reset,
},
}
@@ -113,9 +114,10 @@ impl relm4::Component for PowerCapSection {
self.value_text.clear();
write!(
self.value_text,
"{}/{} W",
"{}/{} {}",
self.adjustment.value(),
self.adjustment.upper()
self.adjustment.upper(),
fl!(I18N, "watt")
)
.unwrap();
}
@@ -6,13 +6,14 @@ use crate::{
PowerStatesListMsg, PowerStatesListOptions,
},
},
APP_BROKER,
APP_BROKER, I18N,
};
use amdgpu_sysfs::gpu_handle::{PerformanceLevel, PowerLevelKind};
use gtk::{
glib::{object::ObjectExt, SignalHandlerId},
prelude::{BoxExt, CheckButtonExt, OrientableExt, WidgetExt},
};
use i18n_embed_fl::fl;
use indexmap::IndexMap;
use lact_schema::{DeviceStats, PowerStates};
use relm4::{
@@ -52,7 +53,7 @@ impl relm4::SimpleComponent for PowerStatesFrame {
view! {
gtk::Expander {
set_label: Some("Power states"),
set_label: Some(&fl!(I18N, "pstates")),
add_binding: (&model.states_expanded, "expanded"),
gtk::Box {
@@ -62,7 +63,7 @@ impl relm4::SimpleComponent for PowerStatesFrame {
add_binding: (&model.states_configurable, "sensitive"),
gtk::Label {
set_label: "Note: performance level must be set to 'manual' to toggle power states",
set_label: &fl!(I18N, "pstates-manual-needed"),
set_margin_horizontal: 10,
set_halign: gtk::Align::Start,
#[watch]
@@ -70,7 +71,7 @@ impl relm4::SimpleComponent for PowerStatesFrame {
},
gtk::CheckButton {
set_label: Some("Enable power state configuration"),
set_label: Some(&fl!(I18N, "enable-pstate-config")),
add_binding: (&model.states_configured, "active"),
#[watch]
set_visible: model.performance_level.is_some(),
@@ -95,14 +96,14 @@ impl relm4::SimpleComponent for PowerStatesFrame {
) -> ComponentParts<Self> {
let core_states_list = PowerStatesList::builder()
.launch(PowerStatesListOptions {
title: "GPU Power States",
value_suffix: "MHz",
title: fl!(I18N, "gpu-pstates"),
value_suffix: fl!(I18N, "mhz"),
})
.detach();
let vram_states_list = PowerStatesList::builder()
.launch(PowerStatesListOptions {
title: "VRAM Power States",
value_suffix: "MHz",
title: fl!(I18N, "vram-pstates"),
value_suffix: fl!(I18N, "mhz"),
})
.detach();
@@ -8,12 +8,12 @@ use crate::{app::msg::AppMsg, APP_BROKER};
pub struct PowerStatesList {
states: FactoryVecDeque<PowerStateRow>,
value_suffix: &'static str,
value_suffix: String,
}
pub struct PowerStatesListOptions {
pub title: &'static str,
pub value_suffix: &'static str,
pub title: String,
pub value_suffix: String,
}
#[derive(Debug)]
@@ -31,7 +31,7 @@ impl relm4::SimpleComponent for PowerStatesList {
view! {
gtk::Frame {
set_hexpand: true,
set_label: Some(opts.title),
set_label: Some(&opts.title),
set_child: Some(model.states.widget()),
}
}
@@ -63,7 +63,7 @@ impl relm4::SimpleComponent for PowerStatesList {
power_state.value = (power_state.value as f64 * value_ratio) as u64;
let opts = PowerStateRowOptions {
power_state,
value_suffix: self.value_suffix,
value_suffix: self.value_suffix.clone(),
active: false,
};
states.push_back(opts);
@@ -97,12 +97,12 @@ struct PowerStateRow {
active: BoolBinding,
enabled: BoolBinding,
power_state: PowerState,
value_suffix: &'static str,
value_suffix: String,
}
pub struct PowerStateRowOptions {
pub power_state: PowerState,
pub value_suffix: &'static str,
pub value_suffix: String,
pub active: bool,
}
+26 -25
View File
@@ -2,9 +2,10 @@ mod vulkan;
use crate::{
app::{format_friendly_size, info_row::InfoRow, page_section::PageSection},
GUI_VERSION, REPO_URL,
GUI_VERSION, I18N, REPO_URL,
};
use gtk::prelude::*;
use i18n_embed_fl::fl;
use indexmap::IndexMap;
use lact_client::schema::{SystemInfo, GIT_COMMIT};
use lact_schema::{DeviceInfo, VulkanInfo};
@@ -42,10 +43,10 @@ impl relm4::SimpleComponent for SoftwarePage {
set_spacing: 15,
set_margin_horizontal: 20,
PageSection::new("System") {
append = &InfoRow::new_selectable("LACT Daemon:", &daemon_version),
append = &InfoRow::new_selectable("LACT GUI:", &gui_version),
append = &InfoRow::new_selectable("Kernel Version:", &system_info.kernel_version),
PageSection::new(&fl!(I18N, "system-section")) {
append = &InfoRow::new_selectable(&fl!(I18N, "lact-daemon"), &daemon_version),
append = &InfoRow::new_selectable(&fl!(I18N, "lact-gui"), &gui_version),
append = &InfoRow::new_selectable(&fl!(I18N, "kernel-version"), &system_info.kernel_version),
},
#[name = "vulkan_stack"]
@@ -61,32 +62,32 @@ impl relm4::SimpleComponent for SoftwarePage {
append = &gtk::Label {
set_halign: gtk::Align::Start,
set_hexpand: true,
set_label: "Instance:"
set_label: &fl!(I18N, "instance"),
},
append = model.vulkan_driver_selector.widget(),
},
append = &InfoRow {
set_name: "Device Name:",
set_name: fl!(I18N, "device-name"),
#[watch]
set_value: info.device_name.as_str(),
set_selectable: true,
},
append = &InfoRow {
set_name: "API Version:",
set_name: fl!(I18N, "api-version"),
#[watch]
set_value: info.api_version.as_str(),
set_selectable: true,
},
append = &InfoRow {
set_name: "Driver Name:",
set_name: fl!(I18N, "driver-name"),
#[watch]
set_value: info.driver.name.as_deref().unwrap_or_default(),
set_selectable: true,
},
append = &InfoRow {
set_name: "Driver Version:",
set_name: fl!(I18N, "driver-version"),
#[watch]
set_value: info.driver.info.as_deref().unwrap_or_default(),
set_selectable: true,
@@ -99,12 +100,12 @@ impl relm4::SimpleComponent for SoftwarePage {
append = &gtk::Label {
set_halign: gtk::Align::Start,
set_hexpand: true,
set_label: "Features:"
set_label: &format!("{}:", fl!(I18N, "features")),
},
append = &gtk::Button {
connect_clicked => SoftwarePageMsg::ShowVulkanFeatures,
set_label: "Show",
set_label: &fl!(I18N, "show-button"),
}
},
@@ -115,12 +116,12 @@ impl relm4::SimpleComponent for SoftwarePage {
append = &gtk::Label {
set_halign: gtk::Align::Start,
set_hexpand: true,
set_label: "Extensions:"
set_label: &format!("{}:", fl!(I18N, "extensions")),
},
append = &gtk::Button {
connect_clicked => SoftwarePageMsg::ShowVulkanExtensions,
set_label: "Show",
set_label: &fl!(I18N, "show-button"),
}
},
}
@@ -128,7 +129,7 @@ impl relm4::SimpleComponent for SoftwarePage {
None => {
PageSection::new("Vulkan") {
append = &gtk::Label {
set_label: "Vulkan device not found",
set_label: &fl!(I18N, "device-not-found", kind = "Vulkan"),
set_halign: gtk::Align::Start,
},
}
@@ -140,55 +141,55 @@ impl relm4::SimpleComponent for SoftwarePage {
Some(info) => {
PageSection::new("OpenCL") {
append = &InfoRow {
set_name: "Platform Name:",
set_name: fl!(I18N, "platform-name"),
#[watch]
set_value: info.platform_name.as_str(),
set_selectable: true,
},
append = &InfoRow {
set_name: "Device Name:",
set_name: fl!(I18N, "device-name"),
#[watch]
set_value: info.device_name.as_str(),
set_selectable: true,
},
append = &InfoRow {
set_name: "Version:",
set_name: fl!(I18N, "version"),
#[watch]
set_value: info.version.as_str(),
set_selectable: true,
},
append = &InfoRow {
set_name: "Driver Version:",
set_name: fl!(I18N, "driver-version"),
#[watch]
set_value: info.driver_version.as_str(),
set_selectable: true,
},
append = &InfoRow {
set_name: "OpenCL C Version:",
set_name: fl!(I18N, "cl-c-version"),
#[watch]
set_value: info.c_version.as_str(),
set_selectable: true,
},
append = &InfoRow {
set_name: "Compute Units:",
set_name: fl!(I18N, "compute-units"),
#[watch]
set_value: info.compute_units.to_string(),
set_selectable: true,
},
append = &InfoRow {
set_name: "Workgroup Size:",
set_name: fl!(I18N, "workgroup-size"),
#[watch]
set_value: info.workgroup_size.to_string(),
set_selectable: true,
},
append = &InfoRow {
set_name: "Global Memory:",
set_name: fl!(I18N, "global-memory"),
#[watch]
set_value: format_friendly_size(info.global_memory),
set_selectable: true,
},
append = &InfoRow {
set_name: "Local Memory:",
set_name: fl!(I18N, "local-memory"),
#[watch]
set_value: format_friendly_size(info.local_memory),
set_selectable: true,
@@ -198,7 +199,7 @@ impl relm4::SimpleComponent for SoftwarePage {
None => {
PageSection::new("OpenCL") {
append = &gtk::Label {
set_label: "OpenCL device not found",
set_label: &fl!(I18N, "device-not-found", kind = "OpenCL"),
set_halign: gtk::Align::Start,
},
}
+20 -19
View File
@@ -7,7 +7,7 @@ use super::{
};
use crate::{
app::{info_row::InfoRow, msg::AppMsg, page_section::PageSection},
APP_BROKER,
APP_BROKER, I18N,
};
use amdgpu_sysfs::gpu_handle::fan_control::FanInfo;
use fan_curve_frame::{
@@ -22,6 +22,7 @@ use gtk::{
prelude::{AdjustmentExt, BoxExt, ButtonExt, OrientableExt, RangeExt, ScaleExt, WidgetExt},
Adjustment,
};
use i18n_embed_fl::fl;
use lact_daemon::AMDGPU_FAMILY_GC_11_0_0;
use lact_schema::{
config::{FanControlSettings, FanCurve, GpuConfig},
@@ -107,31 +108,31 @@ impl relm4::Component for ThermalsPage {
set_visible: model.system_info.amdgpu_overdrive_enabled == Some(false) && model.has_pmfw,
gtk::Label {
set_label: "Warning: Overclocking support is disabled, fan control functionality is not available.",
set_label: &fl!(I18N, "oc-missing-fan-control-warning"),
},
},
PageSection::new("Monitoring") {
PageSection::new(&fl!(I18N, "monitoring-section")) {
append = &InfoRow {
set_name: "Temperatures:",
set_name: fl!(I18N, "temperatures"),
#[watch]
set_value: model.temperatures.as_deref().unwrap_or("No sensors found"),
},
append = &InfoRow {
set_name: "Fan Speed:",
set_name: fl!(I18N, "fan-speed"),
#[watch]
set_value: model.fan_speed.as_deref().unwrap_or("No fan detected"),
},
append = &InfoRow {
set_name: "Throttling:",
set_name: fl!(I18N, "throttling"),
#[watch]
set_value: model.throttling.as_str(),
},
},
PageSection::new("Fan Control") {
PageSection::new(&fl!(I18N, "fan-control-section")) {
// Disable fan configuration when overdrive is disabled on GPUs that have PMFW (RDNA3+)
#[watch]
set_sensitive: model.fan_speed.is_some() && !(model.system_info.amdgpu_overdrive_enabled == Some(false) && model.has_pmfw),
@@ -145,7 +146,7 @@ impl relm4::Component for ThermalsPage {
#[watch]
set_visible: model.fan_speed.is_some(),
add_titled[Some(AUTO_PAGE), "Automatic"] = &gtk::Box {
add_titled[Some(AUTO_PAGE), &fl!(I18N, "auto-page")] = &gtk::Box {
set_orientation: gtk::Orientation::Vertical,
set_spacing: 5,
@@ -156,7 +157,7 @@ impl relm4::Component for ThermalsPage {
#[template_child]
label {
set_label: "Target temperature (°C)",
set_label: &fl!(I18N, "target-temp"),
set_size_group: &label_size_group,
},
@@ -179,7 +180,7 @@ impl relm4::Component for ThermalsPage {
#[template_child]
label {
set_label: "Acoustic Limit (RPM)",
set_label: &fl!(I18N, "acoustic-limit"),
set_size_group: &label_size_group,
},
@@ -202,7 +203,7 @@ impl relm4::Component for ThermalsPage {
#[template_child]
label {
set_label: "Acoustic Target (RPM)",
set_label: &fl!(I18N, "acoustic-target"),
set_size_group: &label_size_group,
},
@@ -225,7 +226,7 @@ impl relm4::Component for ThermalsPage {
#[template_child]
label {
set_label: "Minimum Fan Speed (%)",
set_label: &fl!(I18N, "min-fan-speed"),
set_size_group: &label_size_group,
},
@@ -248,7 +249,7 @@ impl relm4::Component for ThermalsPage {
set_visible: model.pmfw_options.zero_rpm_available.get(),
gtk::Label {
set_label: "Zero RPM",
set_label: &fl!(I18N, "zero-rpm"),
set_xalign: 0.0,
set_size_group: &label_size_group,
},
@@ -267,7 +268,7 @@ impl relm4::Component for ThermalsPage {
#[template_child]
label {
set_label: "Zero RPM stop temperature (°C)",
set_label: &fl!(I18N, "zero-rpm-stop-temp"),
set_size_group: &label_size_group,
},
@@ -284,10 +285,10 @@ impl relm4::Component for ThermalsPage {
},
gtk::Button {
set_label: "Reset",
set_label: &fl!(I18N, "reset-button"),
set_halign: gtk::Align::End,
set_margin_vertical: 5,
set_tooltip_text: Some("Warning: this resets the fan firmware settings!"),
set_tooltip_text: Some(&fl!(I18N, "pmfw-reset-warning")),
add_css_class: "destructive-action",
set_size_group: &spin_size_group,
#[watch]
@@ -297,8 +298,8 @@ impl relm4::Component for ThermalsPage {
}
},
},
add_titled[Some(CURVE_PAGE), "Curve"] = model.fan_curve_frame.widget(),
add_titled[Some(STATIC_PAGE), "Static"] = &gtk::Box {
add_titled[Some(CURVE_PAGE), &fl!(I18N, "curve-page")] = model.fan_curve_frame.widget(),
add_titled[Some(STATIC_PAGE), &fl!(I18N, "static-page")] = &gtk::Box {
set_valign: gtk::Align::Start,
set_orientation: gtk::Orientation::Vertical,
set_spacing: 5,
@@ -308,7 +309,7 @@ impl relm4::Component for ThermalsPage {
FanSettingRow {
#[template_child]
label {
set_label: "Static Speed (%)",
set_label: &fl!(I18N, "static-speed"),
},
#[template_child]
+16 -1
View File
@@ -1,11 +1,15 @@
mod app;
mod config;
use std::sync::LazyLock;
use anyhow::Context;
use app::{AppModel, APP_BROKER};
use config::UiConfig;
use lact_schema::args::GuiArgs;
use i18n_embed::fluent::{fluent_language_loader, FluentLanguageLoader};
use lact_schema::{args::GuiArgs, i18n};
use relm4::{RelmApp, SharedState};
use rust_embed::RustEmbed;
use tracing::metadata::LevelFilter;
use tracing_subscriber::EnvFilter;
@@ -15,6 +19,13 @@ const GUI_VERSION: &str = env!("CARGO_PKG_VERSION");
pub const APP_ID: &str = "io.github.ilya_zlobintsev.LACT";
pub const REPO_URL: &str = "https://github.com/ilya-zlobintsev/LACT";
pub(crate) static I18N: LazyLock<FluentLanguageLoader> =
LazyLock::new(|| i18n::loader(fluent_language_loader!(), &Localizations));
#[derive(RustEmbed)]
#[folder = "i18n"]
pub struct Localizations;
pub fn run(args: GuiArgs) -> anyhow::Result<()> {
let env_filter = EnvFilter::builder()
.with_default_directive(LevelFilter::INFO.into())
@@ -22,6 +33,10 @@ pub fn run(args: GuiArgs) -> anyhow::Result<()> {
.context("Invalid log level")?;
tracing_subscriber::fmt().with_env_filter(env_filter).init();
// Pre-init localization
LazyLock::force(&I18N);
LazyLock::force(&lact_schema::i18n::LANGUAGE_LOADER);
if let Some(existing_config) = UiConfig::load() {
*CONFIG.write() = existing_config;
}
+4
View File
@@ -13,9 +13,13 @@ serde_with = { workspace = true }
serde_json = { workspace = true }
anyhow = { workspace = true }
indexmap = { workspace = true }
i18n-embed = { workspace = true }
i18n-embed-fl = { workspace = true }
rust-embed = { workspace = true }
serde-error = "=0.1.3"
clap = { version = "4.4.18", features = ["derive"], optional = true }
[build-dependencies]
vergen = { version = "8.0.0", features = ["git", "gitcl"] }
+4
View File
@@ -0,0 +1,4 @@
fallback_language = "en"
[fluent]
assets_dir = "i18n"
+24
View File
@@ -0,0 +1,24 @@
gpu-model = GPU Model
subvendor = Card Manufacturer
subdevice = Card Model
driver-used = Driver Used
vbios-version = VBIOS Version
vram-size = VRAM Size
vram-type = VRAM Type
gpu-family = GPU Family
asic-name = ASIC Name
compute-units = Compute Units
execution-units = Execution Units
subslices = Subslices
cuda-cores = CUDA Cores
hardware-count = {$name} Count
isa = Instruction Set
l1-cache-per-cu = L1 Cache (Per CU)
l2-cache = L2 Cache
l3-cache = L3 Cache
rebar = Resizable Bar
cpu-vram = CPU Accessible VRAM
pcie-speed = PCIe Link Speed
enabled = Enabled
disabled = Disabled
+22
View File
@@ -0,0 +1,22 @@
use std::sync::LazyLock;
use i18n_embed::{
fluent::{fluent_language_loader, FluentLanguageLoader},
DesktopLanguageRequester, I18nAssets,
};
use rust_embed::RustEmbed;
pub static LANGUAGE_LOADER: LazyLock<FluentLanguageLoader> =
LazyLock::new(|| loader(fluent_language_loader!(), &Localizations));
pub fn loader(loader: FluentLanguageLoader, assets: &dyn I18nAssets) -> FluentLanguageLoader {
let requested_languages = DesktopLanguageRequester::requested_languages();
let _selected_languages = i18n_embed::select(&loader, assets, &requested_languages)
.expect("Failed to select localization");
loader
}
#[derive(RustEmbed)]
#[folder = "i18n"]
pub struct Localizations;
+43 -25
View File
@@ -1,6 +1,7 @@
#[cfg(feature = "args")]
pub mod args;
pub mod config;
pub mod i18n;
mod profiles;
pub mod request;
mod response;
@@ -8,6 +9,7 @@ mod response;
#[cfg(test)]
mod tests;
use i18n_embed_fl::fl;
pub use request::Request;
pub use response::Response;
@@ -29,7 +31,7 @@ use std::{
sync::Arc,
};
use crate::config::ProfileHooks;
use crate::{config::ProfileHooks, i18n::LANGUAGE_LOADER};
pub const GIT_COMMIT: &str = env!("VERGEN_GIT_SHA");
@@ -134,7 +136,7 @@ impl DeviceInfo {
.unwrap_or(1.0)
}
pub fn info_elements(&self, stats: Option<&DeviceStats>) -> Vec<(&str, Option<String>)> {
pub fn info_elements(&self, stats: Option<&DeviceStats>) -> Vec<(String, Option<String>)> {
let pci_info = self.pci_info.as_ref();
let mut gpu_model = self
@@ -186,16 +188,22 @@ impl DeviceInfo {
};
let mut elements = vec![
("GPU Model", Some(gpu_model)),
("Card Manufacturer", Some(card_manufacturer)),
("Card Model", Some(card_model)),
("Driver Used", Some(self.driver.clone())),
("VBIOS Version", self.vbios_version.clone()),
(fl!(LANGUAGE_LOADER, "gpu-model"), Some(gpu_model)),
(fl!(LANGUAGE_LOADER, "subvendor"), Some(card_manufacturer)),
(fl!(LANGUAGE_LOADER, "subdevice"), Some(card_model)),
(
fl!(LANGUAGE_LOADER, "driver-used"),
Some(self.driver.clone()),
),
(
fl!(LANGUAGE_LOADER, "vbios-version"),
self.vbios_version.clone(),
),
];
if let Some(stats) = stats {
elements.push((
"VRAM Size",
fl!(LANGUAGE_LOADER, "vram-size"),
stats
.vram
.total
@@ -222,35 +230,41 @@ impl DeviceInfo {
}
elements.extend([
("GPU Family", drm_info.family_name.clone()),
("ASIC Name", drm_info.asic_name.clone()),
(
"Compute Units",
fl!(LANGUAGE_LOADER, "gpu-family"),
drm_info.family_name.clone(),
),
(
fl!(LANGUAGE_LOADER, "asic-name"),
drm_info.asic_name.clone(),
),
(
fl!(LANGUAGE_LOADER, "compute-units"),
drm_info.compute_units.map(|count| count.to_string()),
),
(
"Execution Units",
fl!(LANGUAGE_LOADER, "execution-units"),
drm_info
.intel
.execution_units
.map(|count| count.to_string()),
),
(
"Subslices",
fl!(LANGUAGE_LOADER, "subslices"),
drm_info.intel.subslices.map(|count| count.to_string()),
),
(
"Cuda Cores",
fl!(LANGUAGE_LOADER, "cuda-cores"),
drm_info.cuda_cores.map(|count| count.to_string()),
),
(
"SM Count",
fl!(LANGUAGE_LOADER, "hardware-count", name = "SM"),
drm_info
.streaming_multiprocessors
.map(|count| count.to_string()),
),
(
"ROP Count",
fl!(LANGUAGE_LOADER, "hardware-count", name = "ROP"),
drm_info.rop_info.as_ref().map(|rop| {
format!(
"{} ({} * {})",
@@ -258,34 +272,38 @@ impl DeviceInfo {
)
}),
),
("Instruction Set", drm_info.isa.clone()),
("VRAM Type", vram_type),
(fl!(LANGUAGE_LOADER, "isa"), drm_info.isa.clone()),
(fl!(LANGUAGE_LOADER, "vram-type"), vram_type),
(
"L1 Cache (Per CU)",
fl!(LANGUAGE_LOADER, "l1-cache-per-cu"),
drm_info
.l1_cache_per_cu
.map(|cache| format!("{} KiB", cache / 1024)),
),
(
"L2 Cache",
fl!(LANGUAGE_LOADER, "l2-cache"),
drm_info
.l2_cache
.map(|cache| format!("{} KiB", cache / 1024)),
),
(
"L3 Cache",
fl!(LANGUAGE_LOADER, "l3-cache"),
drm_info.l3_cache_mb.map(|cache| format!("{cache} MiB")),
),
]);
if let Some(memory_info) = &drm_info.memory_info {
if let Some(rebar) = memory_info.resizeable_bar {
let rebar = if rebar { "Enabled" } else { "Disabled" };
elements.push(("Resizeable bar", Some(rebar.to_owned())));
let rebar = if rebar {
fl!(LANGUAGE_LOADER, "enabled")
} else {
fl!(LANGUAGE_LOADER, "disabled")
};
elements.push((fl!(LANGUAGE_LOADER, "rebar"), Some(rebar.to_owned())));
}
elements.push((
"CPU Accessible VRAM",
fl!(LANGUAGE_LOADER, "cpu-vram"),
Some((memory_info.cpu_accessible_total / 1024 / 1024).to_string()),
));
}
@@ -297,7 +315,7 @@ impl DeviceInfo {
if let (Some(current_link_speed), Some(current_link_width)) =
(&self.link_info.current_speed, &self.link_info.current_width)
{
elements.push(("PCIe Link Speed", Some(format!("{current_link_speed} x{current_link_width} (Max: {max_link_speed} x{max_link_width})"))));
elements.push((fl!(LANGUAGE_LOADER, "pcie-speed"), Some(format!("{current_link_speed} x{current_link_width} (Max: {max_link_speed} x{max_link_width})"))));
}
}