From ba65ec7bcaafc60f43ff1388a237d37de7abd99a Mon Sep 17 00:00:00 2001 From: snnh Date: Wed, 13 May 2026 18:57:29 +0800 Subject: [PATCH 01/13] feat(i18n): add internationalization support with language selection and translation tokens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat(i18n): 添加国际化支持,包括语言选择和翻译令牌 --- files.cmake | 2 + include/dusk/settings.h | 1 + res/HarmonyOS_Sans_Regular.ttf | Bin 0 -> 146616 bytes res/i18n/en.xml | 268 ++++++++++++++ res/i18n/fr.xml | 268 ++++++++++++++ res/i18n/ja.xml | 268 ++++++++++++++ res/i18n/zh-cn.xml | 268 ++++++++++++++ res/rml/overlay.rcss | 8 + res/rml/prelaunch.rcss | 8 + res/rml/window.rcss | 7 +- src/dusk/settings.cpp | 2 + src/dusk/ui/i18n.cpp | 276 +++++++++++++++ src/dusk/ui/i18n.hpp | 18 + src/dusk/ui/menu_bar.cpp | 31 +- src/dusk/ui/modal.cpp | 3 +- src/dusk/ui/overlay.cpp | 20 +- src/dusk/ui/prelaunch.cpp | 63 ++-- src/dusk/ui/settings.cpp | 617 ++++++++++++++++----------------- src/dusk/ui/ui.cpp | 26 ++ 19 files changed, 1778 insertions(+), 376 deletions(-) create mode 100644 res/HarmonyOS_Sans_Regular.ttf create mode 100644 res/i18n/en.xml create mode 100644 res/i18n/fr.xml create mode 100644 res/i18n/ja.xml create mode 100644 res/i18n/zh-cn.xml create mode 100644 src/dusk/ui/i18n.cpp create mode 100644 src/dusk/ui/i18n.hpp diff --git a/files.cmake b/files.cmake index eb8c62bee5..60a97526c5 100644 --- a/files.cmake +++ b/files.cmake @@ -1481,6 +1481,8 @@ set(DUSK_FILES src/dusk/ui/graphics_tuner.hpp src/dusk/ui/input.cpp src/dusk/ui/input.hpp + src/dusk/ui/i18n.cpp + src/dusk/ui/i18n.hpp src/dusk/ui/modal.cpp src/dusk/ui/modal.hpp src/dusk/ui/nav_types.hpp diff --git a/include/dusk/settings.h b/include/dusk/settings.h index bd6f30683b..c20ab75910 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -219,6 +219,7 @@ struct UserSettings { struct { ConfigVar isoPath; ConfigVar isoVerification; + ConfigVar uiLanguage; ConfigVar graphicsBackend; ConfigVar skipPreLaunchUI; ConfigVar showPipelineCompilation; diff --git a/res/HarmonyOS_Sans_Regular.ttf b/res/HarmonyOS_Sans_Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..d7eccac8ee2259a042d7434fbf65dfe398265b24 GIT binary patch literal 146616 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 + + Prelaunch + Video + Input + Audio + Gameplay + Cheats + Interface + Display + Resolution + Post-Processing + Rendering + Controller + Camera + Gyro + Tools + Volume + Effects + Tweaks + General + Difficulty + Quality of Life + Speedrunning + Resources + Abilities + Dusklight + Types + Game + UI Language + English + Simplified Chinese +
Applies to Dusklight UI text.
+ Settings + Warp + Editor + Achievements + Reset + Quit + Reset Timer + Verifying disc image + Cancelling... + Opening disc image... + Play + Select Disc Image + No disc image found. + Pending restart. + Disc ready. + Disc hash mismatch. + Disc not verified. + Disc unavailable. + Checking for updates... + Update available! + Failed to check for updates + Reset Game + Unsaved progress will be lost. + Unsaved progress will be lost.
Tip: You can also reset by holding Start+X+B
+ Cancel + Quit Dusklight + Update check failed with an unknown exception +

You may proceed at your own risk.
+ Disc verification warning + Continue anyway + Disc verification error + OK + Restart later + Restart now + Apply Options + A restart is required to apply selected options.

Restart now to apply them immediately?
+ A restart is required to apply selected options.

Close and reopen Dusklight to apply them.
+ Disc Image + Set the disc image that Dusklight uses to launch the game.

Changes require a restart.
+ Data Folder + The data folder is where Dusklight stores settings, saves, logs, texture replacements, and other app data. + Open Data Folder + Change Data Folder + Portable Mode + Reset to Default + Data will be migrated automatically on restart. + Language + German + French + Spanish + Italian +
Changes require a restart.
+ Graphics Backend + Save File Type + Open the folder where Dusklight stores settings, saves, logs, texture replacements, and other app data. + Notifications + Off + All + Some + Select All + Select None +
Choose which notifications can be displayed.
+ Crash Reporting + Dusklight can automatically send crash reports to the developers. Crash reports contain the following:
• Operating system version
• CPU architecture
• GPU model & driver version
• File paths (may include account username)
• Stack trace
+ Skip Dusklight Main Menu + When starting Dusklight, skip the main menu and boot straight into the game if a disc image is available. + Show Pipeline Compilation + Show an overlay when shaders are being compiled for your hardware. + Check for Updates + Checks GitHub releases for a new Dusklight version on startup.

No personal information is transmitted or collected.
+ No controller assigned + Configure controller port 1 in Settings. + 3-finger tap or + Press F1 or + to open menu + LiveSplit connected + LiveSplit disconnected + Adjusts the volume of all sounds in the game + Allow Background Input + Allow controller input even when the game window is not focused + Allows wolf link to howl and change the time of day + Always collect rupees even if your wallet is too full + Autosave + Autosaves the game when going to a new area, opening a dungeon door, or getting a new item. + Bigger Wallets + Bloom + Bloom Brightness + Bottom Left + Bottom Right + Configure Controller + Connect to LiveSplit server on localhost:16834. For this to work you must right click LiveSplit, and turn on Control -> Start TCP Server. To see IGT in LiveSplit you must change your comparison to Game Time. + Damage Multiplier + Disable Cutscene Pillarboxing + Disable Rupee Cutscenes + Disable the beeping sound when having low health. + Disables the elements of the main HUD of the game.
Useful for a more immersive experience.
+ Display the current framerate in a corner of the screen while playing + Display the RTA timer. IGT is always visible. + Dusklight Menu Sounds + Emulate surround sound via hrtf recommended only for use with headphones + Enable Depth Of Field + Enable Mini Map Shadows + Enable Reverb + Enable Rotating Link Doll + Enable Spatial Sound + Enable Vsync + Enables gyro controls for rollgoal in hena s cabin + Enables gyro controls while in look mode, aiming a hawk, and aiming supported items.

Supported items include the Slingshot, Gale Boomerang, Hero's Bow, Clawshot(s), Ball and Chain, and Dominion Rod.
+ Enables rotating Link in the collection menu with the C-Stick. + Enables speedrunning options while restricting certain gameplay modifiers. + Enables the reverb effect in game audio + Enables twin-stick camera control, letting the C-Stick move the camera vertically as well as horizontally. + Faster Climbing + Faster Tears Of Light + Free Camera + Free Camera Sensitivity + Gyro Aim + Gyro Deadband + Gyro Input Method + Gyro Pitch Sensitivity + Gyro Rollgoal + Gyro Smoothing + Gyro Yaw Sensitivity + Hearts will never drop from enemies, pots, and various other places. + Hold b for instant text + Hold tab to increase game speed by up to 4x + Ignores small gyro movement to reduce drift and jitter + Instant Death + Instant Saves + Internal Resolution + Invert Camera X Axis + Invert Camera Y Axis + Invert first person x axis + Invert first person y axis + Invert Gyro Pitch + Invert Gyro Yaw + Invert horizontal camera movement + Invert Horizontal Gyro Aiming + Invert horizontal movement while aiming with items or first person camera. Applies only to the control stick (the gyroscope can be inverted in Input settings). + Invert vertical camera movement when free camera is enabled + Invert Vertical Gyro Aiming + Invert vertical movement while aiming with items or first person camera. Applies only to the control stick (the gyroscope can be inverted in Input settings). + Link will not recoil when his sword hits walls + Livesplit Connection + Lock 4 3 aspect ratio + Lock the game s aspect ratio to the original + Makes text scroll immediately by holding B. + Minimal Hud + Mirror Mode + Mirrors the world horizontally, matching the Wii version of the game. + Mouse + Multiplies incoming damage. + No 2nd fish for cat + No Climbing Miss Animation + No Heart Drops + No Low HP Sound + No Rupee Returns + No Sword Recoil + Non-Stop Midna's Lament + Open controller binding configuration + Pause On Focus Lost + Pause the game when window focus is lost + Play sound effects when navigating the dusklight menu + Press + Prevents enemy music while Midna's Lament is playing. + Prevents Link from playing a struggle animation when grabbing ledges or climbing on vines. + Quick Transform R Y + Quicker climbing on ladders and vines like the HD version. + Reset Key + Restore Default Window Size + Restore wii 1 0 glitches + Restores patched glitches from Wii USA 1.0, the first released version. + Rollgoal Sensitivity + Rupees will not play cutscenes after you have collected them the first time. + Sensor +
Sensor reads motion directly from a supported controller's gyro via SDL.

Mouse treats mouse input as gyro, intended for use with the Steam Deck.

Mouse input cannot currently be used with Gyro Rollgoal.
+ Shadow Resolution + Show FPS Counter + Show RTA + Skip needing to catch a second fish for Sera's cat. + Skips the delay when writing to the Memory Card. + Speedrun Mode + Sun's Song (R+X) + Synchronizes the frame rate to your monitor s refresh rate + Tears of Light dropped by Shadow Insects pop out faster like the HD version. + Toggle Fullscreen + Top Left + Top Right + Transform instantly by pressing R and Y simultaneously. + Turbo Key + Unlock Framerate + Wallet sizes are like in the HD version. (500, 1000, 2000) + Japanese + Any hit will instantly kill you + Controls how strongly gyro input tilts the Rollgoal table + to reset the game. + Adjusts twin-stick camera sensitivity + Controls vertical gyro aiming sensitivity + Controls horizontal gyro aiming sensitivity + Higher values smooth gyro input over time + Infinite Hearts + Keeps your health full. + Infinite Arrows + Keeps your arrow count full. + Infinite Seeds + Keeps your slingshot pellets (seeds) full. + Infinite Bombs + Keeps all bomb bags full. + Infinite Oil + Keeps your lantern oil full. + Infinite Oxygen + Keeps your underwater oxygen meter full. + Infinite Rupees + Keeps your rupee count full. + No Item Timer + Item drops such as rupees and hearts will never disappear after they drop. + Moon Jump (R+A) + Hold R and A to rise into the air. + Super Clawshot + Extends Clawshot behavior beyond the normal game rules. + Always Greatspin + Allows the Great Spin attack without requiring full health. + Fast Iron Boots + Speeds up movement while wearing the Iron Boots. + Can Transform Anywhere + Allows transforming even if NPCs are looking. + Fast Roll + Makes Link's roll animation and movement twice as fast. + Fast Spinner + Speeds up Spinner movement while holding R. + Free Magic Armor + Lets the magic armor work without consuming rupees. + Invincible Enemies + Prevents enemies from taking damage. +
diff --git a/res/i18n/fr.xml b/res/i18n/fr.xml new file mode 100644 index 0000000000..9a41fc64d5 --- /dev/null +++ b/res/i18n/fr.xml @@ -0,0 +1,268 @@ + + + Prelaunch + Video + Input + Audio + Gameplay + Cheats + Interface + Display + Resolution + Post-Processing + Rendering + Controller + Camera + Gyro + Tools + Volume + Effects + Tweaks + General + Difficulty + Quality of Life + Speedrunning + Resources + Abilities + Dusklight + Types + Game + UI Language + English + Simplified Chinese +
Applies to Dusklight UI text.
+ Settings + Warp + Editor + Achievements + Reset + Quit + Reset Timer + Verifying disc image + Cancelling... + Opening disc image... + Play + Select Disc Image + No disc image found. + Pending restart. + Disc ready. + Disc hash mismatch. + Disc not verified. + Disc unavailable. + Checking for updates... + Update available! + Failed to check for updates + Reset Game + Unsaved progress will be lost. + Unsaved progress will be lost.
Tip: You can also reset by holding Start+X+B
+ Cancel + Quit Dusklight + Update check failed with an unknown exception +

You may proceed at your own risk.
+ Disc verification warning + Continue anyway + Disc verification error + OK + Restart later + Restart now + Apply Options + A restart is required to apply selected options.

Restart now to apply them immediately?
+ A restart is required to apply selected options.

Close and reopen Dusklight to apply them.
+ Disc Image + Set the disc image that Dusklight uses to launch the game.

Changes require a restart.
+ Data Folder + The data folder is where Dusklight stores settings, saves, logs, texture replacements, and other app data. + Open Data Folder + Change Data Folder + Portable Mode + Reset to Default + Data will be migrated automatically on restart. + Language + German + French + Spanish + Italian +
Changes require a restart.
+ Graphics Backend + Save File Type + Open the folder where Dusklight stores settings, saves, logs, texture replacements, and other app data. + Notifications + Off + All + Some + Select All + Select None +
Choose which notifications can be displayed.
+ Crash Reporting + Dusklight can automatically send crash reports to the developers. Crash reports contain the following:
• Operating system version
• CPU architecture
• GPU model & driver version
• File paths (may include account username)
• Stack trace
+ Skip Dusklight Main Menu + When starting Dusklight, skip the main menu and boot straight into the game if a disc image is available. + Show Pipeline Compilation + Show an overlay when shaders are being compiled for your hardware. + Check for Updates + Checks GitHub releases for a new Dusklight version on startup.

No personal information is transmitted or collected.
+ No controller assigned + Configure controller port 1 in Settings. + 3-finger tap or + Press F1 or + to open menu + LiveSplit connected + LiveSplit disconnected + Adjusts the volume of all sounds in the game + Allow Background Input + Allow controller input even when the game window is not focused + Allows wolf link to howl and change the time of day + Always collect rupees even if your wallet is too full + Autosave + Autosaves the game when going to a new area, opening a dungeon door, or getting a new item. + Bigger Wallets + Bloom + Bloom Brightness + Bottom Left + Bottom Right + Configure Controller + Connect to LiveSplit server on localhost:16834. For this to work you must right click LiveSplit, and turn on Control -> Start TCP Server. To see IGT in LiveSplit you must change your comparison to Game Time. + Damage Multiplier + Disable Cutscene Pillarboxing + Disable Rupee Cutscenes + Disable the beeping sound when having low health. + Disables the elements of the main HUD of the game.
Useful for a more immersive experience.
+ Display the current framerate in a corner of the screen while playing + Display the RTA timer. IGT is always visible. + Dusklight Menu Sounds + Emulate surround sound via hrtf recommended only for use with headphones + Enable Depth Of Field + Enable Mini Map Shadows + Enable Reverb + Enable Rotating Link Doll + Enable Spatial Sound + Enable Vsync + Enables gyro controls for rollgoal in hena s cabin + Enables gyro controls while in look mode, aiming a hawk, and aiming supported items.

Supported items include the Slingshot, Gale Boomerang, Hero's Bow, Clawshot(s), Ball and Chain, and Dominion Rod.
+ Enables rotating Link in the collection menu with the C-Stick. + Enables speedrunning options while restricting certain gameplay modifiers. + Enables the reverb effect in game audio + Enables twin-stick camera control, letting the C-Stick move the camera vertically as well as horizontally. + Faster Climbing + Faster Tears Of Light + Free Camera + Free Camera Sensitivity + Gyro Aim + Gyro Deadband + Gyro Input Method + Gyro Pitch Sensitivity + Gyro Rollgoal + Gyro Smoothing + Gyro Yaw Sensitivity + Hearts will never drop from enemies, pots, and various other places. + Hold b for instant text + Hold tab to increase game speed by up to 4x + Ignores small gyro movement to reduce drift and jitter + Instant Death + Instant Saves + Internal Resolution + Invert Camera X Axis + Invert Camera Y Axis + Invert first person x axis + Invert first person y axis + Invert Gyro Pitch + Invert Gyro Yaw + Invert horizontal camera movement + Invert Horizontal Gyro Aiming + Invert horizontal movement while aiming with items or first person camera. Applies only to the control stick (the gyroscope can be inverted in Input settings). + Invert vertical camera movement when free camera is enabled + Invert Vertical Gyro Aiming + Invert vertical movement while aiming with items or first person camera. Applies only to the control stick (the gyroscope can be inverted in Input settings). + Link will not recoil when his sword hits walls + Livesplit Connection + Lock 4 3 aspect ratio + Lock the game s aspect ratio to the original + Makes text scroll immediately by holding B. + Minimal Hud + Mirror Mode + Mirrors the world horizontally, matching the Wii version of the game. + Mouse + Multiplies incoming damage. + No 2nd fish for cat + No Climbing Miss Animation + No Heart Drops + No Low HP Sound + No Rupee Returns + No Sword Recoil + Non-Stop Midna's Lament + Open controller binding configuration + Pause On Focus Lost + Pause the game when window focus is lost + Play sound effects when navigating the dusklight menu + Press + Prevents enemy music while Midna's Lament is playing. + Prevents Link from playing a struggle animation when grabbing ledges or climbing on vines. + Quick Transform R Y + Quicker climbing on ladders and vines like the HD version. + Reset Key + Restore Default Window Size + Restore wii 1 0 glitches + Restores patched glitches from Wii USA 1.0, the first released version. + Rollgoal Sensitivity + Rupees will not play cutscenes after you have collected them the first time. + Sensor +
Sensor reads motion directly from a supported controller's gyro via SDL.

Mouse treats mouse input as gyro, intended for use with the Steam Deck.

Mouse input cannot currently be used with Gyro Rollgoal.
+ Shadow Resolution + Show FPS Counter + Show RTA + Skip needing to catch a second fish for Sera's cat. + Skips the delay when writing to the Memory Card. + Speedrun Mode + Sun's Song (R+X) + Synchronizes the frame rate to your monitor s refresh rate + Tears of Light dropped by Shadow Insects pop out faster like the HD version. + Toggle Fullscreen + Top Left + Top Right + Transform instantly by pressing R and Y simultaneously. + Turbo Key + Unlock Framerate + Wallet sizes are like in the HD version. (500, 1000, 2000) + Japanese + Any hit will instantly kill you + Controls how strongly gyro input tilts the Rollgoal table + to reset the game. + Adjusts twin-stick camera sensitivity + Controls vertical gyro aiming sensitivity + Controls horizontal gyro aiming sensitivity + Higher values smooth gyro input over time + Infinite Hearts + Keeps your health full. + Infinite Arrows + Keeps your arrow count full. + Infinite Seeds + Keeps your slingshot pellets (seeds) full. + Infinite Bombs + Keeps all bomb bags full. + Infinite Oil + Keeps your lantern oil full. + Infinite Oxygen + Keeps your underwater oxygen meter full. + Infinite Rupees + Keeps your rupee count full. + No Item Timer + Item drops such as rupees and hearts will never disappear after they drop. + Moon Jump (R+A) + Hold R and A to rise into the air. + Super Clawshot + Extends Clawshot behavior beyond the normal game rules. + Always Greatspin + Allows the Great Spin attack without requiring full health. + Fast Iron Boots + Speeds up movement while wearing the Iron Boots. + Can Transform Anywhere + Allows transforming even if NPCs are looking. + Fast Roll + Makes Link's roll animation and movement twice as fast. + Fast Spinner + Speeds up Spinner movement while holding R. + Free Magic Armor + Lets the magic armor work without consuming rupees. + Invincible Enemies + Prevents enemies from taking damage. +
diff --git a/res/i18n/ja.xml b/res/i18n/ja.xml new file mode 100644 index 0000000000..9a41fc64d5 --- /dev/null +++ b/res/i18n/ja.xml @@ -0,0 +1,268 @@ + + + Prelaunch + Video + Input + Audio + Gameplay + Cheats + Interface + Display + Resolution + Post-Processing + Rendering + Controller + Camera + Gyro + Tools + Volume + Effects + Tweaks + General + Difficulty + Quality of Life + Speedrunning + Resources + Abilities + Dusklight + Types + Game + UI Language + English + Simplified Chinese +
Applies to Dusklight UI text.
+ Settings + Warp + Editor + Achievements + Reset + Quit + Reset Timer + Verifying disc image + Cancelling... + Opening disc image... + Play + Select Disc Image + No disc image found. + Pending restart. + Disc ready. + Disc hash mismatch. + Disc not verified. + Disc unavailable. + Checking for updates... + Update available! + Failed to check for updates + Reset Game + Unsaved progress will be lost. + Unsaved progress will be lost.
Tip: You can also reset by holding Start+X+B
+ Cancel + Quit Dusklight + Update check failed with an unknown exception +

You may proceed at your own risk.
+ Disc verification warning + Continue anyway + Disc verification error + OK + Restart later + Restart now + Apply Options + A restart is required to apply selected options.

Restart now to apply them immediately?
+ A restart is required to apply selected options.

Close and reopen Dusklight to apply them.
+ Disc Image + Set the disc image that Dusklight uses to launch the game.

Changes require a restart.
+ Data Folder + The data folder is where Dusklight stores settings, saves, logs, texture replacements, and other app data. + Open Data Folder + Change Data Folder + Portable Mode + Reset to Default + Data will be migrated automatically on restart. + Language + German + French + Spanish + Italian +
Changes require a restart.
+ Graphics Backend + Save File Type + Open the folder where Dusklight stores settings, saves, logs, texture replacements, and other app data. + Notifications + Off + All + Some + Select All + Select None +
Choose which notifications can be displayed.
+ Crash Reporting + Dusklight can automatically send crash reports to the developers. Crash reports contain the following:
• Operating system version
• CPU architecture
• GPU model & driver version
• File paths (may include account username)
• Stack trace
+ Skip Dusklight Main Menu + When starting Dusklight, skip the main menu and boot straight into the game if a disc image is available. + Show Pipeline Compilation + Show an overlay when shaders are being compiled for your hardware. + Check for Updates + Checks GitHub releases for a new Dusklight version on startup.

No personal information is transmitted or collected.
+ No controller assigned + Configure controller port 1 in Settings. + 3-finger tap or + Press F1 or + to open menu + LiveSplit connected + LiveSplit disconnected + Adjusts the volume of all sounds in the game + Allow Background Input + Allow controller input even when the game window is not focused + Allows wolf link to howl and change the time of day + Always collect rupees even if your wallet is too full + Autosave + Autosaves the game when going to a new area, opening a dungeon door, or getting a new item. + Bigger Wallets + Bloom + Bloom Brightness + Bottom Left + Bottom Right + Configure Controller + Connect to LiveSplit server on localhost:16834. For this to work you must right click LiveSplit, and turn on Control -> Start TCP Server. To see IGT in LiveSplit you must change your comparison to Game Time. + Damage Multiplier + Disable Cutscene Pillarboxing + Disable Rupee Cutscenes + Disable the beeping sound when having low health. + Disables the elements of the main HUD of the game.
Useful for a more immersive experience.
+ Display the current framerate in a corner of the screen while playing + Display the RTA timer. IGT is always visible. + Dusklight Menu Sounds + Emulate surround sound via hrtf recommended only for use with headphones + Enable Depth Of Field + Enable Mini Map Shadows + Enable Reverb + Enable Rotating Link Doll + Enable Spatial Sound + Enable Vsync + Enables gyro controls for rollgoal in hena s cabin + Enables gyro controls while in look mode, aiming a hawk, and aiming supported items.

Supported items include the Slingshot, Gale Boomerang, Hero's Bow, Clawshot(s), Ball and Chain, and Dominion Rod.
+ Enables rotating Link in the collection menu with the C-Stick. + Enables speedrunning options while restricting certain gameplay modifiers. + Enables the reverb effect in game audio + Enables twin-stick camera control, letting the C-Stick move the camera vertically as well as horizontally. + Faster Climbing + Faster Tears Of Light + Free Camera + Free Camera Sensitivity + Gyro Aim + Gyro Deadband + Gyro Input Method + Gyro Pitch Sensitivity + Gyro Rollgoal + Gyro Smoothing + Gyro Yaw Sensitivity + Hearts will never drop from enemies, pots, and various other places. + Hold b for instant text + Hold tab to increase game speed by up to 4x + Ignores small gyro movement to reduce drift and jitter + Instant Death + Instant Saves + Internal Resolution + Invert Camera X Axis + Invert Camera Y Axis + Invert first person x axis + Invert first person y axis + Invert Gyro Pitch + Invert Gyro Yaw + Invert horizontal camera movement + Invert Horizontal Gyro Aiming + Invert horizontal movement while aiming with items or first person camera. Applies only to the control stick (the gyroscope can be inverted in Input settings). + Invert vertical camera movement when free camera is enabled + Invert Vertical Gyro Aiming + Invert vertical movement while aiming with items or first person camera. Applies only to the control stick (the gyroscope can be inverted in Input settings). + Link will not recoil when his sword hits walls + Livesplit Connection + Lock 4 3 aspect ratio + Lock the game s aspect ratio to the original + Makes text scroll immediately by holding B. + Minimal Hud + Mirror Mode + Mirrors the world horizontally, matching the Wii version of the game. + Mouse + Multiplies incoming damage. + No 2nd fish for cat + No Climbing Miss Animation + No Heart Drops + No Low HP Sound + No Rupee Returns + No Sword Recoil + Non-Stop Midna's Lament + Open controller binding configuration + Pause On Focus Lost + Pause the game when window focus is lost + Play sound effects when navigating the dusklight menu + Press + Prevents enemy music while Midna's Lament is playing. + Prevents Link from playing a struggle animation when grabbing ledges or climbing on vines. + Quick Transform R Y + Quicker climbing on ladders and vines like the HD version. + Reset Key + Restore Default Window Size + Restore wii 1 0 glitches + Restores patched glitches from Wii USA 1.0, the first released version. + Rollgoal Sensitivity + Rupees will not play cutscenes after you have collected them the first time. + Sensor +
Sensor reads motion directly from a supported controller's gyro via SDL.

Mouse treats mouse input as gyro, intended for use with the Steam Deck.

Mouse input cannot currently be used with Gyro Rollgoal.
+ Shadow Resolution + Show FPS Counter + Show RTA + Skip needing to catch a second fish for Sera's cat. + Skips the delay when writing to the Memory Card. + Speedrun Mode + Sun's Song (R+X) + Synchronizes the frame rate to your monitor s refresh rate + Tears of Light dropped by Shadow Insects pop out faster like the HD version. + Toggle Fullscreen + Top Left + Top Right + Transform instantly by pressing R and Y simultaneously. + Turbo Key + Unlock Framerate + Wallet sizes are like in the HD version. (500, 1000, 2000) + Japanese + Any hit will instantly kill you + Controls how strongly gyro input tilts the Rollgoal table + to reset the game. + Adjusts twin-stick camera sensitivity + Controls vertical gyro aiming sensitivity + Controls horizontal gyro aiming sensitivity + Higher values smooth gyro input over time + Infinite Hearts + Keeps your health full. + Infinite Arrows + Keeps your arrow count full. + Infinite Seeds + Keeps your slingshot pellets (seeds) full. + Infinite Bombs + Keeps all bomb bags full. + Infinite Oil + Keeps your lantern oil full. + Infinite Oxygen + Keeps your underwater oxygen meter full. + Infinite Rupees + Keeps your rupee count full. + No Item Timer + Item drops such as rupees and hearts will never disappear after they drop. + Moon Jump (R+A) + Hold R and A to rise into the air. + Super Clawshot + Extends Clawshot behavior beyond the normal game rules. + Always Greatspin + Allows the Great Spin attack without requiring full health. + Fast Iron Boots + Speeds up movement while wearing the Iron Boots. + Can Transform Anywhere + Allows transforming even if NPCs are looking. + Fast Roll + Makes Link's roll animation and movement twice as fast. + Fast Spinner + Speeds up Spinner movement while holding R. + Free Magic Armor + Lets the magic armor work without consuming rupees. + Invincible Enemies + Prevents enemies from taking damage. +
diff --git a/res/i18n/zh-cn.xml b/res/i18n/zh-cn.xml new file mode 100644 index 0000000000..9a41fc64d5 --- /dev/null +++ b/res/i18n/zh-cn.xml @@ -0,0 +1,268 @@ + + + Prelaunch + Video + Input + Audio + Gameplay + Cheats + Interface + Display + Resolution + Post-Processing + Rendering + Controller + Camera + Gyro + Tools + Volume + Effects + Tweaks + General + Difficulty + Quality of Life + Speedrunning + Resources + Abilities + Dusklight + Types + Game + UI Language + English + Simplified Chinese +
Applies to Dusklight UI text.
+ Settings + Warp + Editor + Achievements + Reset + Quit + Reset Timer + Verifying disc image + Cancelling... + Opening disc image... + Play + Select Disc Image + No disc image found. + Pending restart. + Disc ready. + Disc hash mismatch. + Disc not verified. + Disc unavailable. + Checking for updates... + Update available! + Failed to check for updates + Reset Game + Unsaved progress will be lost. + Unsaved progress will be lost.
Tip: You can also reset by holding Start+X+B
+ Cancel + Quit Dusklight + Update check failed with an unknown exception +

You may proceed at your own risk.
+ Disc verification warning + Continue anyway + Disc verification error + OK + Restart later + Restart now + Apply Options + A restart is required to apply selected options.

Restart now to apply them immediately?
+ A restart is required to apply selected options.

Close and reopen Dusklight to apply them.
+ Disc Image + Set the disc image that Dusklight uses to launch the game.

Changes require a restart.
+ Data Folder + The data folder is where Dusklight stores settings, saves, logs, texture replacements, and other app data. + Open Data Folder + Change Data Folder + Portable Mode + Reset to Default + Data will be migrated automatically on restart. + Language + German + French + Spanish + Italian +
Changes require a restart.
+ Graphics Backend + Save File Type + Open the folder where Dusklight stores settings, saves, logs, texture replacements, and other app data. + Notifications + Off + All + Some + Select All + Select None +
Choose which notifications can be displayed.
+ Crash Reporting + Dusklight can automatically send crash reports to the developers. Crash reports contain the following:
• Operating system version
• CPU architecture
• GPU model & driver version
• File paths (may include account username)
• Stack trace
+ Skip Dusklight Main Menu + When starting Dusklight, skip the main menu and boot straight into the game if a disc image is available. + Show Pipeline Compilation + Show an overlay when shaders are being compiled for your hardware. + Check for Updates + Checks GitHub releases for a new Dusklight version on startup.

No personal information is transmitted or collected.
+ No controller assigned + Configure controller port 1 in Settings. + 3-finger tap or + Press F1 or + to open menu + LiveSplit connected + LiveSplit disconnected + Adjusts the volume of all sounds in the game + Allow Background Input + Allow controller input even when the game window is not focused + Allows wolf link to howl and change the time of day + Always collect rupees even if your wallet is too full + Autosave + Autosaves the game when going to a new area, opening a dungeon door, or getting a new item. + Bigger Wallets + Bloom + Bloom Brightness + Bottom Left + Bottom Right + Configure Controller + Connect to LiveSplit server on localhost:16834. For this to work you must right click LiveSplit, and turn on Control -> Start TCP Server. To see IGT in LiveSplit you must change your comparison to Game Time. + Damage Multiplier + Disable Cutscene Pillarboxing + Disable Rupee Cutscenes + Disable the beeping sound when having low health. + Disables the elements of the main HUD of the game.
Useful for a more immersive experience.
+ Display the current framerate in a corner of the screen while playing + Display the RTA timer. IGT is always visible. + Dusklight Menu Sounds + Emulate surround sound via hrtf recommended only for use with headphones + Enable Depth Of Field + Enable Mini Map Shadows + Enable Reverb + Enable Rotating Link Doll + Enable Spatial Sound + Enable Vsync + Enables gyro controls for rollgoal in hena s cabin + Enables gyro controls while in look mode, aiming a hawk, and aiming supported items.

Supported items include the Slingshot, Gale Boomerang, Hero's Bow, Clawshot(s), Ball and Chain, and Dominion Rod.
+ Enables rotating Link in the collection menu with the C-Stick. + Enables speedrunning options while restricting certain gameplay modifiers. + Enables the reverb effect in game audio + Enables twin-stick camera control, letting the C-Stick move the camera vertically as well as horizontally. + Faster Climbing + Faster Tears Of Light + Free Camera + Free Camera Sensitivity + Gyro Aim + Gyro Deadband + Gyro Input Method + Gyro Pitch Sensitivity + Gyro Rollgoal + Gyro Smoothing + Gyro Yaw Sensitivity + Hearts will never drop from enemies, pots, and various other places. + Hold b for instant text + Hold tab to increase game speed by up to 4x + Ignores small gyro movement to reduce drift and jitter + Instant Death + Instant Saves + Internal Resolution + Invert Camera X Axis + Invert Camera Y Axis + Invert first person x axis + Invert first person y axis + Invert Gyro Pitch + Invert Gyro Yaw + Invert horizontal camera movement + Invert Horizontal Gyro Aiming + Invert horizontal movement while aiming with items or first person camera. Applies only to the control stick (the gyroscope can be inverted in Input settings). + Invert vertical camera movement when free camera is enabled + Invert Vertical Gyro Aiming + Invert vertical movement while aiming with items or first person camera. Applies only to the control stick (the gyroscope can be inverted in Input settings). + Link will not recoil when his sword hits walls + Livesplit Connection + Lock 4 3 aspect ratio + Lock the game s aspect ratio to the original + Makes text scroll immediately by holding B. + Minimal Hud + Mirror Mode + Mirrors the world horizontally, matching the Wii version of the game. + Mouse + Multiplies incoming damage. + No 2nd fish for cat + No Climbing Miss Animation + No Heart Drops + No Low HP Sound + No Rupee Returns + No Sword Recoil + Non-Stop Midna's Lament + Open controller binding configuration + Pause On Focus Lost + Pause the game when window focus is lost + Play sound effects when navigating the dusklight menu + Press + Prevents enemy music while Midna's Lament is playing. + Prevents Link from playing a struggle animation when grabbing ledges or climbing on vines. + Quick Transform R Y + Quicker climbing on ladders and vines like the HD version. + Reset Key + Restore Default Window Size + Restore wii 1 0 glitches + Restores patched glitches from Wii USA 1.0, the first released version. + Rollgoal Sensitivity + Rupees will not play cutscenes after you have collected them the first time. + Sensor +
Sensor reads motion directly from a supported controller's gyro via SDL.

Mouse treats mouse input as gyro, intended for use with the Steam Deck.

Mouse input cannot currently be used with Gyro Rollgoal.
+ Shadow Resolution + Show FPS Counter + Show RTA + Skip needing to catch a second fish for Sera's cat. + Skips the delay when writing to the Memory Card. + Speedrun Mode + Sun's Song (R+X) + Synchronizes the frame rate to your monitor s refresh rate + Tears of Light dropped by Shadow Insects pop out faster like the HD version. + Toggle Fullscreen + Top Left + Top Right + Transform instantly by pressing R and Y simultaneously. + Turbo Key + Unlock Framerate + Wallet sizes are like in the HD version. (500, 1000, 2000) + Japanese + Any hit will instantly kill you + Controls how strongly gyro input tilts the Rollgoal table + to reset the game. + Adjusts twin-stick camera sensitivity + Controls vertical gyro aiming sensitivity + Controls horizontal gyro aiming sensitivity + Higher values smooth gyro input over time + Infinite Hearts + Keeps your health full. + Infinite Arrows + Keeps your arrow count full. + Infinite Seeds + Keeps your slingshot pellets (seeds) full. + Infinite Bombs + Keeps all bomb bags full. + Infinite Oil + Keeps your lantern oil full. + Infinite Oxygen + Keeps your underwater oxygen meter full. + Infinite Rupees + Keeps your rupee count full. + No Item Timer + Item drops such as rupees and hearts will never disappear after they drop. + Moon Jump (R+A) + Hold R and A to rise into the air. + Super Clawshot + Extends Clawshot behavior beyond the normal game rules. + Always Greatspin + Allows the Great Spin attack without requiring full health. + Fast Iron Boots + Speeds up movement while wearing the Iron Boots. + Can Transform Anywhere + Allows transforming even if NPCs are looking. + Fast Roll + Makes Link's roll animation and movement twice as fast. + Fast Spinner + Speeds up Spinner movement while holding R. + Free Magic Armor + Lets the magic armor work without consuming rupees. + Invincible Enemies + Prevents enemies from taking damage. +
diff --git a/res/rml/overlay.rcss b/res/rml/overlay.rcss index 8927e1a01c..10c64aaffe 100644 --- a/res/rml/overlay.rcss +++ b/res/rml/overlay.rcss @@ -20,6 +20,14 @@ body { pointer-events: none; } +.lang-zh-cn body, +.lang-zh-cn body toast, +.lang-zh-cn body toast heading, +.lang-zh-cn body toast message, +.lang-zh-cn body fps { + font-family: "HarmonyOS Sans"; +} + fps, toast { position: absolute; diff --git a/res/rml/prelaunch.rcss b/res/rml/prelaunch.rcss index f5d7240d56..3fd87a08af 100644 --- a/res/rml/prelaunch.rcss +++ b/res/rml/prelaunch.rcss @@ -14,6 +14,14 @@ body { z-index: -1; } +.lang-zh-cn body, +.lang-zh-cn body menu, +.lang-zh-cn body menu button, +.lang-zh-cn body disc-info, +.lang-zh-cn body version-info { + font-family: "HarmonyOS Sans"; +} + .gradient { position: absolute; width: 100%; diff --git a/res/rml/window.rcss b/res/rml/window.rcss index ab338e97c7..68df9c81cd 100644 --- a/res/rml/window.rcss +++ b/res/rml/window.rcss @@ -14,8 +14,11 @@ body { color: #E0DBC8; } -b { - font-weight: bold; +.lang-zh-cn body, +.lang-zh-cn body window tab-bar, +.lang-zh-cn body window content pane, +.lang-zh-cn body window content pane:last-of-type > div { + font-family: "HarmonyOS Sans"; } window { diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index 5861241fe3..88a902a02a 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -131,6 +131,7 @@ UserSettings g_userSettings = { .backend = { .isoPath {"backend.isoPath", ""}, .isoVerification {"backend.isoVerification", DiscVerificationState::Unknown}, + .uiLanguage {"backend.uiLanguage", "en"}, .graphicsBackend {"backend.graphicsBackend", "auto"}, .skipPreLaunchUI {"backend.skipPreLaunchUI", false}, .showPipelineCompilation {"backend.showPipelineCompilation", false}, @@ -279,6 +280,7 @@ void registerSettings() { Register(g_userSettings.backend.isoPath); Register(g_userSettings.backend.isoVerification); + Register(g_userSettings.backend.uiLanguage); Register(g_userSettings.backend.graphicsBackend); Register(g_userSettings.backend.skipPreLaunchUI); Register(g_userSettings.backend.showPipelineCompilation); diff --git a/src/dusk/ui/i18n.cpp b/src/dusk/ui/i18n.cpp new file mode 100644 index 0000000000..9971f0e3ca --- /dev/null +++ b/src/dusk/ui/i18n.cpp @@ -0,0 +1,276 @@ +#include "i18n.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "aurora/lib/logging.hpp" +#include "dusk/io.hpp" +#include "ui.hpp" + +namespace dusk::ui::i18n { +namespace { + +aurora::Module I18nLog{"dusk::ui::i18n"}; + +absl::flat_hash_map sDictionary; +std::string sLanguage = "en"; +bool sInitialized = false; + +std::string to_lower_ascii(std::string_view value) { + std::string lowered; + lowered.reserve(value.size()); + for (const char ch : value) { + lowered.push_back(static_cast(std::tolower(static_cast(ch)))); + } + return lowered; +} + +Rml::String trim_ascii(std::string_view value) { + std::size_t begin = 0; + std::size_t end = value.size(); + while (begin < end && std::isspace(static_cast(value[begin]))) { + ++begin; + } + while (end > begin && std::isspace(static_cast(value[end - 1]))) { + --end; + } + return Rml::String(value.substr(begin, end - begin)); +} + +Rml::String decode_xml_entities(std::string_view value) { + Rml::String decoded; + decoded.reserve(value.size()); + for (std::size_t i = 0; i < value.size(); ++i) { + if (value[i] != '&') { + decoded.push_back(value[i]); + continue; + } + + if (value.compare(i, 5, "&") == 0) { + decoded.push_back('&'); + i += 4; + continue; + } + if (value.compare(i, 4, "<") == 0) { + decoded.push_back('<'); + i += 3; + continue; + } + if (value.compare(i, 4, ">") == 0) { + decoded.push_back('>'); + i += 3; + continue; + } + if (value.compare(i, 6, """) == 0) { + decoded.push_back('"'); + i += 5; + continue; + } + if (value.compare(i, 6, "'") == 0) { + decoded.push_back('\''); + i += 5; + continue; + } + + if (i + 3 < value.size() && value[i + 1] == '#') { + const bool isHex = (value[i + 2] == 'x' || value[i + 2] == 'X'); + const std::size_t digitsBegin = i + (isHex ? 3 : 2); + const std::size_t semicolon = value.find(';', digitsBegin); + if (semicolon != std::string_view::npos) { + const auto digits = value.substr(digitsBegin, semicolon - digitsBegin); + unsigned int codepoint = 0; + auto [ptr, ec] = std::from_chars( + digits.data(), digits.data() + digits.size(), codepoint, isHex ? 16 : 10); + if (ec == std::errc() && ptr == digits.data() + digits.size()) { + if (codepoint <= 0x7F) { + decoded.push_back(static_cast(codepoint)); + } else if (codepoint <= 0x7FF) { + decoded.push_back(static_cast(0xC0 | (codepoint >> 6))); + decoded.push_back(static_cast(0x80 | (codepoint & 0x3F))); + } else if (codepoint <= 0xFFFF) { + decoded.push_back(static_cast(0xE0 | (codepoint >> 12))); + decoded.push_back(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + decoded.push_back(static_cast(0x80 | (codepoint & 0x3F))); + } else if (codepoint <= 0x10FFFF) { + decoded.push_back(static_cast(0xF0 | (codepoint >> 18))); + decoded.push_back(static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + decoded.push_back(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + decoded.push_back(static_cast(0x80 | (codepoint & 0x3F))); + } + i = semicolon; + continue; + } + } + } + + decoded.push_back('&'); + } + return decoded; +} + +bool parse_language_xml(std::string_view xml) { + sDictionary.clear(); + std::size_t cursor = 0; + while (true) { + const std::size_t entryOpen = xml.find("', keyEnd); + if (tagEnd == std::string_view::npos) { + cursor = entryOpen + 6; + continue; + } + const std::size_t entryClose = xml.find("", tagEnd + 1); + if (entryClose == std::string_view::npos) { + cursor = entryOpen + 6; + continue; + } + + const auto key = trim_ascii(xml.substr(keyBegin, keyEnd - keyBegin)); + const auto rawValue = xml.substr(tagEnd + 1, entryClose - (tagEnd + 1)); + sDictionary[key] = decode_xml_entities(rawValue); + cursor = entryClose + 8; + } + return !sDictionary.empty(); +} + +bool load_dictionary(std::string_view language) { + const std::filesystem::path filePath = resource_path( + std::filesystem::path("i18n") / fmt::format("{}.xml", language)); + try { + const auto bytes = io::FileStream::ReadAllBytes(filePath); + const std::string xml(bytes.begin(), bytes.end()); + if (!parse_language_xml(xml)) { + I18nLog.error("No valid i18n entries found in '{}'", io::fs_path_to_string(filePath)); + return false; + } + return true; + } catch (const std::exception& e) { + I18nLog.error("Failed to load i18n dictionary '{}': {}", io::fs_path_to_string(filePath), e.what()); + return false; + } +} + +bool translate_token(Rml::String& translated, std::string_view token) { + const auto it = sDictionary.find(Rml::String(token)); + if (it == sDictionary.end()) { + return false; + } + translated += it->second; + return true; +} + +} // namespace + +bool initialize() noexcept { + sInitialized = true; + return set_language("en"); +} + +void shutdown() noexcept { + sDictionary.clear(); + sLanguage = "en"; + sInitialized = false; +} + +bool set_language(std::string_view language) noexcept { + std::string normalized = to_lower_ascii(language); + if (normalized.empty()) { + normalized = "en"; + } + if (normalized != "en" && normalized != "zh-cn" && normalized != "zh-hans" && + normalized != "fr" && normalized != "ja") + { + normalized = "en"; + } + + if (normalized == sLanguage && !sDictionary.empty()) { + return true; + } + + if (load_dictionary(normalized)) { + sLanguage = std::move(normalized); + return true; + } + + if (normalized != "en" && load_dictionary("en")) { + // Keep requested UI language so style decisions (e.g. zh-cn default font) + // still apply even when text falls back to en resources. + sLanguage = std::move(normalized); + return true; + } + + return false; +} + +const std::string& language() noexcept { + return sLanguage; +} + +bool is_simplified_chinese() noexcept { + return sLanguage == "zh-cn" || sLanguage == "zh-hans"; +} + +bool use_harmonyos_font() noexcept { + return is_simplified_chinese() || sLanguage == "ja"; +} + +int translate(Rml::String& translated, const Rml::String& input) { + if (!sInitialized || sDictionary.empty()) { + translated = input; + return 0; + } + + translated.clear(); + translated.reserve(input.size()); + + int replacements = 0; + std::size_t cursor = 0; + while (cursor < input.size()) { + const std::size_t open = input.find('[', cursor); + if (open == Rml::String::npos) { + translated.append(input.substr(cursor)); + break; + } + + translated.append(input.substr(cursor, open - cursor)); + const std::size_t close = input.find(']', open + 1); + if (close == Rml::String::npos) { + translated.append(input.substr(open)); + break; + } + + const std::string_view token(input.data() + open + 1, close - open - 1); + if (translate_token(translated, token)) { + ++replacements; + } else { + translated.append(input.substr(open, close - open + 1)); + } + cursor = close + 1; + } + return replacements; +} + +} // namespace dusk::ui::i18n diff --git a/src/dusk/ui/i18n.hpp b/src/dusk/ui/i18n.hpp new file mode 100644 index 0000000000..12641d612a --- /dev/null +++ b/src/dusk/ui/i18n.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +#include +#include + +namespace dusk::ui::i18n { + +bool initialize() noexcept; +void shutdown() noexcept; +bool set_language(std::string_view language) noexcept; +const std::string& language() noexcept; +bool is_simplified_chinese() noexcept; +bool use_harmonyos_font() noexcept; +int translate(Rml::String& translated, const Rml::String& input); + +} // namespace dusk::ui::i18n diff --git a/src/dusk/ui/menu_bar.cpp b/src/dusk/ui/menu_bar.cpp index 1be5c7abb0..debd05c809 100644 --- a/src/dusk/ui/menu_bar.cpp +++ b/src/dusk/ui/menu_bar.cpp @@ -51,27 +51,26 @@ MenuBar::MenuBar() : Document(kDocumentSource), mRoot(mDocument->GetElementById( }, .autoSelect = false, }); - mTabBar->add_tab("Settings", [this] { push(std::make_unique()); }); + mTabBar->add_tab("[SETTINGS]", [this] { push(std::make_unique()); }); if (getSettings().backend.enableAdvancedSettings) { - mTabBar->add_tab("Warp", [this] { push(std::make_unique()); }); - mTabBar->add_tab("Editor", [this] { push(std::make_unique()); }); + mTabBar->add_tab("[WARP]", [this] { push(std::make_unique()); }); + mTabBar->add_tab("[EDITOR]", [this] { push(std::make_unique()); }); } - mTabBar->add_tab("Achievements", [this] { push(std::make_unique()); }); + mTabBar->add_tab("[ACHIEVEMENTS]", [this] { push(std::make_unique()); }); - mTabBar->add_tab("Reset", [this] { + mTabBar->add_tab("[RESET]", [this] { mTabBar->set_active_tab(-1); const auto dismiss = [](Modal& modal) { modal.pop(); }; push(std::make_unique(Modal::Props{ - .title = "Reset Game", - .bodyRml = "Unsaved progress will be lost.
" - "Tip: You can also reset by holding Start+X+B", + .title = "[RESET_GAME]", + .bodyRml = "[UNSAVED_PROGRESS_WILL_BE_LOST_TIP_YOU_CAN_ALSO_RESET_BY_HOLDING_START_X]", .actions = { ModalAction{ - .label = "Cancel", + .label = "[CANCEL]", .onPressed = [this, dismiss](Modal& modal) { mDoAud_seStartMenu(kSoundWindowClose); @@ -79,7 +78,7 @@ MenuBar::MenuBar() : Document(kDocumentSource), mRoot(mDocument->GetElementById( }, }, ModalAction{ - .label = "Reset", + .label = "[RESET]", .onPressed = [this, dismiss](Modal& modal) { mDoAud_seStartMenu(kSoundClick); @@ -97,16 +96,16 @@ MenuBar::MenuBar() : Document(kDocumentSource), mRoot(mDocument->GetElementById( .icon = "question-mark", })); }); - mTabBar->add_tab("Quit", [this] { + mTabBar->add_tab("[QUIT]", [this] { mTabBar->set_active_tab(-1); const auto dismiss = [](Modal& modal) { modal.pop(); }; push(std::make_unique(Modal::Props{ - .title = "Quit Dusklight", - .bodyRml = "Unsaved progress will be lost.", + .title = "[QUIT_DUSKLIGHT]", + .bodyRml = "[UNSAVED_PROGRESS_WILL_BE_LOST]", .actions = { ModalAction{ - .label = "Cancel", + .label = "[CANCEL]", .onPressed = [dismiss](Modal& modal) { mDoAud_seStartMenu(kSoundWindowClose); @@ -114,7 +113,7 @@ MenuBar::MenuBar() : Document(kDocumentSource), mRoot(mDocument->GetElementById( }, }, ModalAction{ - .label = "Quit", + .label = "[QUIT]", .onPressed = [dismiss](Modal& modal) { mDoAud_seStartMenu(kSoundClick); @@ -129,7 +128,7 @@ MenuBar::MenuBar() : Document(kDocumentSource), mRoot(mDocument->GetElementById( }); if (getSettings().game.speedrunMode) { - mTabBar->add_tab("Reset Timer", [this] { + mTabBar->add_tab("[RESET_TIMER]", [this] { mTabBar->set_active_tab(-1); mDoAud_seStartMenu(kSoundClick); m_speedrunInfo.reset(); diff --git a/src/dusk/ui/modal.cpp b/src/dusk/ui/modal.cpp index b08a333163..3f0d302e36 100644 --- a/src/dusk/ui/modal.cpp +++ b/src/dusk/ui/modal.cpp @@ -12,7 +12,8 @@ Modal::Modal(Props props) : WindowSmall("modal", "modal-dialog"), mProps(std::mo auto* title = append(header, "div"); title->SetClass("modal-title", true); - title->SetInnerRML(mProps.title); + // Keep title as plain text so translated tokens cannot accidentally alter layout. + title->SetInnerRML(escape(mProps.title)); if (!mProps.icon.empty()) { auto* icon = append(header, "icon"); diff --git a/src/dusk/ui/overlay.cpp b/src/dusk/ui/overlay.cpp index 69c262720e..e1610e6a3d 100644 --- a/src/dusk/ui/overlay.cpp +++ b/src/dusk/ui/overlay.cpp @@ -103,13 +103,13 @@ Rml::Element* create_controller_warning(Rml::Element* parent) { auto* heading = append(elem, "heading"); auto* title = append(heading, "span"); - title->SetInnerRML("No Device Assigned"); + title->SetInnerRML("[NO_CONTROLLER_ASSIGNED]"); auto* icon = append(heading, "icon"); icon->SetClass("warning", true); auto* message = append(elem, "message"); auto* content = append(message, "span"); - content->SetInnerRML("Configure Port 1 in Settings."); + content->SetInnerRML("[CONFIGURE_CONTROLLER_PORT_1_IN_SETTINGS]"); return elem; } @@ -145,9 +145,9 @@ Rml::String back_button_name() { } #if defined(TARGET_ANDROID) || (defined(__APPLE__) && TARGET_OS_IOS && !TARGET_OS_MACCATALYST) -constexpr auto kMenuNotificationPrefix = "3-finger tap or"; +constexpr auto kMenuNotificationPrefix = "[THREE_FINGER_TAP_OR]"; #else -constexpr auto kMenuNotificationPrefix = "Press F1 or"; +constexpr auto kMenuNotificationPrefix = "[PRESS_F1_OR]"; #endif Rml::Element* create_menu_notification(Rml::Element* parent) { @@ -169,8 +169,8 @@ Rml::Element* create_menu_notification(Rml::Element* parent) { append(row, "span")->SetInnerRML(kMenuNotificationPrefix); auto* icon = append(row, "icon"); icon->SetClass("controller", true); - append(row, "span")->SetInnerRML("" + escape(padButton) + ""); - append(row, "span")->SetInnerRML("to open menu"); + append(row, "span")->SetInnerRML(escape(padButton)); + append(row, "span")->SetInnerRML("[TO_OPEN_MENU]"); return elem; } @@ -301,10 +301,11 @@ void Overlay::update() { if (getSettings().game.speedrunMode && getSettings().game.liveSplitEnabled) { dusk::speedrun::updateLiveSplit(); if (dusk::speedrun::consumeConnectedEvent()) { - push_toast({.title = "LiveSplit connected", .duration = std::chrono::seconds(3)}); + push_toast({.title = "[LIVESPLIT_CONNECTED]", .duration = std::chrono::seconds(3)}); } if (dusk::speedrun::consumeDisconnectedEvent()) { - push_toast({.title = "LiveSplit disconnected", .duration = std::chrono::seconds(3)}); + push_toast( + {.title = "[LIVESPLIT_DISCONNECTED]", .duration = std::chrono::seconds(3)}); } } #endif @@ -354,9 +355,8 @@ void Overlay::update() { } } - u32 count = 0; const bool showControllerWarning = PADGetIndexForPort(PAD_CHAN0) < 0 && - PADGetKeyButtonBindings(PAD_CHAN0, &count) == nullptr && + PADGetKeyButtonBindings(PAD_CHAN0, nullptr) == nullptr && dynamic_cast(top_document()) == nullptr && dynamic_cast(top_document()) == nullptr; if (showControllerWarning && mControllerWarning == nullptr) { diff --git a/src/dusk/ui/prelaunch.cpp b/src/dusk/ui/prelaunch.cpp index ef6b6e5709..d3be7ede29 100644 --- a/src/dusk/ui/prelaunch.cpp +++ b/src/dusk/ui/prelaunch.cpp @@ -137,7 +137,7 @@ struct UpdateCheckTask { } catch (...) { result = { .status = update_check::Status::Failed, - .message = "Update check failed with an unknown exception", + .message = "[UPDATE_CHECK_FAILED_WITH_AN_UNKNOWN_EXCEPTION]", }; } done.store(true, std::memory_order_release); @@ -366,7 +366,7 @@ class DiscVerificationModal : public WindowSmall { auto* title = append(header, "div"); title->SetClass("modal-title", true); - title->SetInnerRML("Verifying disc image"); + title->SetInnerRML("[DISC_VERIFYING_TITLE]"); auto* icon = append(header, "icon"); icon->SetClass("verifying", true); @@ -439,7 +439,7 @@ class DiscVerificationModal : public WindowSmall { mCancelRequested = true; sDiscVerificationTask->status.shouldCancel.store(true, std::memory_order_relaxed); if (mCancelButton != nullptr) { - mCancelButton->set_text("Cancelling..."); + mCancelButton->set_text("[ACTION_CANCELLING]"); mCancelButton->set_disabled(true); } } @@ -471,7 +471,7 @@ class DiscVerificationModal : public WindowSmall { mProgress->SetAttribute("value", 0.f); } if (mDetail != nullptr) { - mDetail->SetInnerRML("Opening disc image..."); + mDetail->SetInnerRML("[DISC_OPENING]"); } return; } @@ -578,7 +578,7 @@ void try_push_verification_modal(Document& host) { if (!state.pendingDiscPath.empty()) { const Rml::String bodyRml = - state.errorString + "

You may proceed at your own risk."; + state.errorString + "[YOU_MAY_PROCEED_AT_YOUR_OWN_RISK]"; auto acceptHashMismatch = [](Modal& modal) { auto& st = prelaunch_state(); std::string path = std::move(st.pendingDiscPath); @@ -593,16 +593,16 @@ void try_push_verification_modal(Document& host) { modal.pop(); }; host.push(std::make_unique(Modal::Props{ - .title = "Disc verification warning", + .title = "[DISC_VERIFICATION_WARNING]", .bodyRml = bodyRml, .actions = { ModalAction{ - .label = "Cancel", + .label = "[CANCEL]", .onPressed = dismiss, }, ModalAction{ - .label = "Continue anyway", + .label = "[CONTINUE_ANYWAY]", .onPressed = acceptHashMismatch, }, }, @@ -614,12 +614,12 @@ void try_push_verification_modal(Document& host) { } host.push(std::make_unique(Modal::Props{ - .title = "Disc verification error", + .title = "[DISC_VERIFICATION_ERROR]", .bodyRml = state.errorString, .actions = { ModalAction{ - .label = "OK", + .label = "[OK]", .onPressed = dismiss, }, }, @@ -691,8 +691,8 @@ Prelaunch::Prelaunch() : Document(kDocumentSource), mRoot(mDocument->GetElementB if (auto* menuList = mDocument->GetElementById("menu-list")) { auto& state = prelaunch_state(); const bool activeDiscLoaded = !state.activeDiscPath.empty(); - mMenuButtons.push_back( - std::make_unique