diff --git a/api/rs/slint/private_unstable_api.rs b/api/rs/slint/private_unstable_api.rs
index f3f66db5260..6c1f1f78255 100644
--- a/api/rs/slint/private_unstable_api.rs
+++ b/api/rs/slint/private_unstable_api.rs
@@ -211,6 +211,7 @@ pub mod re_exports {
pub use i_slint_core::string::shared_string_from_number;
pub use i_slint_core::string::shared_string_from_number_fixed;
pub use i_slint_core::string::shared_string_from_number_precision;
+ pub use i_slint_core::string::shared_string_from_number_unlocalized;
pub use i_slint_core::timers::{Timer, TimerMode};
pub use i_slint_core::translations::{
set_bundled_languages, translate_from_bundle, translate_from_bundle_with_plural,
diff --git a/cspell.json b/cspell.json
index 73df0d2ee02..3b61227f891 100644
--- a/cspell.json
+++ b/cspell.json
@@ -1,348 +1,351 @@
{
- "version": "0.2",
- "language": "en",
- "languageSettings": [
- {
- "languageId": "rust",
- "words": [
- "bbox",
- "bindgen",
- "boxshadowcache",
- "builtins",
- "canonicalize", // std::path::Path::canonicalize
- "codemap",
- "concat",
- "Consts",
- "evenodd",
- "FBOs", // plural of FBO (frame buffer object)
- "fontdb",
- "fullscreen",
- "glutin",
- "glutin's",
- "maxx",
- "maxy",
- "minx",
- "miny",
- "miri",
- "peekable",
- "Realloc",
- "refcell",
- "rtti",
- "Smol",
- "smolstr",
- "subspan",
- "uninit",
- "unmap",
- "unsync",
- "vsync"
- ],
- "ignoreRegExpList": [
- "/#\\[cfg\\b.+\\]/",
- "/#\\[repr\\b.+\\]/",
- // Exclude crate paths
- "/(\\b|::)[a-z0-9_]+::/",
- "/::[a-z0-9_]+(\\b|::)/",
- // Qt things:
- "/\\bQt_[a-zA-Z0-9_]+/"
- ]
- },
- {
- "languageId": "restructuredtext",
+ "version": "0.2",
+ "language": "en",
+ "languageSettings": [
+ {
+ "languageId": "rust",
+ "words": [
+ "bbox",
+ "bindgen",
+ "boxshadowcache",
+ "builtins",
+ "canonicalize", // std::path::Path::canonicalize
+ "codemap",
+ "concat",
+ "Consts",
+ "evenodd",
+ "FBOs", // plural of FBO (frame buffer object)
+ "fontdb",
+ "fullscreen",
+ "glutin",
+ "glutin's",
+ "maxx",
+ "maxy",
+ "minx",
+ "miny",
+ "miri",
+ "peekable",
+ "Realloc",
+ "refcell",
+ "rtti",
+ "Smol",
+ "smolstr",
+ "subspan",
+ "uninit",
+ "unlocalized",
+ "Unlocalized",
+ "unmap",
+ "unsync",
+ "vsync"
+ ],
+ "ignoreRegExpList": [
+ "/#\\[cfg\\b.+\\]/",
+ "/#\\[repr\\b.+\\]/",
+ // Exclude crate paths
+ "/(\\b|::)[a-z0-9_]+::/",
+ "/::[a-z0-9_]+(\\b|::)/",
+ // Qt things:
+ "/\\bQt_[a-zA-Z0-9_]+/"
+ ]
+ },
+ {
+ "languageId": "restructuredtext",
"words": [
"genindex"
],
"ignoreRegExpList": [
"/:.+:/"
]
- },
- {
+ },
+ {
"languageId": [
"markdown",
"md",
"mdx"
],
- "words": [
- "DSLINT",
- "Espressif",
- "libname",
- "linebuffer",
- "mylibrary",
- "mywidgets",
- "otherlibrary",
- "Lmylibrary",
- "progressindicator",
- "standardbutton",
- "standardlistview",
- "standardtableview",
- "rustup",
- "setdefault",
- "Sonoma",
- "SUBDIR",
- "Yocto",
- "relpath",
- "classslint",
- "vectormodel",
- "structslint",
- "namespaceslint",
- "compilerconfiguration",
- "componentweakhandle",
- "listmodel",
- "skiarenderer",
- "softwarerenderer",
- "tschüß",
- "TSCHÜSS",
- "ὈΔΥΣΣΕΎΣ",
- "ὀδυσσεύς",
- "commonmark",
- "strikethroughs"
- ]
- }
- ],
- "words": [
- "aarch",
- "AARRGGBB",
- "accesskit",
- "antialiasing",
- "Argb",
- "armv",
- "astrojs",
- "Bezier",
- "Bézier",
- "cbindgen",
- "cdylib",
- "cmake",
- "colspan",
- "combobox",
- "radiobutton",
- "contexte",
- "cupertino",
- "datastructures",
- "dealloc",
- "distros",
- "embedders",
- "espflash",
- "Femto",
- "femtovg",
- "flexboxlayout",
- "flickable",
- "focusable",
- "frameless",
- "fullscreen",
- "gles",
- "Goffart",
- "gradians",
- "grayscale",
- "gridlayout",
- "groupbox",
- "Hausmann",
- "Helvetica",
- "imagefilter",
- "inout",
- "knip",
- "layouting",
- "linebreak",
- "lineedit",
- "LINUXFB",
- "listview",
- "lvalue",
- "mdns",
- "microcontroller",
- "microcontrollers",
- "MSVC",
- "muda",
- "napi",
- "ogoffart",
- "oneshot",
- "Oooops",
- "opengl",
- "opengles",
- "Pandoc",
- "pico",
- "pixmap",
- "prestart",
- "printerdemo",
- "pythonw",
- "replacen",
- "rehype",
- "Rgba",
- "riscv",
- "rowspan",
- "RRGGBB",
- "RRGGBBAA",
- "rustc",
- "rustdoc",
- "rustflags",
- "rvalue",
- "SANDBOXING",
- "scrollview",
- "SDK",
- "seti",
- "sixtyfps",
- "skia",
- "slint",
- "slintdoc",
- "slintdocs",
- "slintpad",
- "spdx",
- "spinbox",
- "Srgb",
- "streetsidesoftware",
- "tableview",
- "tabwidget",
- "testcase",
- "textedit",
- "tmpobj",
- "toolchain",
- "toolkit",
- "Toradex",
- "touchpad",
- "trackpad",
- "trackpads",
- "tronical",
- "typeloader",
- "uefi",
- "uncompiled",
- "Unshiftable",
- "unerase",
- "unmapping",
- "unminimize",
- "untracked",
- "viewbox",
- "virtualizes",
- "Vivante",
- "vtable",
- "vulkan",
- "wasm",
- "webassembly",
- "wgpu",
- "windowrc",
- "winit",
- "xaarrggbb",
- "Xcodegen",
- "Xcodeproj",
- "xtask",
- "xtensa",
- // CSS Color 4 named-color keywords not accepted by the default English dictionary (W3C CSS Color Module Level 4).
- "aliceblue",
- "antiquewhite",
- "blanchedalmond",
- "blueviolet",
- "burlywood",
- "cadetblue",
- "cornflowerblue",
- "cornsilk",
- "darkcyan",
- "darkgoldenrod",
- "darkgray",
- "darkgreen",
- "darkgrey",
- "darkkhaki",
- "darkmagenta",
- "darkolivegreen",
- "darkorange",
- "darkorchid",
- "darkred",
- "darksalmon",
- "darkseagreen",
- "darkslateblue",
- "darkslategray",
- "darkslategrey",
- "darkturquoise",
- "darkviolet",
- "deeppink",
- "deepskyblue",
- "dimgray",
- "dimgrey",
- "dodgerblue",
- "floralwhite",
- "forestgreen",
- "gainsboro",
- "ghostwhite",
- "greenyellow",
- "hotpink",
- "indianred",
- "lavenderblush",
- "lawngreen",
- "lemonchiffon",
- "lightcoral",
- "lightcyan",
- "lightgoldenrodyellow",
- "lightgray",
- "lightgreen",
- "lightpink",
- "lightsalmon",
- "lightseagreen",
- "lightskyblue",
- "lightslategray",
- "lightslategrey",
- "lightsteelblue",
- "lightyellow",
- "limegreen",
- "mediumaquamarine",
- "mediumblue",
- "mediumorchid",
- "mediumpurple",
- "mediumseagreen",
- "mediumslateblue",
- "mediumspringgreen",
- "mediumturquoise",
- "mediumvioletred",
- "midnightblue",
- "mintcream",
- "mistyrose",
- "navajowhite",
- "oldlace",
- "olivedrab",
- "orangered",
- "palegoldenrod",
- "palegreen",
- "paleturquoise",
- "palevioletred",
- "papayawhip",
- "peachpuff",
- "powderblue",
- "rebeccapurple",
- "rosybrown",
- "royalblue",
- "saddlebrown",
- "sandybrown",
- "seagreen",
- "skyblue",
- "slateblue",
- "slategray",
- "slategrey",
- "springgreen",
- "steelblue",
- "whitesmoke",
- "yellowgreen"
- ],
- "ignorePaths": [
- ".github/**",
- "api/cpp/docs/conf.py",
- "CHANGELOG.md",
- "cspell.json",
- "LICENSES",
- "examples/*/lang/**",
- "pnpm-lock.yaml",
- "pnpm-workspace.yaml",
- "REUSE.toml",
- "**/Cargo.lock",
- "docs/common/src/utils/slint.tmLanguage.json",
- "ui-libraries/material/docs/vendor/**",
- ".cspell/slint-project-words.txt",
- ".cspell/slint-project-words-audit.tsv",
- "tools/figma-inspector/tests/figma_output.json",
- "**/*.gltf",
- "**/*.po",
- "**/*.pot",
- "**/*.svg",
- "target/**",
- "**/node_modules/**"
- ],
- "overrides": [
- {
- "filename": "demos/printerdemo/lang/fr/**",
- "language": "en,fr",
+ "words": [
+ "DSLINT",
+ "Espressif",
+ "libname",
+ "linebuffer",
+ "mylibrary",
+ "mywidgets",
+ "otherlibrary",
+ "Lmylibrary",
+ "progressindicator",
+ "standardbutton",
+ "standardlistview",
+ "standardtableview",
+ "rustup",
+ "setdefault",
+ "Sonoma",
+ "SUBDIR",
+ "Yocto",
+ "relpath",
+ "classslint",
+ "vectormodel",
+ "structslint",
+ "namespaceslint",
+ "compilerconfiguration",
+ "componentweakhandle",
+ "listmodel",
+ "skiarenderer",
+ "softwarerenderer",
+ "tschüß",
+ "TSCHÜSS",
+ "ὈΔΥΣΣΕΎΣ",
+ "ὀδυσσεύς",
+ "commonmark",
+ "strikethroughs",
+ "unlocalized"
+ ]
+ }
+ ],
+ "words": [
+ "aarch",
+ "AARRGGBB",
+ "accesskit",
+ "antialiasing",
+ "Argb",
+ "armv",
+ "astrojs",
+ "Bezier",
+ "Bézier",
+ "cbindgen",
+ "cdylib",
+ "cmake",
+ "colspan",
+ "combobox",
+ "radiobutton",
+ "contexte",
+ "cupertino",
+ "datastructures",
+ "dealloc",
+ "distros",
+ "embedders",
+ "espflash",
+ "Femto",
+ "femtovg",
+ "flexboxlayout",
+ "flickable",
+ "focusable",
+ "frameless",
+ "fullscreen",
+ "gles",
+ "Goffart",
+ "gradians",
+ "grayscale",
+ "gridlayout",
+ "groupbox",
+ "Hausmann",
+ "Helvetica",
+ "imagefilter",
+ "inout",
+ "knip",
+ "layouting",
+ "linebreak",
+ "lineedit",
+ "LINUXFB",
+ "listview",
+ "lvalue",
+ "mdns",
+ "microcontroller",
+ "microcontrollers",
+ "MSVC",
+ "muda",
+ "napi",
+ "ogoffart",
+ "oneshot",
+ "Oooops",
+ "opengl",
+ "opengles",
+ "Pandoc",
+ "pico",
+ "pixmap",
+ "prestart",
+ "printerdemo",
+ "pythonw",
+ "replacen",
+ "rehype",
+ "Rgba",
+ "riscv",
+ "rowspan",
+ "RRGGBB",
+ "RRGGBBAA",
+ "rustc",
+ "rustdoc",
+ "rustflags",
+ "rvalue",
+ "SANDBOXING",
+ "scrollview",
+ "SDK",
+ "seti",
+ "sixtyfps",
+ "skia",
+ "slint",
+ "slintdoc",
+ "slintdocs",
+ "slintpad",
+ "spdx",
+ "spinbox",
+ "Srgb",
+ "streetsidesoftware",
+ "tableview",
+ "tabwidget",
+ "testcase",
+ "textedit",
+ "tmpobj",
+ "toolchain",
+ "toolkit",
+ "Toradex",
+ "touchpad",
+ "trackpad",
+ "trackpads",
+ "tronical",
+ "typeloader",
+ "uefi",
+ "uncompiled",
+ "Unshiftable",
+ "unerase",
+ "unmapping",
+ "unminimize",
+ "untracked",
+ "viewbox",
+ "virtualizes",
+ "Vivante",
+ "vtable",
+ "vulkan",
+ "wasm",
+ "webassembly",
+ "wgpu",
+ "windowrc",
+ "winit",
+ "xaarrggbb",
+ "Xcodegen",
+ "Xcodeproj",
+ "xtask",
+ "xtensa",
+ // CSS Color 4 named-color keywords not accepted by the default English dictionary (W3C CSS Color Module Level 4).
+ "aliceblue",
+ "antiquewhite",
+ "blanchedalmond",
+ "blueviolet",
+ "burlywood",
+ "cadetblue",
+ "cornflowerblue",
+ "cornsilk",
+ "darkcyan",
+ "darkgoldenrod",
+ "darkgray",
+ "darkgreen",
+ "darkgrey",
+ "darkkhaki",
+ "darkmagenta",
+ "darkolivegreen",
+ "darkorange",
+ "darkorchid",
+ "darkred",
+ "darksalmon",
+ "darkseagreen",
+ "darkslateblue",
+ "darkslategray",
+ "darkslategrey",
+ "darkturquoise",
+ "darkviolet",
+ "deeppink",
+ "deepskyblue",
+ "dimgray",
+ "dimgrey",
+ "dodgerblue",
+ "floralwhite",
+ "forestgreen",
+ "gainsboro",
+ "ghostwhite",
+ "greenyellow",
+ "hotpink",
+ "indianred",
+ "lavenderblush",
+ "lawngreen",
+ "lemonchiffon",
+ "lightcoral",
+ "lightcyan",
+ "lightgoldenrodyellow",
+ "lightgray",
+ "lightgreen",
+ "lightpink",
+ "lightsalmon",
+ "lightseagreen",
+ "lightskyblue",
+ "lightslategray",
+ "lightslategrey",
+ "lightsteelblue",
+ "lightyellow",
+ "limegreen",
+ "mediumaquamarine",
+ "mediumblue",
+ "mediumorchid",
+ "mediumpurple",
+ "mediumseagreen",
+ "mediumslateblue",
+ "mediumspringgreen",
+ "mediumturquoise",
+ "mediumvioletred",
+ "midnightblue",
+ "mintcream",
+ "mistyrose",
+ "navajowhite",
+ "oldlace",
+ "olivedrab",
+ "orangered",
+ "palegoldenrod",
+ "palegreen",
+ "paleturquoise",
+ "palevioletred",
+ "papayawhip",
+ "peachpuff",
+ "powderblue",
+ "rebeccapurple",
+ "rosybrown",
+ "royalblue",
+ "saddlebrown",
+ "sandybrown",
+ "seagreen",
+ "skyblue",
+ "slateblue",
+ "slategray",
+ "slategrey",
+ "springgreen",
+ "steelblue",
+ "whitesmoke",
+ "yellowgreen"
+ ],
+ "ignorePaths": [
+ ".github/**",
+ "api/cpp/docs/conf.py",
+ "CHANGELOG.md",
+ "cspell.json",
+ "LICENSES",
+ "examples/*/lang/**",
+ "pnpm-lock.yaml",
+ "pnpm-workspace.yaml",
+ "REUSE.toml",
+ "**/Cargo.lock",
+ "docs/common/src/utils/slint.tmLanguage.json",
+ "ui-libraries/material/docs/vendor/**",
+ ".cspell/slint-project-words.txt",
+ ".cspell/slint-project-words-audit.tsv",
+ "tools/figma-inspector/tests/figma_output.json",
+ "**/*.gltf",
+ "**/*.po",
+ "**/*.pot",
+ "**/*.svg",
+ "target/**",
+ "**/node_modules/**"
+ ],
+ "overrides": [
+ {
+ "filename": "demos/printerdemo/lang/fr/**",
+ "language": "en,fr",
"dictionaries": [
"slint-project-words",
"fr-fr-printerdemo"
@@ -355,23 +358,23 @@
"nplurals",
"YCMB"
]
- },
- {
- "filename": "**/*.rst",
- "languageId": "restructuredtext"
- },
- {
- "filename": "**/*.slint",
- "languageId": "rust"
- }
- ],
- "dictionaryDefinitions": [
- {
- "name": "slint-project-words",
- "path": "./.cspell/slint-project-words.txt",
- "addWords": true
- }
- ],
+ },
+ {
+ "filename": "**/*.rst",
+ "languageId": "restructuredtext"
+ },
+ {
+ "filename": "**/*.slint",
+ "languageId": "rust"
+ }
+ ],
+ "dictionaryDefinitions": [
+ {
+ "name": "slint-project-words",
+ "path": "./.cspell/slint-project-words.txt",
+ "addWords": true
+ }
+ ],
"dictionaries": [
"slint-project-words"
]
diff --git a/docs/astro/src/content/docs/reference/primitive-types.mdx b/docs/astro/src/content/docs/reference/primitive-types.mdx
index dccd007f674..40fc01c8bd2 100644
--- a/docs/astro/src/content/docs/reference/primitive-types.mdx
+++ b/docs/astro/src/content/docs/reference/primitive-types.mdx
@@ -186,6 +186,10 @@ export component FloatToString {
In addition, call the in postfix style on any numeric value,
for example `(-10).abs()` or `x.round()`.
+#### to-string-unlocalized() -> string
+
+Returns a string representation of the number but not localized. This means the decimal separator will always be a dot.
+
### int
Signed integral number.
@@ -478,6 +482,7 @@ export component Example {
`int` and `float` convert implicitly to `string`.
To control how many digits appear in the result, use the [`to-fixed()` and `to-precision()`](#to-fixeddigits-int---string)
member functions instead of the implicit conversion.
+To always use a dot as decimal separator regardless of the locale, use [`to-string-unlocalized()`](#to-string-unlocalized---string).
For the other direction, convert a `string` to a number with the [`to-float()`](#to-float---float) member function,
and check whether a string holds a valid number with `is-float()`.
diff --git a/internal/compiler/builtins.slint b/internal/compiler/builtins.slint
index b8d14349837..6954d5aa8fd 100644
--- a/internal/compiler/builtins.slint
+++ b/internal/compiler/builtins.slint
@@ -2375,6 +2375,9 @@ export component Path {
//!
//! A string providing the commands according to the SVG path specification.
//! This property can only be set in a binding and cannot be accessed in an expression.
+ //! **Important:** if you concatenate strings with float values, use `.to-string-unlocalized()`
+ //! instead of implicit string conversion otherwise the path generation will not work on systems
+ //! with different localization.
//!
//!
//! ## Path Using SVG Path Elements
diff --git a/internal/compiler/expression_tree.rs b/internal/compiler/expression_tree.rs
index eb547f90796..a698f0ab98a 100644
--- a/internal/compiler/expression_tree.rs
+++ b/internal/compiler/expression_tree.rs
@@ -46,6 +46,7 @@ pub enum BuiltinFunction {
Exp,
ToFixed,
ToPrecision,
+ ToStringUnlocalized,
SetFocusItem,
ClearFocusItem,
ShowPopupWindow,
@@ -213,6 +214,7 @@ declare_builtin_function_types!(
Exp: (Type::Float32) -> Type::Float32,
ToFixed: (Type::Float32, Type::Int32) -> Type::String,
ToPrecision: (Type::Float32, Type::Int32) -> Type::String,
+ ToStringUnlocalized: (Type::Float32) -> Type::String,
SetFocusItem: (Type::ElementReference) -> Type::Void,
ClearFocusItem: (Type::ElementReference) -> Type::Void,
ShowPopupWindow: (Type::ElementReference) -> Type::Void,
@@ -368,7 +370,8 @@ impl BuiltinFunction {
| BuiltinFunction::Pow
| BuiltinFunction::Exp
| BuiltinFunction::ATan
- | BuiltinFunction::ATan2 => true,
+ | BuiltinFunction::ATan2
+ | BuiltinFunction::ToStringUnlocalized => true,
// The result depends on the locale's decimal separator, like DecimalSeparator.
// The constant propagation folds the locale-independent cases and promotes
// their binding back to constant.
@@ -468,7 +471,8 @@ impl BuiltinFunction {
| BuiltinFunction::ATan
| BuiltinFunction::ATan2
| BuiltinFunction::ToFixed
- | BuiltinFunction::ToPrecision => true,
+ | BuiltinFunction::ToPrecision
+ | BuiltinFunction::ToStringUnlocalized => true,
BuiltinFunction::SetFocusItem | BuiltinFunction::ClearFocusItem => false,
BuiltinFunction::ShowPopupWindow
| BuiltinFunction::ClosePopupWindow
diff --git a/internal/compiler/generator/cpp.rs b/internal/compiler/generator/cpp.rs
index d93faebca19..b363ccd25f7 100644
--- a/internal/compiler/generator/cpp.rs
+++ b/internal/compiler/generator/cpp.rs
@@ -4648,6 +4648,11 @@ fn compile_builtin_function_call(
a.next().unwrap(), a.next().unwrap(),
)
}
+ BuiltinFunction::ToStringUnlocalized => {
+ format!("[](double n) {{ slint::SharedString out; slint::cbindgen_private::slint_shared_string_from_number_unlocalized(&out, n); return out; }}({})",
+ a.next().unwrap(),
+ )
+ }
BuiltinFunction::SetFocusItem => {
if let [llr::Expression::PropertyReference(pr)] = arguments {
let window = access_window_field(ctx);
diff --git a/internal/compiler/generator/rust.rs b/internal/compiler/generator/rust.rs
index c7127c70f84..360829b8159 100644
--- a/internal/compiler/generator/rust.rs
+++ b/internal/compiler/generator/rust.rs
@@ -4056,6 +4056,10 @@ fn compile_builtin_function_call(
let (a1, a2) = (a.next().unwrap(), a.next().unwrap());
quote!(sp::shared_string_from_number_precision(#a1 as f64, (#a2 as i32).max(0) as usize))
}
+ BuiltinFunction::ToStringUnlocalized => {
+ let a1 = a.next().unwrap();
+ quote!(sp::shared_string_from_number_unlocalized(#a1 as f64))
+ }
BuiltinFunction::StringToFloat => {
quote!(sp::string_to_float(#(#a)*.as_str()).unwrap_or_default())
}
diff --git a/internal/compiler/llr/optim_passes/inline_expressions.rs b/internal/compiler/llr/optim_passes/inline_expressions.rs
index 2a018914fa4..f733831997d 100644
--- a/internal/compiler/llr/optim_passes/inline_expressions.rs
+++ b/internal/compiler/llr/optim_passes/inline_expressions.rs
@@ -115,6 +115,7 @@ fn builtin_function_cost(function: &BuiltinFunction) -> isize {
BuiltinFunction::Exp => 10,
BuiltinFunction::ToFixed => ALLOC_COST,
BuiltinFunction::ToPrecision => ALLOC_COST,
+ BuiltinFunction::ToStringUnlocalized => ALLOC_COST,
BuiltinFunction::SetFocusItem | BuiltinFunction::ClearFocusItem => isize::MAX,
BuiltinFunction::ShowPopupWindow
| BuiltinFunction::ClosePopupWindow
diff --git a/internal/compiler/lookup.rs b/internal/compiler/lookup.rs
index 573eb1e9a47..3fd5300ef7b 100644
--- a/internal/compiler/lookup.rs
+++ b/internal/compiler/lookup.rs
@@ -1163,6 +1163,9 @@ impl LookupObject for NumberExpression<'_> {
.or_else(|| f2("sign", member_macro(BuiltinMacroFunction::Sign)))
.or_else(|| f2("to-fixed", member_function(BuiltinFunction::ToFixed)))
.or_else(|| f2("to-precision", member_function(BuiltinFunction::ToPrecision)))
+ .or_else(|| {
+ f2("to-string-unlocalized", member_function(BuiltinFunction::ToStringUnlocalized))
+ })
.or_else(|| NumberWithUnitExpression(self.0).for_each_entry(ctx, f))
}
}
diff --git a/internal/core/string.rs b/internal/core/string.rs
index 32bd85122a6..01f7aaad234 100644
--- a/internal/core/string.rs
+++ b/internal/core/string.rs
@@ -341,10 +341,16 @@ where
}
}
+/// Convert a f64 to a SharedString but unlocalized with "." as decimal separator
+#[inline]
+pub fn shared_string_from_number_unlocalized(n: f64) -> SharedString {
+ crate::format!("{}", i_slint_common::FormattedNumber(n))
+}
+
/// Convert a f64 to a SharedString
pub fn shared_string_from_number(n: f64) -> SharedString {
crate::context::GLOBAL_CONTEXT.with(|ctx| {
- let mut result = crate::format!("{}", i_slint_common::FormattedNumber(n));
+ let mut result = shared_string_from_number_unlocalized(n);
if let Some(ctx) = ctx.get() {
let pinned = ctx.0.as_ref().project_ref();
@@ -542,6 +548,17 @@ pub(crate) mod ffi {
}
}
+ /// Create a string from a number but unlocalized.
+ /// The resulting structure must be passed to slint_shared_string_drop
+ #[unsafe(no_mangle)]
+ pub unsafe extern "C" fn slint_shared_string_from_number_unlocalized(
+ out: *mut SharedString,
+ n: f64,
+ ) {
+ let str = shared_string_from_number_unlocalized(n);
+ unsafe { core::ptr::write(out, str) };
+ }
+
/// Create a string from a number.
/// The resulting structure must be passed to slint_shared_string_drop
#[unsafe(no_mangle)]
diff --git a/internal/interpreter/eval.rs b/internal/interpreter/eval.rs
index 7f1356a1e67..1fc0c3750e4 100644
--- a/internal/interpreter/eval.rs
+++ b/internal/interpreter/eval.rs
@@ -861,6 +861,10 @@ fn call_builtin_function(
let precision: usize = precision.max(0) as usize;
Value::String(i_slint_core::string::shared_string_from_number_precision(n, precision))
}
+ BuiltinFunction::ToStringUnlocalized => {
+ let n: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
+ Value::String(i_slint_core::string::shared_string_from_number_unlocalized(n))
+ }
BuiltinFunction::SetFocusItem => {
if arguments.len() != 1 {
panic!("internal error: incorrect argument count to SetFocusItem")
diff --git a/tests/cases/translations/decimal_separator.slint b/tests/cases/translations/decimal_separator.slint
index f966574ac84..73ff91e2184 100644
--- a/tests/cases/translations/decimal_separator.slint
+++ b/tests/cases/translations/decimal_separator.slint
@@ -15,6 +15,8 @@ export component TestCase {
in-out property string_as_float: value_string.to-float();
in-out property is_float: value_string.is-float();
+ in-out property float_as_string_unlocalized: value.to-string-unlocalized();
+
// \{ cannot be in a slint macro, so we add it here, because we wanna execute the Test with
// compiled source (otherwise bundling translations is not possible)
out property same_or_diff: "\{the-decimal-separator}";
@@ -41,6 +43,7 @@ assert!(slint::select_bundled_translation("fr").is_ok());
assert_eq!(instance.get_the_decimal_separator(), ",");
instance.set_value(-5.5);
assert_eq!(instance.get_float_as_string(), "-5,5");
+assert_eq!(instance.get_float_as_string_unlocalized(), "-5.5");
assert_eq!(instance.get_const_float_as_string(), "1,5");
assert_eq!(instance.get_const_int_as_string(), "42");
assert_eq!(instance.get_const_to_fixed(), "1,50");
@@ -57,6 +60,7 @@ assert!(slint::select_bundled_translation("en").is_ok());
assert_eq!(instance.get_the_decimal_separator(), ".");
instance.set_value(7.123);
assert_eq!(instance.get_float_as_string(), "7.123");
+assert_eq!(instance.get_float_as_string_unlocalized(), "7.123");
assert_eq!(instance.get_const_float_as_string(), "1.5");
assert_eq!(instance.get_const_to_fixed(), "1.50");
assert_eq!(instance.get_const_is_float(), false);
@@ -85,6 +89,7 @@ assert(slint::select_bundled_translation("fr"));
assert_eq(instance.get_the_decimal_separator(), ",");
instance.set_value(-5.5);
assert_eq(instance.get_float_as_string(), "-5,5");
+assert_eq(instance.get_float_as_string_unlocalized(), "-5.5");
assert_eq(instance.get_const_float_as_string(), "1,5");
assert_eq(instance.get_const_int_as_string(), "42");
assert_eq(instance.get_const_to_fixed(), "1,50");
@@ -101,6 +106,7 @@ assert(slint::select_bundled_translation("en"));
assert_eq(instance.get_the_decimal_separator(), ".");
instance.set_value(7.123);
assert_eq(instance.get_float_as_string(), "7.123");
+assert_eq(instance.get_float_as_string_unlocalized(), "7.123");
assert_eq(instance.get_const_float_as_string(), "1.5");
assert_eq(instance.get_const_to_fixed(), "1.50");
assert_eq(instance.get_const_is_float(), false);
@@ -113,4 +119,4 @@ instance.set_value_string("3.2");
assert_eq(instance.get_is_float(), true);
assert_eq(instance.get_string_as_float(), 3.2);
```
-*/
\ No newline at end of file
+*/
diff --git a/tools/lsp/preview/eval.rs b/tools/lsp/preview/eval.rs
index 564d8e60498..62f5d34ca92 100644
--- a/tools/lsp/preview/eval.rs
+++ b/tools/lsp/preview/eval.rs
@@ -437,6 +437,11 @@ fn handle_builtin_function(
let precision: usize = precision.max(0) as usize;
Value::String(i_slint_core::string::shared_string_from_number_precision(n, precision))
}
+ BuiltinFunction::ToStringUnlocalized => {
+ let n: f64 =
+ eval_expression(&arguments[0], local_context, None).try_into().unwrap_or_default();
+ Value::String(i_slint_core::string::shared_string_from_number_unlocalized(n))
+ }
BuiltinFunction::StringIsFloat => {
if arguments.len() != 1 {
return Value::Void;