Phase 19: the copy stops being hardcoded Mandarin
Every `中文 · English` string moves out of ~29 components into web/src/i18n: one Pack type, a verbatim zh pack, and two ways to read it — usePack() for components, pack() for the modules that build a line when something happens rather than when something renders. Anything with a value in it is a function on the pack rather than a template at the call site, English pluralisation included: word order isn't universal, and a pack author has to be able to move the number. The roster constants (tones, rewrite styles, export formats, companions) keep only value + emoji, so a label can't drift from its key. On the server, internal/llm/lang.go replaces "Simplified Chinese" in the three prompts that actually name her language. pt-PT is spelled "European Portuguese (pt-PT, never Brazilian Portuguese)" in the prompt itself, and each Lang carries her word for "why" so the tutor prompt still recognises the question when she asks it her way. pair_lang reaches the model through the row-scoped query each handler already ran — the one that proves she owns the document — rather than a second lookup that could disagree with it. Also records Phase 18's deploy: migration 0011 rehearsed against a copy of the live VPS database, then applied for real.
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
package llm
|
||||
|
||||
import "strings"
|
||||
|
||||
// The pair language, as the prompts need to talk about it.
|
||||
//
|
||||
// Three of Petal's prompts name the writer's first language rather than merely
|
||||
// being written in English: the collocation coach asks for a gloss in it, Ask
|
||||
// Petal offers to answer in it, and the explanation translator renders into it.
|
||||
// Before Phase 19 all three said "Simplified Chinese" outright, which made the
|
||||
// zh pair the only one that could ever work.
|
||||
//
|
||||
// A Lang is not a translation of the prompt — the instructions stay in English,
|
||||
// which is what the model follows best. It is the name the model should use for
|
||||
// her language, plus the one word it should watch for when she writes in it.
|
||||
type Lang struct {
|
||||
// Code matches users.pair_lang.
|
||||
Code string
|
||||
// Name is how the prompt refers to the language, spelled the way a model
|
||||
// recognises it. Regional precision matters here: "European Portuguese" is
|
||||
// not "Portuguese" to a model that has read far more pt-BR than pt-PT.
|
||||
Name string
|
||||
// Why asks the same thing she would ask in her own language. It goes into
|
||||
// the Ask Petal prompt as an example, so a model that answers only to
|
||||
// English "why" still recognises the question when she types it her way.
|
||||
Why string
|
||||
}
|
||||
|
||||
// langs holds every pair Petal can currently be a partner in. A language with a
|
||||
// frontend langpack but no entry here still works — it falls back to zh's
|
||||
// behaviour of the prompts, which is wrong but not broken — so keep the two in
|
||||
// step when a pair ships.
|
||||
var langs = map[string]Lang{
|
||||
"zh": {Code: "zh", Name: "Simplified Chinese (Mandarin)", Why: "为什么"},
|
||||
"pt-PT": {Code: "pt-PT", Name: "European Portuguese (pt-PT, never Brazilian Portuguese)", Why: "porquê"},
|
||||
"fr": {Code: "fr", Name: "French", Why: "pourquoi"},
|
||||
"es": {Code: "es", Name: "Spanish", Why: "por qué"},
|
||||
}
|
||||
|
||||
// DefaultLang is the pair assumed when none is known — the column's default, and
|
||||
// the only pair that existed before Phase 19.
|
||||
var DefaultLang = langs["zh"]
|
||||
|
||||
// LangFor resolves a users.pair_lang value. An empty or unrecognised code falls
|
||||
// back to the default rather than erroring: a prompt is not the place to
|
||||
// discover a configuration problem, and the writing still has to be checked.
|
||||
func LangFor(code string) Lang {
|
||||
if l, ok := langs[strings.TrimSpace(code)]; ok {
|
||||
return l
|
||||
}
|
||||
return DefaultLang
|
||||
}
|
||||
Reference in New Issue
Block a user