From 8c129ddf1e170a16882b03c677f71fb379de5e69 Mon Sep 17 00:00:00 2001 From: Candid Dauth Date: Thu, 9 Jan 2025 22:17:49 +0300 Subject: [PATCH] Internationalize more components (#49) --- .../app.md => assets/appInstructions/en.md | 0 assets/i18n/en.json | 59 +++++++++++++ src/config.ts | 86 +++++++++---------- src/services/i18n.ts | 12 ++- src/ui/compose/song-player-toolbar.vue | 21 +++-- src/ui/compose/song-player.vue | 15 ++-- src/ui/help/help.vue | 29 ++++--- src/ui/pattern-list-filter.vue | 2 +- src/ui/pattern-player/pattern-player.vue | 4 +- src/ui/pattern-player/stroke-dropdown.vue | 2 +- .../pattern-player/time-signature-picker.vue | 4 +- .../playback-settings-picker.vue | 2 +- 12 files changed, 156 insertions(+), 80 deletions(-) rename src/ui/help/app.md => assets/appInstructions/en.md (100%) diff --git a/src/ui/help/app.md b/assets/appInstructions/en.md similarity index 100% rename from src/ui/help/app.md rename to assets/appInstructions/en.md diff --git a/assets/i18n/en.json b/assets/i18n/en.json index 9390da0d..930faadc 100644 --- a/assets/i18n/en.json +++ b/assets/i18n/en.json @@ -104,8 +104,67 @@ "remove-tooltip": "Remove", "new-song": "New song" }, + "song-player-toolbar": { + "clear-song-title": "Clear song", + "clear-song-message": "Do you really want to clear the current song?", + "clear-song-ok": "Clear", + "tools": "Tools", + "clear-song": "Clear song", + "export-mp3": "Export MP3", + "export-wav": "Export WAV", + "share": "Share", + "import": "Import" + }, + "song-player": { + "all-instruments": "All", + "pick-intruments-tooltip": "Pick instruments", + "remove-tooltip": "Remove", + "all-instruments-drop": "(All)" + }, + "help": { + "user-manual": "User manual", + "report-problem": "Report a problem", + "download": "Download {{appName}}", + "app": "{{appName}} app", + "source-code": "Source code on GitHub", + "language": "Language" + }, "overview": { "listen": "Listen", "compose": "Compose" + }, + "config": { + "instruments-ls": "Low Surdo", + "instruments-ms": "Mid Surdo", + "instruments-hs": "High Surdo", + "instruments-re": "Repi", + "instruments-sn": "Snare", + "instruments-ta": "Tamborim", + "instruments-ag": "Agogô", + "instruments-sh": "Shaker", + "instruments-ot": "Shouting", + "stroke-description-hd": "Slap with hand", + "stroke-description-0": "Damp with hand", + "stroke-description-sil": "Silent stroke", + "stroke-description-fl": "Flare", + "stroke-description-w": "Whippy (tamborim) stick", + "stroke-description-.": "Silent stroke", + "stroke-description-wh": "Whistle", + "stroke-description-wh2": "Long whistle", + "stroke-description-s": "Soft flare", + "time-with-triplets": "{{time}} with triplets", + "time-with-quintuplets": "{{time}} with quintuplets", + "category-all": "All tunes", + "category-common": "Common tunes", + "category-uncommon": "Uncommon tunes", + "category-new": "New tunes", + "category-proposed": "Proposed tunes", + "category-custom": "Custom tunes", + "category-onesurdo": "One Surdo", + "category-easy": "Easy", + "category-medium": "Medium", + "category-tricky": "Tricky", + "category-wester": "Western music", + "category-cultural-appropriation": "Cultural appropriation" } } diff --git a/src/config.ts b/src/config.ts index 6c45825e..406e6177 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,4 +1,5 @@ import * as z from "zod"; +import { getI18n } from "./services/i18n"; const instrumentKeys = ["ls", "ms", "hs", "re", "sn", "ta", "ag", "sh", "ot"] as const; export const instrumentValidator = z.enum(instrumentKeys); @@ -21,7 +22,7 @@ export type Config = { instrumentKeys: Instrument[]; instruments: Record string; /** The strokes that this instrument can play. Defines what options the stroke picker will display. */ strokes: Array; }>; @@ -30,7 +31,7 @@ export type Config = { strokes: Record; /** Optionally defining a tooltip that will describe a particular stroke further. */ - strokesDescription: Partial>; + strokesDescription: Partial string>>; /** Presets for the values of the instrument volume sliders, by preset name. */ volumePresets: Record>; @@ -39,7 +40,7 @@ export type Config = { * The available time signatures. The key is the number of strokes per beat (the number of beats per bar is fixed to 4), the value is * the name of the time measurement as it should be shown in the UI. */ - times: Record; + times: Record string>; /** * The stroke resolution that will be used throughout the app, in number of strokes per beat (the number of beats per bar is fixed to 4). @@ -49,7 +50,7 @@ export type Config = { playTime: number; /** The available tune filter categories mapped to their display name. */ - filterCats: Record; + filterCats: Record string>; /** * The current tune of the year. It will be opened by default when the app is opened. If multiple tunes are specified, one of them will be @@ -70,39 +71,39 @@ const config: Config = { instruments: { ls: { - name: "Low Surdo", + name: () => getI18n().t("config.instruments-ls"), strokes: [ "X", "0", "s", "t", "r" ] }, ms: { - name: "Mid Surdo", + name: () => getI18n().t("config.instruments-ms"), strokes: [ "X", "0", "s", "t", "r" ] }, hs: { - name: "High Surdo", + name: () => getI18n().t("config.instruments-hs"), strokes: [ "X", "0", "s", "t", "r" ] }, re: { - name: "Repi", + name: () => getI18n().t("config.instruments-re"), strokes: [ "X", "f", "r", "h", ".", "z", "s" ] }, sn: { - name: "Snare", + name: () => getI18n().t("config.instruments-sn"), strokes: [ ".", "X", "r", "f" ] }, ta: { - name: "Tamborim", + name: () => getI18n().t("config.instruments-ta"), strokes: [ "X", "r", "f" ] }, ag: { - name: "Agogô", + name: () => getI18n().t("config.instruments-ag"), strokes: [ "o", "a", "r", "." ] }, sh: { - name: "Shaker", + name: () => getI18n().t("config.instruments-sh"), strokes: [ "X", "." ] }, ot: { - name: "Shouting", + name: () => getI18n().t("config.instruments-ot"), strokes: [ "w", "y", "A", "B", "D", "E", "F", "G", "J", "K", "L", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "Y", "Z", "9", "8", "7", "6", "5", "b", "c", "d", "e", "g", "q", "j", "k", "m", "n", "u", "v", "x", "i", "l", "p", "$", "%", "&", "'", "(", ")", "*", ",", "-", "?", ":", ";", "<", "=", ">", "K", "[", "\\", "^", "_", "`", "{", "|", "}", "~", "À", "Á", "Â", "Ã", "Ä", "Å", "Æ", "Ç", "È", "É", "Ê", "Ë", "Ì", "Í", "Î", "Ï", "İ", "Ǐ", "Ī", "Ĩ", "Į", "IJ", "Ð", "Ñ", "Ò", "Ó", "Ô" ] } }, @@ -227,16 +228,15 @@ const config: Config = { }, strokesDescription: { - "h": "Hand", - "i": "Slap with hand", - "0": "Damp with hand", - "s": "Silent stroke", - "f": "Flare", - "t": "Whippy (tamborim) stick", - ".": "Silent stroke", - "w" : "Whistle", - "y" : "Long whistle", - "z": "Soft flare" + "h": () => getI18n().t("config.stroke-description-hd"), + "0": () => getI18n().t("config.stroke-description-0"), + "s": () => getI18n().t("config.stroke-description-sil"), + "f": () => getI18n().t("config.stroke-description-fl"), + "t": () => getI18n().t("config.stroke-description-w"), + ".": () => getI18n().t("config.stroke-description-."), + "w" :() => getI18n().t("config.stroke-description-wh"), + "y" :() => getI18n().t("config.stroke-description-wh2"), + "z": () => getI18n().t("config.stroke-description-s") }, volumePresets: { @@ -265,32 +265,32 @@ const config: Config = { }, times: { - 2: "2⁄4", - 3: "6⁄8", - 4: "4⁄4", - 5: "5⁄8", - 6: "3⁄4", - 8: "8⁄8", - 12: "4⁄4 with triplets", - 20: "4⁄4 with quintuplets" + 2: () => "2⁄4", + 3: () => "6⁄8", + 4: () => "4⁄4", + 5: () => "5⁄8", + 6: () => "3⁄4", + 8: () => "8⁄8", + 12: () => getI18n().t("config.time-with-triplets", { time: "4⁄4" }), + 20: () => getI18n().t("config.time-with-quintuplets", { time: "4⁄4" }) }, // Time measurement that is used for beatbox.js. Should be able to represent all the time measurements above playTime: 120, filterCats: { - all: "All tunes", - common: "Common tunes", - uncommon: "Uncommon tunes", - new: "New tunes", - proposed: "Proposed tunes", - custom: "Custom tunes", - onesurdo: "One Surdo", - easy: "Easy", - medium: "Medium", - tricky: "Tricky", - western: "Western music", - "cultural-appropriation": "Cultural appropriation" + all: () => getI18n().t("config.category-all"), + common: () => getI18n().t("config.category-common"), + uncommon: () => getI18n().t("config.category-uncommon"), + new: () => getI18n().t("config.category-new"), + proposed: () => getI18n().t("config.category-proposed"), + custom: () => getI18n().t("config.category-custom"), + onesurdo: () => getI18n().t("config.category-onesurdo"), + easy: () => getI18n().t("config.category-easy"), + medium: () => getI18n().t("config.category-medium"), + tricky: () => getI18n().t("config.category-tricky"), + western: () => getI18n().t("config.category-western"), + "cultural-appropriation": () => getI18n().t("config.category-cultural-appropriation") }, tuneOfTheYear: "The Roof Is on Fire", diff --git a/src/services/i18n.ts b/src/services/i18n.ts index c857bfbf..299a23bf 100644 --- a/src/services/i18n.ts +++ b/src/services/i18n.ts @@ -42,12 +42,18 @@ i18n.init({ }); const TUNE_DESCRIPTIONS_NS = "tune-descriptions"; - for (const [filename, module] of Object.entries(import.meta.glob('../../assets/tuneDescriptions/*/*.md', { eager: true }))) { const m = filename.match(/([^/\\]+)[/\\]([^/\\]+)\.md/)!; i18n.addResource(m[2], TUNE_DESCRIPTIONS_NS, m[1], (module as any).html); } +const APP_INSTRUCTIONS_NS = "app-instructions"; +const APP_INSTRUCTIONS_KEY = "app-instructions"; +for (const [filename, module] of Object.entries(import.meta.glob('../../assets/appInstructions/*.md', { eager: true }))) { + const m = filename.match(/([^/\\]+)\.md/)!; + i18n.addResource(m[1], APP_INSTRUCTIONS_NS, APP_INSTRUCTIONS_KEY, (module as any).html); +} + const i18nResourceChangeCounter = ref(0); const onI18nResourceChange = () => { i18nResourceChangeCounter.value++; @@ -92,4 +98,8 @@ export function useI18n(): ReturnType { export function getTuneDescriptionHtml(tuneName: string): string { return i18n.t(tuneName, { ns: TUNE_DESCRIPTIONS_NS, defaultValue: "" }); +} + +export function getAppInstructionsHtml(): string { + return i18n.t(APP_INSTRUCTIONS_KEY, { ns: APP_INSTRUCTIONS_NS }); } \ No newline at end of file diff --git a/src/ui/compose/song-player-toolbar.vue b/src/ui/compose/song-player-toolbar.vue index c51889d7..7dba5ad9 100644 --- a/src/ui/compose/song-player-toolbar.vue +++ b/src/ui/compose/song-player-toolbar.vue @@ -11,6 +11,7 @@ import SongPicker from "./song-picker.vue"; import PlayPauseStopButton from "../play-pause-stop-button.vue"; import { download, ExportType } from "../utils/export"; + import { useI18n } from "../../services/i18n"; const props = defineProps<{ player: BeatboxReference; @@ -21,6 +22,8 @@ "update:songIdx": [songIdx: number]; }>(); + const i18n = useI18n(); + const state = injectStateRequired(); const songIdx = computed({ @@ -37,10 +40,10 @@ const handleClearSong = async () => { if(await showConfirm({ - title: "Clear song", - message: "Do you really want to clear the current song?", + title: i18n.t("song-player-toolbar.clear-song-title"), + message: i18n.t("song-player-toolbar.clear-song-message"), variant: "danger", - okLabel: "Clear" + okLabel: i18n.t("song-player-toolbar.clear-song-ok") })) { clearSong(song.value); } @@ -66,14 +69,14 @@ diff --git a/src/ui/compose/song-player.vue b/src/ui/compose/song-player.vue index 4f304b28..6dbb5151 100644 --- a/src/ui/compose/song-player.vue +++ b/src/ui/compose/song-player.vue @@ -16,6 +16,7 @@ import vTooltip from "../utils/tooltip"; import { useRefWithOverride } from "../../utils"; import AbstractPlayer, { PositionData } from "../utils/abstract-player.vue"; + import { useI18n } from "../../services/i18n"; type DragOver = "trash" | { instr?: Instrument; idx: number }; @@ -31,6 +32,8 @@ "update:isDraggingPattern": [isDraggingPattern: boolean]; }>(); + const i18n = useI18n(); + const state = injectStateRequired(); const songIdx = computed({ @@ -325,9 +328,9 @@
- {{config.instruments[instrumentKey].name}} + {{config.instruments[instrumentKey].name()}}
-
All
+
{{i18n.t("song-player.all-instruments")}}
@@ -366,7 +369,7 @@ - + @@ -395,7 +398,7 @@ @dragleave="handleDragLeave($event, { idx: i-1 })" @drop="handleDrop($event)" > - (All) + {{i18n.t("song-player.all-instruments-drop")}}
diff --git a/src/ui/help/help.vue b/src/ui/help/help.vue index 044480a6..7feac2bc 100644 --- a/src/ui/help/help.vue +++ b/src/ui/help/help.vue @@ -6,11 +6,10 @@