feat(locale): add Fluent translations
Default to the platform language with persisted overrides across native and web builds. Refs #630
This commit is contained in:
parent
e102887000
commit
b90b3e3ecf
26 changed files with 1581 additions and 112 deletions
|
|
@ -2318,7 +2318,7 @@ fn start_page_content<'a>(
|
|||
let headline = text("Open CAD Studio").size(40).style(start_primary_style);
|
||||
|
||||
// Plain outlined button (Open / New / Help / Contribute).
|
||||
let outline_btn = |label: &'static str, msg: Message| {
|
||||
let outline_btn = |label: String, msg: Message| {
|
||||
button(text(label).size(14))
|
||||
.on_press(msg)
|
||||
.padding([10, 22])
|
||||
|
|
@ -2346,7 +2346,7 @@ fn start_page_content<'a>(
|
|||
button(
|
||||
row![
|
||||
crate::ui::icons::themed_danger_text(crate::ui::icons::HEART, 14.0),
|
||||
text("Donate").size(14),
|
||||
text(crate::tr!("start-donate")).size(14),
|
||||
]
|
||||
.spacing(5)
|
||||
.align_y(iced::Center),
|
||||
|
|
@ -2360,8 +2360,8 @@ fn start_page_content<'a>(
|
|||
};
|
||||
|
||||
let primary_row = WrapFlow::new(vec![
|
||||
outline_btn("New Drawing", Message::TabNew).into(),
|
||||
outline_btn("Open File…", Message::OpenFile).into(),
|
||||
outline_btn(crate::tr!("start-new-drawing"), Message::TabNew).into(),
|
||||
outline_btn(crate::tr!("start-open-file"), Message::OpenFile).into(),
|
||||
donate_btn.into(),
|
||||
])
|
||||
.spacing_x(12.0)
|
||||
|
|
@ -2371,16 +2371,16 @@ fn start_page_content<'a>(
|
|||
#[cfg_attr(target_arch = "wasm32", allow(unused_mut))]
|
||||
let mut secondary_items: Vec<Element<'a, Message>> = vec![
|
||||
outline_btn(
|
||||
"Send Feedback",
|
||||
crate::tr!("start-send-feedback"),
|
||||
Message::RibbonToolClick {
|
||||
tool_id: "REPORT".to_string(),
|
||||
event: crate::modules::ModuleEvent::Command("REPORT".to_string()),
|
||||
},
|
||||
)
|
||||
.into(),
|
||||
outline_btn("Options", Message::OptionsOpen).into(),
|
||||
outline_btn(crate::tr!("action-options"), Message::OptionsOpen).into(),
|
||||
];
|
||||
secondary_items.push(outline_btn("Plugins", Message::PluginManagerOpen).into());
|
||||
secondary_items.push(outline_btn(crate::tr!("action-plugins"), Message::PluginManagerOpen).into());
|
||||
// The web build is already in the browser, so only the desktop offers a
|
||||
// link to the web version.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
|
|
@ -2403,7 +2403,7 @@ fn start_page_content<'a>(
|
|||
.report_natural_width(action_width_out.clone());
|
||||
|
||||
let sponsors = column![
|
||||
text("Sponsors").size(15),
|
||||
text(crate::tr!("start-sponsors")).size(15),
|
||||
mouse_area(
|
||||
container(
|
||||
iced::widget::svg(iced::widget::svg::Handle::from_memory(include_bytes!(
|
||||
|
|
@ -2505,7 +2505,7 @@ fn start_page_content<'a>(
|
|||
// whole thumbnail remains visible when that width changes.
|
||||
let thumb_h =
|
||||
(panel_w - VIDEO_PANEL_PADDING * 2.0 - VIDEO_SCROLL_GUTTER) * 9.0 / 16.0;
|
||||
let mut list = column![text("Tutorials").size(15)]
|
||||
let mut list = column![text(crate::tr!("start-tutorials")).size(15)]
|
||||
.spacing(10)
|
||||
.width(Fill)
|
||||
// Keep the scrollbar off the thumbnails.
|
||||
|
|
@ -2545,14 +2545,14 @@ fn start_page_content<'a>(
|
|||
}
|
||||
if videos.is_empty() {
|
||||
let note = if videos_loading {
|
||||
"Loading videos…"
|
||||
crate::tr!("start-loading-videos")
|
||||
} else {
|
||||
"Videos load from the internet."
|
||||
crate::tr!("start-videos-online")
|
||||
};
|
||||
list = list.push(text(note).size(12).style(start_muted_style));
|
||||
}
|
||||
let playlist_btn = mouse_area(
|
||||
container(text("Open playlist on YouTube").size(12))
|
||||
container(text(crate::tr!("start-open-playlist")).size(12))
|
||||
.padding([6, 10])
|
||||
.width(Fill)
|
||||
.center_x(Fill)
|
||||
|
|
@ -2605,7 +2605,7 @@ fn start_page_content<'a>(
|
|||
// web builds read the CI-generated snapshot. Both sources mark pinned
|
||||
// discussions and sort them before the rest of the list.
|
||||
let discussions_panel: Element<'a, Message> = {
|
||||
let mut list = column![text("Discussions").size(15)]
|
||||
let mut list = column![text(crate::tr!("start-discussions")).size(15)]
|
||||
.spacing(8)
|
||||
.width(Fill);
|
||||
for discussion in discussions {
|
||||
|
|
@ -2618,7 +2618,7 @@ fn start_page_content<'a>(
|
|||
.align_y(iced::Center);
|
||||
if discussion.pinned {
|
||||
meta = meta.push(
|
||||
text("Pinned")
|
||||
text(crate::tr!("start-pinned"))
|
||||
.size(10)
|
||||
.style(start_primary_style),
|
||||
);
|
||||
|
|
@ -2661,14 +2661,14 @@ fn start_page_content<'a>(
|
|||
}
|
||||
if discussions.is_empty() {
|
||||
let note = if discussions_loading {
|
||||
"Loading discussions…"
|
||||
crate::tr!("start-loading-discussions")
|
||||
} else {
|
||||
"Discussions load from GitHub."
|
||||
crate::tr!("start-discussions-online")
|
||||
};
|
||||
list = list.push(text(note).size(12).style(start_muted_style));
|
||||
}
|
||||
let open_btn = mouse_area(
|
||||
container(text("Open Discussions on GitHub").size(12))
|
||||
container(text(crate::tr!("start-open-discussions")).size(12))
|
||||
.padding([6, 10])
|
||||
.width(Fill)
|
||||
.center_x(Fill)
|
||||
|
|
@ -2725,7 +2725,7 @@ fn start_page_content<'a>(
|
|||
// shows, so the rail always invites support.
|
||||
let supporters: Element<'a, Message> = {
|
||||
let mut list = column![
|
||||
text("Supporters").size(15),
|
||||
text(crate::tr!("start-supporters")).size(15),
|
||||
Space::new().height(iced::Length::Fixed(12.0)),
|
||||
]
|
||||
.spacing(6)
|
||||
|
|
@ -2746,7 +2746,7 @@ fn start_page_content<'a>(
|
|||
container(
|
||||
iced::widget::row![
|
||||
crate::ui::icons::themed_danger_text(crate::ui::icons::HEART, 13.0),
|
||||
text("Support on Patreon").size(12),
|
||||
text(crate::tr!("start-support-on-patreon")).size(12),
|
||||
]
|
||||
.spacing(6)
|
||||
.align_y(iced::Center),
|
||||
|
|
@ -2831,7 +2831,7 @@ fn start_page_content<'a>(
|
|||
.height(Fill)
|
||||
.into(),
|
||||
StartLayout::Compact => {
|
||||
let tab_btn = |label: &'static str, section: super::StartSection| {
|
||||
let tab_btn = |label: String, section: super::StartSection| {
|
||||
let is_active = active == section;
|
||||
button(text(label).size(14))
|
||||
.on_press(Message::StartSectionSelect(section))
|
||||
|
|
@ -2864,11 +2864,11 @@ fn start_page_content<'a>(
|
|||
})
|
||||
};
|
||||
let tab_bar = Row::with_children(vec![
|
||||
tab_btn("Recent Files", super::StartSection::Recent).into(),
|
||||
tab_btn("Videos", super::StartSection::Videos).into(),
|
||||
tab_btn("Welcome", super::StartSection::Welcome).into(),
|
||||
tab_btn("Discussions", super::StartSection::Discussions).into(),
|
||||
tab_btn("Supporters", super::StartSection::Supporters).into(),
|
||||
tab_btn(crate::tr!("start-recent-files"), super::StartSection::Recent).into(),
|
||||
tab_btn(crate::tr!("start-videos"), super::StartSection::Videos).into(),
|
||||
tab_btn(crate::tr!("start-welcome"), super::StartSection::Welcome).into(),
|
||||
tab_btn(crate::tr!("start-discussions"), super::StartSection::Discussions).into(),
|
||||
tab_btn(crate::tr!("start-supporters"), super::StartSection::Supporters).into(),
|
||||
])
|
||||
.spacing(6.0)
|
||||
.align_y(iced::Center)
|
||||
|
|
@ -2944,11 +2944,11 @@ pub(super) fn recent_files_panel<'a>(
|
|||
) -> Element<'a, Message> {
|
||||
// Title mirrors the Supporters rail: size 15 in the bright text colour,
|
||||
// followed by a 12px gap before the content.
|
||||
let title = text("Recent Documents").size(15);
|
||||
let title = text(crate::tr!("start-recent-documents")).size(15);
|
||||
|
||||
let body: Element<'a, Message> = if recents.is_empty() {
|
||||
container(
|
||||
text("Files you open will show up here.")
|
||||
text(crate::tr!("start-no-recent-files"))
|
||||
.size(12)
|
||||
.style(start_muted_style)
|
||||
)
|
||||
|
|
@ -2976,7 +2976,7 @@ pub(super) fn recent_files_panel<'a>(
|
|||
// directory line.
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let dir = if dir.is_empty() {
|
||||
"Browser storage".to_string()
|
||||
crate::tr!("start-browser-storage")
|
||||
} else {
|
||||
dir
|
||||
};
|
||||
|
|
@ -3087,7 +3087,7 @@ pub(super) fn recent_files_panel<'a>(
|
|||
.padding([2, 6])
|
||||
.width(iced::Length::Fixed(46.0));
|
||||
let limit_row = row![
|
||||
text("Keep recent files").size(11).style(start_muted_style).width(Fill),
|
||||
text(crate::tr!("start-keep-recent-files")).size(11).style(start_muted_style).width(Fill),
|
||||
button(crate::ui::icons::themed(crate::ui::icons::MINUS, 11.0))
|
||||
.on_press(Message::SetRecentLimit(shown.saturating_sub(STEP)))
|
||||
.padding([3, 6])
|
||||
|
|
|
|||
Loading…
Reference in a new issue