Feature request
- A language switch in the UI β a flag icon/button (π¬π§ β π΅π±) β that puts the app into Polish mode: typed
queries and mic dictation are interpreted as Polish, and the answer comes back in Polish.
- Several Polish-speaking colleagues are willing to record voice samples for a Polish eval/training set,
the same way the existing English speakers did (eval/RECORDING.md, eval/audio/{km,lg,mn}/).
- An LLM could plausibly help draft the Polish sentences needed for such a set, rather than hand-writing
all of them.
- Underlying question: what accuracy can we actually expect from the models on Polish, and what has
to change in the app to support it end-to-end?
This issue is a feasibility investigation answering that question, not an implementation β it audits every
pipeline stage this repo already has (ASR worker, deterministic NLU matcher, alias tables, NLG templates,
eval harness, model hosting) with file:line citations, adds external research on Whisper/LLM/TTS Polish
performance, and proposes a scoped path forward. Per this repo's own convention for research/spike issues,
whoever picks this up should land measured findings in a new docs/polish-language-feasibility.md before
closing it β this issue is the proposal, not the final report.
Why this is a natural next step, not a stretch
docs/model-hosting-cyfronet.md:42-43 already justifies the Cyfronet S3 mirror partly on "Cyfronet is a
Polish/EU host... for the project's actual user base" β the intended users already skew Polish.
- The
whisper-small checkpoint already mirrored in production (onnx-community/whisper-small, per
docs/model-hosting-cyfronet.md:9-13) is OpenAI's standard multilingual checkpoint (not the
.en-suffixed English-only variant) β it already covers ~99 languages, including Polish. No new model
needs downloading or mirroring just to attempt Polish ASR.
- Piper β already the planned TTS engine per
docs/design.md ("NLG + TTS: templated sentence β
SpeechSynthesis / Piper") β has real, working Polish voice models today.
None of this makes Polish support free β the NLU and NLG layers are the real cost, detailed below β but
the ASR and TTS legs are cheaper than they might look at first glance.
What performance can we actually expect?
Short answer: generic Polish speech recognition is in a good tier; domain accuracy (the number that
actually matters here) is presently unmeasured and cannot be estimated from generic numbers alone.
- ASR, generic speech. Third-party aggregated reporting on Whisper large-v3 (Common Voice 15) puts
Polish in the "high accuracy" tier (WER < 10%), in the same band as German/Portuguese/Dutch, well above
genuinely low-resource languages (Arabic/Hindi/Russian/Turkish β 10β20%, and 25%+ for the long tail). I
could not independently verify this against OpenAI's own primary per-language table β web fetches to
primary sources (arxiv.org, the Whisper GitHub repo, Wikipedia) all returned 403 in this research
session β so treat the <10% figure as directionally indicative, not authoritative, until someone confirms
it against the actual paper. Separately, Mozilla Common Voice's Polish corpus (cv-corpus-25.0,
2026-03) has 188.84h recorded / 176.61h validated from 3,465 speakers β a mid/well-resourced language for
ASR purposes, not a low-resource one.
- ASR, domain jargon β the actual open question. This repo's own numbers show generic WER is a poor
proxy for domain accuracy: English whisper-small needed domain-prompt biasing (+7.6pp raw slot-token
accuracy, docs/voice-pipeline-feasibility.md Β§2.4) and an extended corrector (+9pp more) to go from a
mediocre raw transcript to 89% E2E audioβintent on physics vocabulary (keV/MeV/GeV, per-nucleon, dE/dx,
ASTAR/PSTAR, isotope names, material trade names like Lucite/PMMA). There's no reason to expect Polish
physics vocabulary ("zdolnoΕΔ/moc hamowania" for stopping power, "zasiΔg" for range, element names,
material names) to be any different β expect a similarly large gap between generic Polish WER and domain
E2E accuracy until a Polish domain-prompt string, a Polish corrector, and a Polish eval set exist. This
is exactly the gap the offered recordings would let us measure and close β nobody can quote a
trustworthy domain-accuracy number today because it doesn't exist yet; the honest answer is "unknown but
cheaply measurable," not "X%."
- On-device LLM fallback (if this project keeps one at all β see
docs/voice-pipeline-feasibility.md
Β§5.6, currently scoped as a rare last resort, English-only, unshipped). The models already benchmarked
here (Qwen2.5-0.5B/1.5B, Β§2.5) claim "multilingual support across 29+ languages," but I found no verified
Polish-specific benchmark for them. A better-fitting candidate: Bielik (SpeakLeash + ACK Cyfronet
AGH β trained on the same Cyfronet HPC infrastructure this repo already has model-mirroring access to),
a Polish-native-tuned open LLM family at 1.5B/4.5B/7B/11B. Bielik-11B-v2 scores 58.14 avg on the Open PL
LLM Leaderboard (best under 20B params, +8.75pp over 2nd place); Bielik-11B-v3 scores 65.93 on the same
leaderboard and 71.83% on the Polish Linguistic & Cultural Competency Benchmark. The 1.5B/4.5B variants
are reported competitive with models 2β3Γ their size, but β same caveat as Qwen β nobody has run this
repo's actual constrained-single-token-classification task against them. Needs its own small bench, not
an assumption; low priority given the fallback itself is unshipped for English.
- TTS. Piper has real Polish voice models today (e.g.
pl_PL-darkman-medium,
community-trained pl_PL-jarvis_wg_glos-medium / pl_PL-justyna_wg_glos-medium /
pl_PL-meski_wg_glos-medium / pl_PL-zenski_wg_glos-medium β HF WitoldG/polish_piper_models, also
mirrored e.g. as csukuangfj/vits-piper-pl_PL-zenski_wg_glos-medium). Browser SpeechSynthesis also
works across Chrome/Edge on Windows/macOS, but whether a Polish voice is actually installed depends on
OS/browser state β unverified with a specific voice name here, needs a real-device check before relying
on it. Piper is the lower-risk choice since its Polish weights are known and shippable exactly like the
Whisper weights already are.
- LLM-generated Polish sentences (for building the eval set). High confidence this works well for raw
fluency β Polish is a mid/high-resource language for frontier LLMs, not a low-resource one where
synthetic generation quality degrades. The real risk isn't fluency, it's domain naturalness (would a
Polish medical physicist actually phrase a query that way?) β that needs native-speaker review, i.e.
exactly the friends who offered to help. Treat LLM output as a first draft, not ground truth.
What exists today, and exactly where English is hardcoded
| Stage |
File(s) |
Finding |
| ASR language token |
src/lib/asr/transcribe.ts:229 |
`const languageEnglish = Number(langToId["< |
| ASR domain prompt |
src/lib/asr/transcribe.ts:75-78 |
DOMAIN_PROMPT β the English biasing string ("MeV, keV, GeV, ..., stopping power, Lucite, adipose tissue"). Needs a Polish counterpart; units (MeV/keV) are language-invariant, words like "stopping power" aren't. |
| ASR model |
src/lib/models/manifest.ts:34-41 |
One whisper entry, onnx-community/whisper-small q8 β already the standard multilingual checkpoint. No per-language variant concept in ModelManifestEntry, and none needed for a first pass. |
| NLU matcher |
src/lib/intent/matcher.ts (705 lines, all of it) |
Entirely inline English regex/Set literals, not data-driven β e.g. INDIRECT_IDIOMS (73-102, /\bhow far\b/ β csdaRange), DIRECT_STOPPING/DIRECT_RANGE (104-106), detectInverse() (109-124, `/\b(?:what |
QueryIntent schema |
src/lib/intent/query-intent.ts, src/lib/intent/coverage.ts |
No lang/locale field anywhere in QueryIntent, EvalExample, EVAL_TAGS, or the coverage types. Language would need to be threaded in as new state, not slotted into an existing field. |
| Alias tables |
src/lib/aliases/materials.ts, particles.ts, elements.ts |
Flat alias β id maps (MATERIAL_ALIAS_INDEX, PARTICLE_ALIAS_INDEX). Adding Polish aliases (["wΔgiel", 6], ["woda", 276], a Polish protony particle entry, β¦) fits the existing shape with no structural change to the lookup mechanism itself. Fun accident: elements.ts:163 already has ["wolfram", 74] as an English variant name for tungsten β that's also the standard Polish word for tungsten, a free hit. Caveat: normalize.ts's NFKD stripping (61-70) folds Polish Δ/Ε/Γ³/Ε/Δ
/Δ to their base letters fine, but "Ε" (U+0142) does not NFKD-decompose and survives as a distinct letter β an unaccented "l" typed by a user won't match a stored "Ε" alias without an explicit extra variant. Recommend a locale-scoped index (reusing the same lookup.ts fuzzy-match machinery) rather than merging Polish aliases into the same flat map as English/trade names, to avoid cross-language collisions. |
| NLG templates |
src/lib/nlg/render.ts (216 lines, all of it) |
Hardcoded English template literals throughout, e.g. `The ${QUANTITY_PHRASE[quantity]} of ${energyLabel(...)} ${particle} in ${material} is ${value} (...)`. β English word order and prepositions baked in; no lang parameter on renderAnswer(). The harder problem isn't translation, it's Polish grammatical case: "in water" is "w wodzie" (locative), not "w woda" β a naive string-table swap doesn't handle noun declension. Recommend a v1 that sidesteps this with a labeled/colon-style Polish output ("ZdolnoΕΔ hamowania: 12.3 MeVΒ·cmΒ²/g (materiaΕ: woda, czΔ
stka: proton, energia: 100 MeV)") instead of attempting fully natural declined sentences, deferring a real declension table to a stretch goal. |
| UI strings / i18n scaffolding |
src/routes/*.svelte (3 files), src/lib/components/**/*.svelte (10 files), plus answer-status.svelte.ts, asr-status.svelte.ts, model-status.svelte.ts |
No i18n library anywhere (package.json has exactly one runtime dependency, @huggingface/transformers; no svelte-i18n/paraglide/i18next/etc.). No centralized strings module β every .svelte file inlines its own English text. Roughly 45β55 distinct user-facing strings across all of it (button labels, status messages, the "Sorry, I couldn't understand that..." fallback, mic states "Warming upβ¦"/"Listeningβ¦", download/cache dialogs). src/app.html:2 hardcodes <html lang="en"> β a real fix needed (a11y-relevant), not a false positive. Two earlier locale/translate grep hits were confirmed false positives: DarkModeToggle.svelte:35-36 (translate-x-* Tailwind transform classes) and coverage.ts:220 (localeCompare for sorting tag strings). |
| Eval set |
eval/intents.jsonl, scripts/validate-intents.ts |
No lang field in the EvalExample schema, so the format itself is language-neutral β but validate-intents.ts:17 hardcodes the file path (../eval/intents.jsonl) and line 21 hardcodes MIN_EXAMPLES = 100. A Polish set needs a genuinely separate frozen file (eval/intents.pl.jsonl, matching CLAUDE.md's rule that the existing intents.jsonl is a frozen regression suite not to be touched) plus a parameterized validator or a second script with its own (lower, initially) minimum-count threshold. EVAL_TAGS categories (phrasing/quantity/comparison/unit/ambiguity/special) are language-agnostic and should be reusable as-is; per CLAUDE.md, LLM-drafted/hard Polish examples must be tagged "adversarial", never "stress-test" (reserved for the two existing Β§7 sentences). |
| Model hosting |
docs/model-hosting-cyfronet.md:9-13,25-29, src/lib/models/remote.ts:12 |
Only whisper (the multilingual checkpoint already discussed) is mirrored; qwen/llama aren't yet. Because the mirrored Whisper weights already cover Polish, basic Polish ASR needs zero new model mirroring β the change is code-level (parametrize the `"< |
The one strategic fork worth deciding early
Option A β native Polish pipeline. Polish ASR (task: "transcribe", <|pl|> token) β a Polish
variant of the matcher β Polish-aware alias lookups β Polish NLG. Full fidelity: the user sees their own
Polish question echoed back for confirmation (this repo's whole "trust UX" design depends on that echo,
per docs/voice-pipeline-feasibility.md Β§4/Β§5.4). Highest cost: forking/parametrizing all 705 lines of
matcher.ts, plus the NLG declension problem above.
Option B β Whisper's built-in translate task as a shortcut. Whisper's decoder supports a task
token alongside the language token β "transcribe" (current: Polish audio β Polish text) vs.
"translate" (Polish audio β English text, directly, in one decode pass). If Polish queries are run
through task: "translate" instead, the output could flow through the existing, unmodified English
matcher/aliases/NLG entirely β sidestepping the expensive matcher fork completely. Real tradeoffs, all
unmeasured: (1) translation is a strictly harder task than transcription, so domain-jargon accuracy on
this path is unknown and needs its own benchmark pass, same as the transcribe-task English work needed;
(2) the "echoed" query shown to the user would be in English even though they spoke Polish β plausibly
fine for bilingual physicist users (this project's actual audience), but a real trust/UX regression worth
a deliberate decision, not an accident; (3) it only solves dictation, not the flag-toggle's promise of a
fully Polish-language app β NLG/UI still need Polish localization regardless.
Suggested pragmatic path: a hybrid β use Option B to shortcut the hard NLU parsing step for a v1 (Polish
speech in β Whisper translate β existing English matcher/compute pipeline), while still localizing NLG
output, TTS voice, and UI chrome to Polish (Polish in, Polish out; English only in the invisible middle).
Defer the full Option A matcher fork unless the translate-task's domain accuracy or the trust-UX regression
turns out to be unacceptable once measured. This isn't free either β it needs its own accuracy benchmark β
but it avoids committing to the single largest cost item (the matcher fork) before knowing if it's
necessary.
Data collection plan (ties the two feature-request ideas together)
- Draft ~30 Polish sentences covering the same coverage matrix
eval/RECORDING.md used for English
(direct stopping-power/range queries, indirect phrasing, conversational filler, multi-material/particle/
energy comparisons, program-name comparison, tricky units, isotope names, an inverse query, a material
alias) β an LLM can produce this first draft.
- Native-speaker review pass (the colleagues who offered to help): correct anything that reads as
unnatural or a literal translation rather than how a Polish physicist would actually ask. This is the
step that actually de-risks the "LLM generates sentences" idea β treat model output as a draft, not
ground truth.
- Record sessions mirroring
scripts/record-session.sh (same WAV/sample-rate conventions), 2β3+ speakers,
into a new eval/audio/<speaker>/ set kept separate from the English speakers.
- Hand-label gold
QueryIntents for each sentence into a new eval/intents.pl.jsonl (same schema, new
file β see "Eval set" row above).
- Run the existing pipeline scripts (
asr-transcribe.mjs, asr-score-slots.mjs, e2e-audio-intents.ts)
against the Polish set once the ASR language plumbing exists, to get the first real Polish domain
E2E number β the thing nobody can quote yet.
Smallest useful next step, doable this week: record even 10β15 of these sentences from 1β2 friends,
run them through whisper-small forced to <|pl|> with no Polish domain prompt yet, and get a first
honest raw-Polish-domain-WER baseline β exactly how the English work started, before any tuning existed.
Effort/risk at a glance
| Component |
Relative effort |
Why |
| ASR language plumbing (thread a language param through worker β transcribe) |
Low |
One hardcoded lookup key to parametrize; no new model. |
| Polish domain prompt + corrector |
Medium |
Needs real Polish recordings to tune against β data-bound, not design-bound. |
| NLU matcher |
High |
705 lines of inline English regex/grammar; no data-driven shortcut (Option B above is the way to defer this). |
| Alias tables (materials/particles) |
Low |
Same alias β id shape already supports this; mainly data entry + native-speaker verification. |
| NLG templates |
MediumβHigh |
Polish case declension is a real linguistic problem, not just translation; a labeled/non-declined v1 output sidesteps it. |
| UI strings / i18n scaffolding |
Medium |
No library exists yet; ~45β55 short strings to extract; recommend a minimal messages.ts + a runes-based locale.svelte.ts store (matching the existing *.svelte.ts store convention, e.g. model-status.svelte.ts) over pulling in a full i18n framework for 2 languages. |
| TTS (Piper Polish voice) |
Low |
Voices already exist; mirrors the same model-hosting pattern already used for Whisper. |
| Eval tooling |
LowβMedium |
New file + a parameterized/duplicated validator script. |
| On-device Polish LLM fallback |
Medium, low priority |
Needs its own Bielik-vs-Qwen bench; the fallback itself is unshipped for English too. |
Open questions (need a human decision, not more research)
- Option A vs. B vs. the hybrid above β is showing an English-translated echo of a Polish spoken
query acceptable for v1, or does trust UX require the literal Polish transcript even if it costs the
full matcher fork?
- NLG v1 style β is a labeled/colon-style Polish answer ("materiaΕ: woda") acceptable initially, or
is naturally-declined Polish a hard requirement from day one?
- UI toggle design β a flag-emoji button (π¬π§/π΅π±, as originally suggested) or a switch control matching
the existing DarkModeToggle.svelte pattern? Does switching language also need to persist across
sessions (localStorage, matching how ASR progress calibration already persists)?
- Auto-detect vs. explicit toggle β should the app ever attempt to detect Polish vs. English from
speech automatically, or should the toggle always be an explicit, user-driven choice? (Recommend the
latter for v1 β simpler, and matches how the ASR domain-prompt mechanism already assumes a known,
fixed language per request.)
- How many speakers/sentences is "enough" for a first Polish eval set? The English set used 3
speakers Γ ~30 sentences as its first iteration (eval/RECORDING.md) and grew from there β is that the
right starting scale here too, given however many friends actually volunteer?
- Physicist/native-speaker sign-off, same as
docs/design.md already flags for English isotope/
energy conventions β Polish terminology choices (e.g. "moc hamowania" vs. "zdolnoΕΔ hamowania" for
stopping power) need a real decision from a Polish-speaking physicist, not a model guess.
References
Gathered during this research pass β general/background reading, not load-bearing citations for any
specific number quoted above (see caveats inline where a figure is unverified):
Filed from an investigation session; no code changes made. Scope intentionally left as an open proposal β
see "Open questions" before picking an implementation path.
Feature request
queries and mic dictation are interpreted as Polish, and the answer comes back in Polish.
the same way the existing English speakers did (
eval/RECORDING.md,eval/audio/{km,lg,mn}/).all of them.
to change in the app to support it end-to-end?
This issue is a feasibility investigation answering that question, not an implementation β it audits every
pipeline stage this repo already has (ASR worker, deterministic NLU matcher, alias tables, NLG templates,
eval harness, model hosting) with file:line citations, adds external research on Whisper/LLM/TTS Polish
performance, and proposes a scoped path forward. Per this repo's own convention for research/spike issues,
whoever picks this up should land measured findings in a new
docs/polish-language-feasibility.mdbeforeclosing it β this issue is the proposal, not the final report.
Why this is a natural next step, not a stretch
docs/model-hosting-cyfronet.md:42-43already justifies the Cyfronet S3 mirror partly on "Cyfronet is aPolish/EU host... for the project's actual user base" β the intended users already skew Polish.
whisper-smallcheckpoint already mirrored in production (onnx-community/whisper-small, perdocs/model-hosting-cyfronet.md:9-13) is OpenAI's standard multilingual checkpoint (not the.en-suffixed English-only variant) β it already covers ~99 languages, including Polish. No new modelneeds downloading or mirroring just to attempt Polish ASR.
docs/design.md("NLG + TTS: templated sentence βSpeechSynthesis / Piper") β has real, working Polish voice models today.
None of this makes Polish support free β the NLU and NLG layers are the real cost, detailed below β but
the ASR and TTS legs are cheaper than they might look at first glance.
What performance can we actually expect?
Short answer: generic Polish speech recognition is in a good tier; domain accuracy (the number that
actually matters here) is presently unmeasured and cannot be estimated from generic numbers alone.
Polish in the "high accuracy" tier (WER < 10%), in the same band as German/Portuguese/Dutch, well above
genuinely low-resource languages (Arabic/Hindi/Russian/Turkish β 10β20%, and 25%+ for the long tail). I
could not independently verify this against OpenAI's own primary per-language table β web fetches to
primary sources (arxiv.org, the Whisper GitHub repo, Wikipedia) all returned 403 in this research
session β so treat the <10% figure as directionally indicative, not authoritative, until someone confirms
it against the actual paper. Separately, Mozilla Common Voice's Polish corpus (
cv-corpus-25.0,2026-03) has 188.84h recorded / 176.61h validated from 3,465 speakers β a mid/well-resourced language for
ASR purposes, not a low-resource one.
proxy for domain accuracy: English whisper-small needed domain-prompt biasing (+7.6pp raw slot-token
accuracy,
docs/voice-pipeline-feasibility.mdΒ§2.4) and an extended corrector (+9pp more) to go from amediocre raw transcript to 89% E2E audioβintent on physics vocabulary (keV/MeV/GeV, per-nucleon, dE/dx,
ASTAR/PSTAR, isotope names, material trade names like Lucite/PMMA). There's no reason to expect Polish
physics vocabulary ("zdolnoΕΔ/moc hamowania" for stopping power, "zasiΔg" for range, element names,
material names) to be any different β expect a similarly large gap between generic Polish WER and domain
E2E accuracy until a Polish domain-prompt string, a Polish corrector, and a Polish eval set exist. This
is exactly the gap the offered recordings would let us measure and close β nobody can quote a
trustworthy domain-accuracy number today because it doesn't exist yet; the honest answer is "unknown but
cheaply measurable," not "X%."
docs/voice-pipeline-feasibility.mdΒ§5.6, currently scoped as a rare last resort, English-only, unshipped). The models already benchmarked
here (Qwen2.5-0.5B/1.5B, Β§2.5) claim "multilingual support across 29+ languages," but I found no verified
Polish-specific benchmark for them. A better-fitting candidate: Bielik (SpeakLeash + ACK Cyfronet
AGH β trained on the same Cyfronet HPC infrastructure this repo already has model-mirroring access to),
a Polish-native-tuned open LLM family at 1.5B/4.5B/7B/11B. Bielik-11B-v2 scores 58.14 avg on the Open PL
LLM Leaderboard (best under 20B params, +8.75pp over 2nd place); Bielik-11B-v3 scores 65.93 on the same
leaderboard and 71.83% on the Polish Linguistic & Cultural Competency Benchmark. The 1.5B/4.5B variants
are reported competitive with models 2β3Γ their size, but β same caveat as Qwen β nobody has run this
repo's actual constrained-single-token-classification task against them. Needs its own small bench, not
an assumption; low priority given the fallback itself is unshipped for English.
pl_PL-darkman-medium,community-trained
pl_PL-jarvis_wg_glos-medium/pl_PL-justyna_wg_glos-medium/pl_PL-meski_wg_glos-medium/pl_PL-zenski_wg_glos-mediumβ HFWitoldG/polish_piper_models, alsomirrored e.g. as
csukuangfj/vits-piper-pl_PL-zenski_wg_glos-medium). BrowserSpeechSynthesisalsoworks across Chrome/Edge on Windows/macOS, but whether a Polish voice is actually installed depends on
OS/browser state β unverified with a specific voice name here, needs a real-device check before relying
on it. Piper is the lower-risk choice since its Polish weights are known and shippable exactly like the
Whisper weights already are.
fluency β Polish is a mid/high-resource language for frontier LLMs, not a low-resource one where
synthetic generation quality degrades. The real risk isn't fluency, it's domain naturalness (would a
Polish medical physicist actually phrase a query that way?) β that needs native-speaker review, i.e.
exactly the friends who offered to help. Treat LLM output as a first draft, not ground truth.
What exists today, and exactly where English is hardcoded
src/lib/asr/transcribe.ts:229src/lib/asr/transcribe.ts:75-78DOMAIN_PROMPTβ the English biasing string ("MeV, keV, GeV, ..., stopping power, Lucite, adipose tissue"). Needs a Polish counterpart; units (MeV/keV) are language-invariant, words like "stopping power" aren't.src/lib/models/manifest.ts:34-41whisperentry,onnx-community/whisper-smallq8 β already the standard multilingual checkpoint. No per-language variant concept inModelManifestEntry, and none needed for a first pass.src/lib/intent/matcher.ts(705 lines, all of it)Setliterals, not data-driven β e.g.INDIRECT_IDIOMS(73-102,/\bhow far\b/βcsdaRange),DIRECT_STOPPING/DIRECT_RANGE(104-106),detectInverse()(109-124, `/\b(?:whatQueryIntentschemasrc/lib/intent/query-intent.ts,src/lib/intent/coverage.tslang/localefield anywhere inQueryIntent,EvalExample,EVAL_TAGS, or the coverage types. Language would need to be threaded in as new state, not slotted into an existing field.src/lib/aliases/materials.ts,particles.ts,elements.tsalias β idmaps (MATERIAL_ALIAS_INDEX,PARTICLE_ALIAS_INDEX). Adding Polish aliases (["wΔgiel", 6],["woda", 276], a Polishprotonyparticle entry, β¦) fits the existing shape with no structural change to the lookup mechanism itself. Fun accident:elements.ts:163already has["wolfram", 74]as an English variant name for tungsten β that's also the standard Polish word for tungsten, a free hit. Caveat:normalize.ts's NFKD stripping (61-70) folds Polish Δ/Ε/Γ³/Ε/Δ /Δ to their base letters fine, but "Ε" (U+0142) does not NFKD-decompose and survives as a distinct letter β an unaccented "l" typed by a user won't match a stored "Ε" alias without an explicit extra variant. Recommend a locale-scoped index (reusing the samelookup.tsfuzzy-match machinery) rather than merging Polish aliases into the same flat map as English/trade names, to avoid cross-language collisions.src/lib/nlg/render.ts(216 lines, all of it)`The ${QUANTITY_PHRASE[quantity]} of ${energyLabel(...)} ${particle} in ${material} is ${value} (...)`.β English word order and prepositions baked in; nolangparameter onrenderAnswer(). The harder problem isn't translation, it's Polish grammatical case: "in water" is "w wodzie" (locative), not "w woda" β a naive string-table swap doesn't handle noun declension. Recommend a v1 that sidesteps this with a labeled/colon-style Polish output ("ZdolnoΕΔ hamowania: 12.3 MeVΒ·cmΒ²/g (materiaΕ: woda, czΔ stka: proton, energia: 100 MeV)") instead of attempting fully natural declined sentences, deferring a real declension table to a stretch goal.src/routes/*.svelte(3 files),src/lib/components/**/*.svelte(10 files), plusanswer-status.svelte.ts,asr-status.svelte.ts,model-status.svelte.tspackage.jsonhas exactly one runtime dependency,@huggingface/transformers; nosvelte-i18n/paraglide/i18next/etc.). No centralized strings module β every.sveltefile inlines its own English text. Roughly 45β55 distinct user-facing strings across all of it (button labels, status messages, the "Sorry, I couldn't understand that..." fallback, mic states "Warming upβ¦"/"Listeningβ¦", download/cache dialogs).src/app.html:2hardcodes<html lang="en">β a real fix needed (a11y-relevant), not a false positive. Two earlierlocale/translategrep hits were confirmed false positives:DarkModeToggle.svelte:35-36(translate-x-*Tailwind transform classes) andcoverage.ts:220(localeComparefor sorting tag strings).eval/intents.jsonl,scripts/validate-intents.tslangfield in theEvalExampleschema, so the format itself is language-neutral β butvalidate-intents.ts:17hardcodes the file path (../eval/intents.jsonl) and line 21 hardcodesMIN_EXAMPLES = 100. A Polish set needs a genuinely separate frozen file (eval/intents.pl.jsonl, matching CLAUDE.md's rule that the existingintents.jsonlis a frozen regression suite not to be touched) plus a parameterized validator or a second script with its own (lower, initially) minimum-count threshold.EVAL_TAGScategories (phrasing/quantity/comparison/unit/ambiguity/special) are language-agnostic and should be reusable as-is; per CLAUDE.md, LLM-drafted/hard Polish examples must be tagged"adversarial", never"stress-test"(reserved for the two existing Β§7 sentences).docs/model-hosting-cyfronet.md:9-13,25-29,src/lib/models/remote.ts:12whisper(the multilingual checkpoint already discussed) is mirrored;qwen/llamaaren't yet. Because the mirrored Whisper weights already cover Polish, basic Polish ASR needs zero new model mirroring β the change is code-level (parametrize the `"<The one strategic fork worth deciding early
Option A β native Polish pipeline. Polish ASR (
task: "transcribe",<|pl|>token) β a Polishvariant of the matcher β Polish-aware alias lookups β Polish NLG. Full fidelity: the user sees their own
Polish question echoed back for confirmation (this repo's whole "trust UX" design depends on that echo,
per
docs/voice-pipeline-feasibility.mdΒ§4/Β§5.4). Highest cost: forking/parametrizing all 705 lines ofmatcher.ts, plus the NLG declension problem above.Option B β Whisper's built-in translate task as a shortcut. Whisper's decoder supports a
tasktoken alongside the language token β
"transcribe"(current: Polish audio β Polish text) vs."translate"(Polish audio β English text, directly, in one decode pass). If Polish queries are runthrough
task: "translate"instead, the output could flow through the existing, unmodified Englishmatcher/aliases/NLG entirely β sidestepping the expensive matcher fork completely. Real tradeoffs, all
unmeasured: (1) translation is a strictly harder task than transcription, so domain-jargon accuracy on
this path is unknown and needs its own benchmark pass, same as the transcribe-task English work needed;
(2) the "echoed" query shown to the user would be in English even though they spoke Polish β plausibly
fine for bilingual physicist users (this project's actual audience), but a real trust/UX regression worth
a deliberate decision, not an accident; (3) it only solves dictation, not the flag-toggle's promise of a
fully Polish-language app β NLG/UI still need Polish localization regardless.
Suggested pragmatic path: a hybrid β use Option B to shortcut the hard NLU parsing step for a v1 (Polish
speech in β Whisper
translateβ existing English matcher/compute pipeline), while still localizing NLGoutput, TTS voice, and UI chrome to Polish (Polish in, Polish out; English only in the invisible middle).
Defer the full Option A matcher fork unless the translate-task's domain accuracy or the trust-UX regression
turns out to be unacceptable once measured. This isn't free either β it needs its own accuracy benchmark β
but it avoids committing to the single largest cost item (the matcher fork) before knowing if it's
necessary.
Data collection plan (ties the two feature-request ideas together)
eval/RECORDING.mdused for English(direct stopping-power/range queries, indirect phrasing, conversational filler, multi-material/particle/
energy comparisons, program-name comparison, tricky units, isotope names, an inverse query, a material
alias) β an LLM can produce this first draft.
unnatural or a literal translation rather than how a Polish physicist would actually ask. This is the
step that actually de-risks the "LLM generates sentences" idea β treat model output as a draft, not
ground truth.
scripts/record-session.sh(same WAV/sample-rate conventions), 2β3+ speakers,into a new
eval/audio/<speaker>/set kept separate from the English speakers.QueryIntents for each sentence into a neweval/intents.pl.jsonl(same schema, newfile β see "Eval set" row above).
asr-transcribe.mjs,asr-score-slots.mjs,e2e-audio-intents.ts)against the Polish set once the ASR language plumbing exists, to get the first real Polish domain
E2E number β the thing nobody can quote yet.
Smallest useful next step, doable this week: record even 10β15 of these sentences from 1β2 friends,
run them through whisper-small forced to
<|pl|>with no Polish domain prompt yet, and get a firsthonest raw-Polish-domain-WER baseline β exactly how the English work started, before any tuning existed.
Effort/risk at a glance
alias β idshape already supports this; mainly data entry + native-speaker verification.messages.ts+ a runes-basedlocale.svelte.tsstore (matching the existing*.svelte.tsstore convention, e.g.model-status.svelte.ts) over pulling in a full i18n framework for 2 languages.Open questions (need a human decision, not more research)
query acceptable for v1, or does trust UX require the literal Polish transcript even if it costs the
full matcher fork?
is naturally-declined Polish a hard requirement from day one?
the existing
DarkModeToggle.sveltepattern? Does switching language also need to persist acrosssessions (
localStorage, matching how ASR progress calibration already persists)?speech automatically, or should the toggle always be an explicit, user-driven choice? (Recommend the
latter for v1 β simpler, and matches how the ASR domain-prompt mechanism already assumes a known,
fixed language per request.)
speakers Γ ~30 sentences as its first iteration (
eval/RECORDING.md) and grew from there β is that theright starting scale here too, given however many friends actually volunteer?
docs/design.mdalready flags for English isotope/energy conventions β Polish terminology choices (e.g. "moc hamowania" vs. "zdolnoΕΔ hamowania" for
stopping power) need a real decision from a Polish-speaking physicist, not a model guess.
References
Gathered during this research pass β general/background reading, not load-bearing citations for any
specific number quoted above (see caveats inline where a figure is unverified):
Filed from an investigation session; no code changes made. Scope intentionally left as an open proposal β
see "Open questions" before picking an implementation path.