diff options
author | metamuffin <metamuffin@disroot.org> | 2024-09-17 21:33:08 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2024-09-17 21:33:08 +0200 |
commit | 976e2fc3e25bfd08c755aa222a45c42a53b0b8e0 (patch) | |
tree | e73b304827d149770a94b533569b08aa35ac4cad | |
parent | 666adbf669afc06d87aa28f1a8ca120c5612d3a4 (diff) | |
download | hurrycurry-976e2fc3e25bfd08c755aa222a45c42a53b0b8e0.tar hurrycurry-976e2fc3e25bfd08c755aa222a45c42a53b0b8e0.tar.bz2 hurrycurry-976e2fc3e25bfd08c755aa222a45c42a53b0b8e0.tar.zst |
tc: localization
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | client/.gitignore | 1 | ||||
-rw-r--r-- | client/makefile | 2 | ||||
-rw-r--r-- | locale/en.ini | 1 | ||||
-rw-r--r-- | locale/tools/src/main.rs | 50 | ||||
-rw-r--r-- | test-client/.gitignore | 2 | ||||
-rw-r--r-- | test-client/locale.ts | 14 | ||||
-rw-r--r-- | test-client/main.ts | 4 | ||||
-rw-r--r-- | test-client/makefile | 13 | ||||
-rw-r--r-- | test-client/protocol.ts | 1 | ||||
-rw-r--r-- | test-client/visual.ts | 30 |
11 files changed, 95 insertions, 25 deletions
@@ -1,5 +1,3 @@ /target -/test-client/*.js /specs/*.html -/client/menu/book/book_*.webp* *~ diff --git a/client/.gitignore b/client/.gitignore index 3f695f45..a1c6d43d 100644 --- a/client/.gitignore +++ b/client/.gitignore @@ -1,2 +1,3 @@ .godot /po +/menu/book/book_*.webp* diff --git a/client/makefile b/client/makefile index 085ed748..c9a58f04 100644 --- a/client/makefile +++ b/client/makefile @@ -35,6 +35,6 @@ po/locales.csv: $(LT) @mkdir -p po $(LT) export-godot-csv ../locale $@ -po/%.po: ../locale/%.ini $(LT) +po/%.po: ../locale/%.ini $(LT) ../locale/en.ini @mkdir -p po $(LT) export-po $< $@ --fallback ../locale/en.ini diff --git a/locale/en.ini b/locale/en.ini index f570818a..155f8f6c 100644 --- a/locale/en.ini +++ b/locale/en.ini @@ -60,6 +60,7 @@ c.score.good=Good service c.score.points_par=You collected %s points c.score.points=Points c.score.poor=Poor service +c.score.time_remaining=Time remaining c.settings.apply=Save & Apply c.settings.audio.master_volume=Master Volume c.settings.audio.music_volume=Music Volume diff --git a/locale/tools/src/main.rs b/locale/tools/src/main.rs index 19f58b39..56b81f86 100644 --- a/locale/tools/src/main.rs +++ b/locale/tools/src/main.rs @@ -20,6 +20,12 @@ enum Args { input: PathBuf, output: PathBuf, }, + ExportJson { + #[arg(long)] + fallback: Option<PathBuf>, + input: PathBuf, + output: PathBuf, + }, ExportGodotCsv { input_dir: PathBuf, output: PathBuf, @@ -52,32 +58,44 @@ static NATIVE_LANGUAGE_NAMES: &[(&str, &str)] = &[ ("pt", "Português"), ]; +fn export_load(input: &Path, fallback: Option<PathBuf>) -> Result<BTreeMap<String, String>> { + let mut ini = load_ini(&input)?; + if let Some(fallback) = fallback { + let f = load_ini(&fallback)?; + for (k, v) in f { + #[allow(clippy::map_entry)] + if !ini.contains_key(&k) { + eprintln!("fallback: key {k:?} is missing"); + ini.insert(k, v); + } + } + } + for &(code, name) in NATIVE_LANGUAGE_NAMES { + ini.insert(format!("c.settings.ui.language.{code}"), name.to_owned()); + } + Ok(ini) +} + fn main() -> Result<()> { let args = Args::parse(); match args { + Args::ExportJson { + fallback, + input, + output, + } => { + let ini = export_load(&input, fallback)?; + File::create(output)?.write_all(serde_json::to_string(&ini)?.as_bytes())?; + Ok(()) + } Args::ExportPo { remap_ids: id_map, input, output, fallback, } => { - let mut ini = load_ini(&input)?; + let ini = export_load(&input, fallback)?; let id_map = id_map.map(|path| load_ini(&path)).transpose()?; - if let Some(fallback) = fallback { - let f = load_ini(&fallback)?; - for (k, v) in f { - #[allow(clippy::map_entry)] - if !ini.contains_key(&k) { - eprintln!("fallback: key {k:?} is missing"); - ini.insert(k, v); - } - } - } - - for &(code, name) in NATIVE_LANGUAGE_NAMES { - ini.insert(format!("c.settings.ui.language.{code}"), name.to_owned()); - } - File::create(output)?.write_all( format!( r#" diff --git a/test-client/.gitignore b/test-client/.gitignore new file mode 100644 index 00000000..27b9b44d --- /dev/null +++ b/test-client/.gitignore @@ -0,0 +1,2 @@ +/locale +/*.js diff --git a/test-client/locale.ts b/test-client/locale.ts new file mode 100644 index 00000000..61d083b4 --- /dev/null +++ b/test-client/locale.ts @@ -0,0 +1,14 @@ + +let TR: { [key: string]: string } = {} +export async function init_locale(lang: string) { + const res = await fetch(`/locale/${encodeURIComponent(lang)}.json`, { headers: { "Accept": "application/json" } }) + if (!res.ok) throw new Error("language pack download failed"); + TR = await res.json() +} + +export function tr(key: string, ...args: string[]): string { + let s = TR[key] ?? key; + if (args.length) + s = s.replace(/%(s|i)/ig, () => args.shift() ?? "[not provided]") + return s +} diff --git a/test-client/main.ts b/test-client/main.ts index 875e420d..f48aef17 100644 --- a/test-client/main.ts +++ b/test-client/main.ts @@ -17,6 +17,7 @@ */ /// <reference lib="dom" /> +import { init_locale } from "./locale.ts"; import { MovementBase, collide_player_player, update_movement } from "./movement.ts"; import { Gamedata, ItemIndex, ItemLocation, Message, MessageTimeout, PacketC, PacketS, PlayerID, TileIndex } from "./protocol.ts"; import { V2, lerp_exp_v2_mut, normalize, lerp_exp } from "./util.ts"; @@ -35,7 +36,8 @@ const HANDLED_KEYS = [KEY_BOOST, KEY_CHAT, KEY_CLOSE, KEY_DOWN, KEY_UP, KEY_LEFT export let ctx: CanvasRenderingContext2D; export let canvas: HTMLCanvasElement; let ws: WebSocket; -document.addEventListener("DOMContentLoaded", () => { +document.addEventListener("DOMContentLoaded", async () => { + await init_locale(navigator.language.split("-")[0] ?? "en") const ws_uri = window.location.protocol.endsWith("s:") ? `wss://${window.location.host}/` : `ws://${window.location.hostname}:27032/` diff --git a/test-client/makefile b/test-client/makefile index 2a83f5d0..72a04791 100644 --- a/test-client/makefile +++ b/test-client/makefile @@ -14,9 +14,20 @@ # along with this program. If not, see <https://www.gnu.org/licenses/>. # .PHONY: all clean -all: main.js +all: main.js locales +locales: $(patsubst ../locale/%.ini,locale/%.json,$(wildcard ../locale/*.ini)) clean: rm main.js + rm -rf locale main.js: main.ts $(wildcard *.ts) esbuild $< --bundle --outfile=$@ --target=esnext --format=esm + +LT = ../target/release/localetool + +$(LT): $(shell find ../locale/tools -type f) + { cd ..; cargo build --release --bin localetool; } + +locale/%.json: ../locale/%.ini $(LT) ../locale/en.ini + @mkdir -p locale + $(LT) export-json $< $@ --fallback ../locale/en.ini diff --git a/test-client/protocol.ts b/test-client/protocol.ts index d0f5f997..4d243702 100644 --- a/test-client/protocol.ts +++ b/test-client/protocol.ts @@ -90,6 +90,7 @@ export type Message = { item: number } | { text: string } | { effect: string } + | { translation: { id: string, params: Message[] } } export type ItemLocation = { player: PlayerID } diff --git a/test-client/visual.ts b/test-client/visual.ts index 09b6ec85..863fea41 100644 --- a/test-client/visual.ts +++ b/test-client/visual.ts @@ -15,6 +15,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ +import { tr } from "./locale.ts"; import { ItemData, MessageData, PlayerData, TileData, camera, camera_scale, canvas, ctx, data, demands_completed, demands_failed, get_interact_target, global_message, interact_active_anim, interact_possible_anim, interact_target_anim, items_removed, keys_down, my_id, nametag_scale_anim, players, points, server_hints, tiles, time_remaining } from "./main.ts"; import { PLAYER_SIZE } from "./movement.ts"; import { draw_item_sprite, draw_tile_sprite, ItemName, TileName } from "./tiles.ts"; @@ -91,12 +92,12 @@ export function draw_ingame() { ctx.textBaseline = "bottom" ctx.font = "20px sans-serif" if (time_remaining != undefined) - ctx.fillText(`Time remaining: ${time_remaining?.toFixed(2)}`, 10, canvas.height - 90) + ctx.fillText(`${tr("c.score.time_remaining")}: ${time_remaining?.toFixed(2)}`, 10, canvas.height - 90) ctx.font = "30px sans-serif" - ctx.fillText(`Points: ${points}`, 10, canvas.height - 60) + ctx.fillText(`${tr("c.score.points")}: ${points}`, 10, canvas.height - 60) ctx.font = "20px sans-serif" - ctx.fillText(`Completed: ${demands_completed}`, 10, canvas.height - 30) - ctx.fillText(`Failed: ${demands_failed}`, 10, canvas.height - 10) + ctx.fillText(`${tr("c.score.completed")}: ${demands_completed}`, 10, canvas.height - 30) + ctx.fillText(`${tr("c.score.failed")}: ${demands_failed}`, 10, canvas.height - 10) if (keys_down.has("KeyP")) { draw_debug() @@ -258,6 +259,27 @@ function draw_message(m: MessageData) { ctx.translate(0, 1) } + if ("translation" in m.inner) { + ctx.translate(0, -1) + + ctx.textAlign = "center" + ctx.font = "15px sans-serif" + ctx.scale(2 / camera_scale, 2 / camera_scale) + + const text = tr(m.inner.translation.id, ...m.inner.translation.params.map(p => "text" in p ? p.text : "[not text]")); + const w = ctx.measureText(text).width + 30 + + ctx.fillStyle = "#fffa" + ctx.beginPath() + ctx.roundRect(-w / 2, -15, w, 30, 5) + ctx.fill() + + ctx.fillStyle = "black" + ctx.textBaseline = "middle" + ctx.fillText(text, 0, 0) + + ctx.translate(0, 1) + } ctx.restore() } |