mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-04 18:58:36 +00:00
Add a function to mark translated strings as dirty
This commit is contained in:
parent
2c012aa2d9
commit
808b1ef946
13 changed files with 112 additions and 14 deletions
|
@ -933,7 +933,7 @@ macro_rules! declare_features {
|
|||
};
|
||||
}
|
||||
|
||||
declare_features! {interpreter backend_qt freestanding renderer_software renderer_skia experimental}
|
||||
declare_features! {interpreter backend_qt freestanding renderer_software renderer_skia experimental gettext}
|
||||
|
||||
/// Generate the headers.
|
||||
/// `root_dir` is the root directory of the slint git repo
|
||||
|
|
|
@ -1258,6 +1258,27 @@ inline SharedString translate(const SharedString &original, const SharedString &
|
|||
|
||||
} // namespace private_api
|
||||
|
||||
#ifdef SLINT_FEATURE_GETTEXT
|
||||
/// Forces all the strings that are translated with `@tr(...)` to be re-evaluated.
|
||||
/// This is useful if the language is changed at runtime.
|
||||
/// The function is only available when Slint is compiled with `SLINT_FEATURE_GETTEXT`.
|
||||
///
|
||||
/// Example
|
||||
/// ```cpp
|
||||
/// my_ui->global<LanguageSettings>().on_french_selected([] {
|
||||
/// // trick from https://www.gnu.org/software/gettext/manual/html_node/gettext-grok.html
|
||||
/// setenv("LANGUAGE", langs[l], true);
|
||||
/// extern int _nl_msg_cat_cntr;
|
||||
/// ++_nl_msg_cat_cntr;
|
||||
/// slint::update_all_translations();
|
||||
/// });
|
||||
/// ```
|
||||
inline void update_all_translations()
|
||||
{
|
||||
cbindgen_private::slint_translations_mark_dirty();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(DOXYGEN)
|
||||
cbindgen_private::Flickable::Flickable()
|
||||
{
|
||||
|
|
|
@ -11,3 +11,9 @@ endif()
|
|||
add_executable(printerdemo main.cpp)
|
||||
target_link_libraries(printerdemo PRIVATE Slint::Slint)
|
||||
slint_target_sources(printerdemo ../ui/printerdemo.slint)
|
||||
|
||||
find_package(Intl)
|
||||
if(Intl_FOUND)
|
||||
target_compile_definitions(printerdemo PRIVATE HAVE_GETTEXT SRC_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
target_link_libraries(printerdemo PRIVATE Intl::Intl)
|
||||
endif()
|
|
@ -2,8 +2,14 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "printerdemo.h"
|
||||
#include "slint.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#ifdef HAVE_GETTEXT
|
||||
# include <locale>
|
||||
# include <libintl.h>
|
||||
#endif
|
||||
|
||||
struct InkLevelModel : slint::Model<InkLevel>
|
||||
{
|
||||
|
@ -23,6 +29,11 @@ struct InkLevelModel : slint::Model<InkLevel>
|
|||
|
||||
int main()
|
||||
{
|
||||
#ifdef HAVE_GETTEXT
|
||||
bindtextdomain("printerdemo", SRC_DIR "/../lang");
|
||||
std::locale::global(std::locale(""));
|
||||
#endif
|
||||
|
||||
auto printer_demo = MainWindow::create();
|
||||
printer_demo->set_ink_levels(std::make_shared<InkLevelModel>());
|
||||
printer_demo->on_quit([] { std::exit(0); });
|
||||
|
@ -65,5 +76,16 @@ int main()
|
|||
}
|
||||
});
|
||||
|
||||
#if defined(HAVE_GETTEXT) && defined(SLINT_FEATURE_GETTEXT)
|
||||
printer_demo->global<PrinterSettings>().on_change_language([](int l) {
|
||||
static const char *langs[] = { "en", "fr" };
|
||||
setenv("LANGUAGE", langs[l], true);
|
||||
// trick from https://www.gnu.org/software/gettext/manual/html_node/gettext-grok.html
|
||||
extern int _nl_msg_cat_cntr;
|
||||
++_nl_msg_cat_cntr;
|
||||
slint::update_all_translations();
|
||||
});
|
||||
#endif
|
||||
|
||||
printer_demo->run();
|
||||
}
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
# Copyright © SixtyFPS GmbH <info@slint.dev>
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# Olivier Goffart <ogoffart@bepointbe.be>, 2023.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-04-17 03:03+0000\n"
|
||||
"PO-Revision-Date: 2023-06-09 07:56+0200\n"
|
||||
"Last-Translator: Olivier Goffart <ogoffart@bepointbe.be>\n"
|
||||
"Language-Team: German <kde-i18n-de@kde.org>\n"
|
||||
"Language: de_DE\n"
|
||||
"Language: fr_FR\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
|
|
@ -23,6 +23,11 @@ name = "printerdemo_lib"
|
|||
slint = { path = "../../../api/rs/slint", features = ["backend-android-activity-05"] }
|
||||
chrono = { version = "0.4", default-features = false, features = ["clock", "std"]}
|
||||
|
||||
# Disable gettext on macOS due to https://github.com/Koka/gettext-rs/issues/114
|
||||
[target.'cfg(not(any(target_os = "macos", target_os = "android")))'.dependencies]
|
||||
slint = { path = "../../../api/rs/slint", features=["gettext"] }
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
slint-build = { path = "../../../api/rs/build" }
|
||||
|
||||
|
|
|
@ -102,6 +102,21 @@ pub fn main() {
|
|||
},
|
||||
);
|
||||
|
||||
// Disable gettext on macOS due to https://github.com/Koka/gettext-rs/issues/114
|
||||
#[cfg(not(any(target_os = "macos", target_os = "android")))]
|
||||
{
|
||||
slint::init_translations!(concat!(env!("CARGO_MANIFEST_DIR"), "/../lang/"));
|
||||
main_window.global::<PrinterSettings>().on_change_language(|l| {
|
||||
let lang = match l {
|
||||
0 => "en",
|
||||
1 => "fr",
|
||||
_ => return,
|
||||
};
|
||||
std::env::set_var("LANGUAGE", lang);
|
||||
slint::init_translations!(concat!(env!("CARGO_MANIFEST_DIR"), "/../lang/"));
|
||||
})
|
||||
}
|
||||
|
||||
main_window.run().unwrap();
|
||||
}
|
||||
|
||||
|
|
|
@ -170,6 +170,7 @@ export component SpinBox inherits Rectangle {
|
|||
export component ComboBox inherits Rectangle {
|
||||
in-out property <string> value;
|
||||
in property <[string]> choices;
|
||||
callback selected(int);
|
||||
|
||||
border-radius: 3px;
|
||||
border-width: 2px;
|
||||
|
@ -235,6 +236,7 @@ export component ComboBox inherits Rectangle {
|
|||
item-area := TouchArea {
|
||||
clicked => {
|
||||
root.value = value;
|
||||
root.selected(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
import { DemoPalette, Page } from "common.slint";
|
||||
import { HomePage } from "./home_page.slint";
|
||||
import { InkLevel, InkPage } from "./ink_page.slint";
|
||||
import { SettingsPage } from "./settings_page.slint";
|
||||
import { SettingsPage, PrinterSettings } from "./settings_page.slint";
|
||||
import { PrinterQueue } from "./printer_queue.slint";
|
||||
|
||||
// re-export for the native code
|
||||
export { PrinterQueue }
|
||||
export { PrinterQueue, PrinterSettings }
|
||||
|
||||
import "./fonts/NotoSans-Regular.ttf";
|
||||
import "./fonts/NotoSans-Bold.ttf";
|
||||
|
|
|
@ -3,6 +3,10 @@
|
|||
|
||||
import { DemoPalette, Page, SpinBox, ComboBox, CheckBox, Label } from "common.slint";
|
||||
|
||||
export global PrinterSettings {
|
||||
callback change-language(int);
|
||||
}
|
||||
|
||||
export component SettingsPage inherits Page {
|
||||
header: @tr("Settings");
|
||||
|
||||
|
@ -65,10 +69,11 @@ export component SettingsPage inherits Page {
|
|||
|
||||
Rectangle {}
|
||||
|
||||
Label { text: @tr("Paper Type"); }
|
||||
Label { text: @tr("Language"); }
|
||||
ComboBox {
|
||||
value: @tr("Standard");
|
||||
choices: [@tr("Standard"), @tr("Non-standard")];
|
||||
value: @tr("English");
|
||||
choices: [@tr("English"), @tr("French")];
|
||||
selected(x) => { PrinterSettings.change-language(x); }
|
||||
}
|
||||
}
|
||||
Row {
|
||||
|
|
|
@ -5,6 +5,7 @@ use crate::api::PlatformError;
|
|||
use crate::platform::{EventLoopProxy, Platform};
|
||||
#[cfg(all(not(feature = "std"), feature = "unsafe-single-threaded"))]
|
||||
use crate::thread_local;
|
||||
use crate::Property;
|
||||
#[cfg(not(feature = "std"))]
|
||||
use alloc::boxed::Box;
|
||||
use alloc::rc::Rc;
|
||||
|
@ -17,6 +18,9 @@ thread_local! {
|
|||
pub(crate) struct SlintContextInner {
|
||||
pub(crate) platform: Box<dyn Platform>,
|
||||
pub(crate) window_count: core::cell::RefCell<isize>,
|
||||
/// This property is read by all translations, and marked dirty when the language change
|
||||
/// so that every translated string gets re-translated
|
||||
pub(crate) translations_dirty: core::pin::Pin<Box<Property<()>>>,
|
||||
}
|
||||
|
||||
/// This context is meant to hold the state and the backend.
|
||||
|
@ -28,7 +32,11 @@ pub struct SlintContext(pub(crate) Rc<SlintContextInner>);
|
|||
impl SlintContext {
|
||||
/// Create a new context with a given platform
|
||||
pub fn new(platform: Box<dyn Platform + 'static>) -> Self {
|
||||
Self(Rc::new(SlintContextInner { platform, window_count: 0.into() }))
|
||||
Self(Rc::new(SlintContextInner {
|
||||
platform,
|
||||
window_count: 0.into(),
|
||||
translations_dirty: Box::pin(Property::new_named((), "SlintContext::translations")),
|
||||
}))
|
||||
}
|
||||
|
||||
/// Return an event proxy
|
||||
|
|
|
@ -192,6 +192,10 @@ pub fn translate(
|
|||
|
||||
#[cfg(all(target_family = "unix", feature = "gettext-rs"))]
|
||||
fn translate_gettext(string: &str, ctx: &str, domain: &str, n: i32, plural: &str) -> String {
|
||||
crate::context::GLOBAL_CONTEXT.with(|ctx| {
|
||||
let Some(ctx) = ctx.get() else { return };
|
||||
ctx.0.translations_dirty.as_ref().get();
|
||||
});
|
||||
fn mangle_context(ctx: &str, s: &str) -> String {
|
||||
format!("{}\u{4}{}", ctx, s)
|
||||
}
|
||||
|
@ -220,6 +224,13 @@ fn translate_gettext(string: &str, ctx: &str, domain: &str, n: i32, plural: &str
|
|||
}
|
||||
}
|
||||
|
||||
pub fn mark_all_translations_dirty() {
|
||||
crate::context::GLOBAL_CONTEXT.with(|ctx| {
|
||||
let Some(ctx) = ctx.get() else { return };
|
||||
ctx.0.translations_dirty.mark_dirty();
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "gettext-rs")]
|
||||
/// Initialize the translation by calling the [`bindtextdomain`](https://man7.org/linux/man-pages/man3/bindtextdomain.3.html) function from gettext
|
||||
pub fn gettext_bindtextdomain(_domain: &str, _dirname: std::path::PathBuf) -> std::io::Result<()> {
|
||||
|
@ -230,6 +241,7 @@ pub fn gettext_bindtextdomain(_domain: &str, _dirname: std::path::PathBuf) -> st
|
|||
START.call_once(|| {
|
||||
gettextrs::setlocale(gettextrs::LocaleCategory::LcAll, "");
|
||||
});
|
||||
mark_all_translations_dirty();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -240,10 +252,8 @@ mod ffi {
|
|||
use super::*;
|
||||
use crate::slice::Slice;
|
||||
|
||||
/// Perform the translation and formatting.
|
||||
#[no_mangle]
|
||||
/// Returns a nul-terminated pointer for this string.
|
||||
/// The returned value is owned by the string, and should not be used after any
|
||||
/// mutable function have been called on the string, and must not be freed.
|
||||
pub extern "C" fn slint_translate(
|
||||
to_translate: &mut SharedString,
|
||||
context: &SharedString,
|
||||
|
@ -255,4 +265,10 @@ mod ffi {
|
|||
*to_translate =
|
||||
translate(to_translate.as_str(), &context, &domain, arguments.as_slice(), n, &plural)
|
||||
}
|
||||
|
||||
/// Mark all translated string as dirty to perform re-translation in case the language change
|
||||
#[no_mangle]
|
||||
pub extern "C" fn slint_translations_mark_dirty() {
|
||||
mark_all_translations_dirty();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,6 +90,7 @@ pub fn generate(show_warnings: bool) -> Result<(), Box<dyn std::error::Error>> {
|
|||
renderer_software: true,
|
||||
renderer_skia: true,
|
||||
experimental: false,
|
||||
gettext: true,
|
||||
};
|
||||
cbindgen::gen_all(&root, &generated_headers_dir, enabled_features)?;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue