b1acc5b5b3
* Add German, Portuguese, and Indonesian translations; fix i18n coverage gaps Extends the existing English/Polish/Japanese/Simplified Chinese i18n system to seven languages total. Also fixes several pre-existing i18n coverage gaps found while auditing: the recent-tracks list, search placeholder, and trash empty-state were hardcoding English text instead of using the translation system; a presence-panel legend lacked data-i18n attributes; upload/job/ playlist error toasts were untranslated; and library list content did not refresh on a live language switch. Widened the settings dropdown to fit the longest new language name and the device-select to stop truncating longer translated values. Bumps the Unraid template pin to 0.13.0. * Remove unused plural import in job.js Flagged in PR review: job.js imports plural from i18n.js but never calls it, only t(). * Native-speaker QA pass on all translations Fixes real mistranslations (German "schleifen" for loop, "Skala" for musical scale, Indonesian countdown/count-in mixup, Chinese Alpha badge), grammar bugs (Polish aria-labels requiring an unavailable grammatical case, singular/plural adjective agreement in playlist skip messages), inconsistent terminology within each language, and a stray three-dot ellipsis instead of the single character used everywhere else. Converts playlist.skip.* from t() to plural() with proper singular/plural forms across all seven languages, since Portuguese and Polish adjectives don't inflect correctly as flat strings. --------- Co-authored-by: Thales <>
75 lines
3.0 KiB
JavaScript
75 lines
3.0 KiB
JavaScript
import { t, TRANSLATIONS } from "./i18n.js";
|
|
|
|
// Fallback list used before /api/config responds. Kept in sync with
|
|
// STEM_NAMES in app/core/config.py — the API is the canonical source.
|
|
export let STEM_NAMES = ["vocals", "drums", "bass", "guitar", "piano", "other"];
|
|
export let TRACK_NAMES = ["original", ...STEM_NAMES];
|
|
|
|
// Stems from the on-demand lead/backing vocal split (#275, EXTRA_STEM_NAMES
|
|
// in app/core/config.py). Not folded into STEM_NAMES/TRACK_NAMES -- most jobs
|
|
// never run this split -- but the player/mixer render them as real lanes,
|
|
// swapped in for "vocals", via effectiveStemOrder() below.
|
|
export let EXTRA_STEM_NAMES = ["lead_vocals", "backing_vocals"];
|
|
|
|
export async function syncStemNamesFromAPI() {
|
|
try {
|
|
const res = await fetch("/api/config");
|
|
if (!res.ok) return;
|
|
const data = await res.json();
|
|
if (Array.isArray(data.stem_names) && data.stem_names.length > 0) {
|
|
STEM_NAMES = data.stem_names;
|
|
TRACK_NAMES = ["original", ...STEM_NAMES];
|
|
}
|
|
if (Array.isArray(data.extra_stem_names) && data.extra_stem_names.length > 0) {
|
|
EXTRA_STEM_NAMES = data.extra_stem_names;
|
|
}
|
|
} catch (e) {
|
|
console.warn("[constants] failed to sync stem names from API:", e);
|
|
}
|
|
}
|
|
|
|
// The lane order for a specific job: STEM_NAMES with "vocals" replaced by
|
|
// lead_vocals + backing_vocals when a job's on-demand split (#275) has
|
|
// produced both. `presentNames` is the Set of stem names the job actually
|
|
// has (typically from job.stems). Order matters -- callers use this both to
|
|
// decide what to render and in what sequence (waveform stacking, mixer rows).
|
|
export function effectiveStemOrder(presentNames) {
|
|
const splitDone = presentNames.has("lead_vocals") && presentNames.has("backing_vocals");
|
|
return STEM_NAMES.flatMap((n) => (n === "vocals" && splitDone ? EXTRA_STEM_NAMES : [n]));
|
|
}
|
|
|
|
// A Proxy, not a plain object, so every lookup resolves through the CURRENT
|
|
// language live -- a plain object would freeze these labels in whatever
|
|
// language was active the moment this module first loaded. Callers keep their
|
|
// existing `STEM_DISPLAY[name] || name` fallback pattern unchanged: an
|
|
// unrecognized name (not one of ours) still yields undefined, same as a plain
|
|
// object would, rather than the i18n engine's own missing-key fallback
|
|
// (which returns the raw dictionary key string -- wrong here).
|
|
export const STEM_DISPLAY = new Proxy({}, {
|
|
get(_target, prop) {
|
|
if (typeof prop !== "string") return undefined;
|
|
const key = `stem.${prop}`;
|
|
return key in TRANSLATIONS.en ? t(key) : undefined;
|
|
},
|
|
});
|
|
|
|
// FL Studio-style channel palette: saturated but slightly dusty, designed
|
|
// to read well on a dark background.
|
|
export const STEM_COLORS = {
|
|
vocals: "#e85f6f",
|
|
drums: "#e89048",
|
|
bass: "#e8b848",
|
|
guitar: "#88d878",
|
|
piano: "#b88fe8",
|
|
other: "#88a8c8",
|
|
original: "#a8b0bd",
|
|
lead_vocals: "#e8748a",
|
|
backing_vocals: "#c98fe0",
|
|
};
|
|
|
|
export const PROGRESS_COLOR = "#3a3a3a";
|
|
|
|
export const LOOP_DEFAULT_START_FRAC = 0.25;
|
|
export const LOOP_DEFAULT_END_FRAC = 0.5;
|
|
|
|
export const LANE_VOLUME_MAX = 2; |