Fix(loc): Register MANY/MAKEPLURAL for the Es-ES Culture#4181
Closed
TheLacrox wants to merge 24 commits into
Closed
Fix(loc): Register MANY/MAKEPLURAL for the Es-ES Culture#4181TheLacrox wants to merge 24 commits into
TheLacrox wants to merge 24 commits into
Conversation
Design for translating Monolith-Capibara-ESP to Spanish (es-ES) via a Fluent es-ES locale tree with en-US fallback, a Claude-subagent MT pipeline, and _Capibara/ maintenance tooling. Keeps upstream merges conflict-free by making all Spanish additive and confining code changes to a single documented divergence in ContentLocalizationManager.cs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Task-by-task plan: upstream remote, es-ES culture switch + fallback with integration test, glossary, structural validator, upstream sync tool + manifest, the Claude-subagent bulk translation workflow, and root CLAUDE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Canonical es-ES term map and Fluent-preservation rules injected into every translation agent for consistency across the locale tree. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Documents the Spanish localization architecture, the single ContentLocalizationManager divergence and its keep-ours merge rule, the never-edit-upstream discipline, the Fluent-preservation rules, and the upstream sync loop. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds validate-locale.ps1, a PowerShell 7 script that checks es-ES Fluent files against their en-US counterparts for: - Dropped or renamed variables (missing/extra $placeholders) - Hallucinated or renamed message IDs not present in en-US - Unbalanced braces within a message - Orphan translation files with no en-US counterpart Also adds test fixtures under _Capibara/tests/validate/: - en-US/sample.ftl — reference English file - es-good/sample.ftl — correct Spanish translation (validator exits 0) - es-bad/sample.ftl — three deliberate errors (validator exits 1) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The line-based parser reset the current message on any column-0 line
that was not a new message. Real Fluent messages contain col-0 content
mid-message (selector-closing `}`, `}{$var}`, arm labels), which caused
two bugs:
C1 (false negative): variables in the second half of multi-selector
messages (e.g. $tool in comp-repairable-repair) were never captured,
so a dropped/renamed variable there passed silently.
C2 (false positive): 27 en-US files with `}` at column 0 produced
spurious "unbalanced braces" failures.
Get-Messages now tracks brace depth and treats a blank line as the
message terminator, so col-0 content stays attached to its message.
The per-message brace-balance check is unchanged but now meaningful.
Also hardens paths: resolve EnRoot as well as EsRoot, use the resolved
en root in Join-Path/Test-Path, add -LiteralPath to the en Test-Path,
and read files as UTF-8.
Adds fixtures exercising the fixes:
- broken-brace (en-US + es-bad) trips the brace-balance check
- es-good/_Capibara/extra.ftl verifies the _Capibara/ orphan exemption
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds _Capibara/sync-locale.ps1 that compares en-US source against
es-ES translations using a SHA1 hash manifest. After an upstream
merge it reports NEW keys (need translation), CHANGED keys (en-US
source changed, needs retranslation), and REMOVED keys (stale,
prune from es-ES).
Fixed a PowerShell variable-collision bug present in the original
spec: the local dict variable was named $manifest, which clashes
with the typed [string]$Manifest parameter (PS variables are
case-insensitive). The type constraint silently coerces @{} to the
string "System.Collections.Hashtable", breaking .ContainsKey().
Renamed the local to $stored to avoid the collision.
Includes fixture files under _Capibara/tests/sync/ (en-US, es-ES,
and a pre-seeded manifest.json) verified to produce:
fixture run -> NEW=1 CHANGED=1 REMOVED=1
real en-US round-trip -> NEW=0 CHANGED=0 REMOVED=0 (23 509 keys)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sets the build-time culture to Spanish (Spain) and registers en-US as the fallback culture so untranslated keys render English. Adds the es-ES seed file and an integration test asserting the switch + fallback. This is the single intentional divergence from upstream. On a merge conflict in ContentLocalizationManager.cs, keep the Capibara block. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds the Spanish (es-ES) translation of the entire en-US Fluent tree (1437 files, ~23.5k message keys), produced by a 185-agent Claude workflow ordered core-facing dirs first. Includes the generated batch list, the source-hash manifest, the workflow runbook, and the progress log. All files are additive under Resources/Locale/es-ES; upstream never touches them. Untranslated keys fall back to en-US at runtime. Verified: validate-locale.ps1 exit 0; sync NEW=0 CHANGED=0; CapibaraCultureTest boots the server with the full es-ES tree (no Fluent parse errors). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a multi-stage Dockerfile (.NET 10 SDK -> runtime) that inits submodules, packages a linux-x64 hybrid-ACZ server, and runs it non-root as the ss14 user on 1212/udp+tcp. Baked prod config in Docker/server_config.prod.toml with runtime env overrides via entrypoint.sh, plus a single-service docker-compose. No TTS/redis (this fork has no tts.* cvars). Documents deploy in CLAUDE.md. Verified: `docker build` succeeds on net10 and the container boots the server (loads the es-ES config, SQLite prefs on the /data volume). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SS14 stores item/mob/structure names+descriptions in YAML prototypes, not .ftl, so the earlier .ftl pass did not cover them (they rendered English in-game). These are now localized via additive ent-<PrototypeId> Fluent overrides, which the engine applies over the YAML name without touching any prototype file. - CapibaraEntityDumpTest: engine-based dumper resolving each entity's English name/desc through inheritance (accurate, skips abstract). - 16,843 entities / ~18k unique strings translated by a 206-agent workflow. - generate-entity-ftl.ps1: merges the translations and emits valid Fluent (brace-escaping, multiline descs) to es-ES/_Capibara/entities/*.ftl (16,824 keys). - sync-locale.ps1: exempt ent-*/capibara-* (fork-owned es-ES-only keys) from REMOVED. Verified: CapibaraCultureTest boots the server with all entity keys loaded (Fluent parse-clean). 20 strings fell back to English (logged in progress.md). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ts tooltip Fourteen entity strings failed to key-match in the bulk run (curly quotes, em-dashes, trademark signs in the source text); translated directly and regenerated the entity .ftl files — entity coverage now 100% (0 fallbacks). Adds hud-chatbox-highlights-tooltip to the fork seed file: upstream references the key in ChannelFilterPopup.xaml but defines it in no locale, so it rendered as a raw key id. Sync tool exempts this fork-owned key from REMOVED. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Translates all 307 ServerInfo XML docs (~832k chars) and the 4 player-facing .txt files in place: the full in-game guidebook, the join-screen server rules (_Mono/MonolithRuleset.xml), Space Law, and the intro/gameplay/sandbox texts. A follow-up pass translated the 434 [textlink=...] link labels the first pass preserved as markup. This is an approved exception to the never-edit-upstream rule: the engine has no per-locale mechanism for ServerInfo docs. Merge rule (documented in CLAUDE.md): on conflict take upstream's English version and retranslate it; _Capibara/guidebook-manifest.json records the source hashes to detect which docs upstream changed. Verified: validate-guidebook.ps1 — every <...> tag byte-identical to the English baseline across all 307 files; upstream GuideEntryPrototypeTests + DocumentParsingTest pass (every doc parses); English-remnant sweep clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ity keys Two issues found verifying in-client: 1. TextLinkTag renders each [textlink] label as a single unbreakable Label control; 27 Spanish labels exceeded the rules-window width after translation expansion, crashing the client in WordWrap.FinalizeText on the join-rules popup. Shortened all labels to <=72 chars (full text lives in the linked rule pages). 2. Upstream defines ent- overrides for 2 entities in regular .ftl files (translated in the mirrored tree), which the generated entity files redefined - a duplicate-message Fluent error at load. The generator now excludes ids already defined in the mirrored es-ES tree. Verified: validate-guidebook + validate-locale clean, no labels >75 chars, 0 duplicate ent- ids, guidebook parse tests pass (2/2). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Guide entry titles come from guideEntry name: fields resolved via Loc.GetString(name). Titles using guide-entry-*/species-name-* loc ids were already translated; 15 _Mono entries use raw strings and rendered English. - 8 raw names that are valid Fluent ids (Economy, Factions, TSFMC, Smuggling, Defusal, Minor_Factions, USSP, Lore) are localized additively in es-ES/_Capibara/guide-entries.ftl - the raw name doubles as the loc key. - 6 names that cannot be Fluent ids (spaces/dots: Economy Recipes, Arc Furnace Recipes, S.O.P, Union of Soviet Socialist Planets, The Viper Group, Quick Timeline) carry the Spanish title directly in the _Mono YAML, marked with Capibara ESP comments (tiny extension of the in-place exception, documented in CLAUDE.md). - sync-locale.ps1: REMOVED exemption is now folder-based - any key defined under es-ES/_Capibara/ is fork-owned by design. Verified: validate-locale clean, sync NEW=0 CHANGED=0 REMOVED=0, guidebook prototype+parse tests pass (2/2). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
~35 user-visible strings were hardcoded English in Content.Client C#/XAML, bypassing localization entirely (language menu "Choose", trait picker "Expand/Collapse All", ammo counter "No Magazine!", surgery window title, atmos device titles, crystallizer labels, drone console, ghost-role admin window, "Unknown station" fallbacks, GPS "Error", late-join "Join Game"). Each is now routed through Loc with a capibara-ui-* key defined in fork-owned Resources/Locale/es-ES/_Capibara/ui/*.ftl; source edits are marked with Capibara ESP comments. Deliberately skipped: dev/mapping/debug tools, runtime-overridden labels, and brand names (Robust#OS). Also translates Minor_Factions.xml, which the guidebook pass missed due to a write race (committed English), unified on "Grupo Viper" naming. Verified: client compiles (0 errors), validate-locale + validate-guidebook clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Upstream ships this file twice in the git index with different casing (Minor_Factions.xml / minor_factions.xml) pointing to one physical file on Windows. Earlier commits only updated the lowercase entry, leaving the other English - and at package-extraction time either could win. Both entries now carry the Spanish content (same blob). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Raises update.restart_delay from the 20s default to 600s so a pending uptime/update restart only fires after the server has been empty for 10 minutes; any player connecting in that window aborts it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Spanish (es-ES) localization + Dokploy dockerization
The Dokploy host already runs another SS14 server on 1212, so the bind
failed. The host-side port is now ${SS14_PORT:-1212} (container still
listens on 1212 internally), and the advertised launcher connect address
(status.connectaddress) uses the external port instead of hardcoding 1212.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fix(deploy): configurable host port (SS14_PORT)
The language-specific Fluent functions were only registered on the en-US fallback culture, so every es-ES message calling MANY() logged "Unknown function: MANY()" at runtime and rendered broken guidebook text. Register Spanish-grammar versions of MANY/MAKEPLURAL on the active es-ES culture, translate the literal "second" arguments in the chemistry guidebook to "segundo", and fix a CAPATALIZE typo that was copied verbatim from upstream en-US. Adds a regression assertion to CapibaraCultureTest. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
|
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
Author
|
Opened against the wrong repo (this is a downstream translation fork change) — sorry for the noise. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Server logs were flooded with:
for
reagent-effect-guidebook-status-effect,-movespeed-modifier,-electrocuteand-reduce-rotting.Root cause
ContentLocalizationManager.csregisters the language-specific Fluent functions (MANY,MAKEPLURAL) only on the en-US fallback culture (upstream comment even says other languages should add their own). The es-ES tree callsMANY()8× andMAKEPLURAL()11×, so the active es-ES bundle hit an unknown function every time those messages rendered.Changes
ContentLocalizationManager.cs(the one approved Capibara divergence): registerMANY/MAKEPLURALon the es-ES culture with Spanish pluralization rules (vowel → +s,í/ú→ +es,z→ -z+ces,s/xinvariable, consonant → +es). Bracketed with// Capibara ESPmarkers.es-ES/guidebook/chemistry/effects.ftl: translate the literal argumentMANY("second", …)→MANY("segundo", …)(8×) so times render as "3 segundos" instead of "3 seconds".es-ES/_EinsteinEngines/power/batteryDrinker.ftl: fixCAPATALIZE→CAPITALIZEtypo (copied verbatim from upstream en-US, which still has the bug — en-US left untouched per fork policy).CapibaraCultureTest: regression assertions thatMANY()resolves with Spanish singular/plural inside the es-ES bundle, via new seed keycapibara-loc-many.Verification
pwsh _Capibara/validate-locale.ps1→ exit 0, "All es-ES files structurally valid."dotnet test Content.IntegrationTests --filter CapibaraCultureTest→ 1 passed, 0 failed.Note: untranslated "Inner Ring" (out of scope)
The location splash texts ("Inner Ring", "Civilised space…") come from
Resources/Prototypes/_Crescent/Biomes/space_biomes.yml(ambientSpaceBiomeprototypes).SpaceTextDisplaySystemdrawsbiome.Name/biome.Descriptionverbatim — there is no Loc call, so they cannot be translated additively. Fixing them requires either a new (tiny) C# divergence inContent.Client/_Crescent/SpaceBiomes/SpaceBiomeTextDisplaySystem.cs(Loc.TryGetString($"space-biome-{id}-name")-style lookup) or translating the YAML in place like the ServerInfo exception. Left out of this PR pending a policy decision.🤖 Generated with Claude Code