@rithien/factorio-translate
v0.1.4
Published
Plugin Clusterio: tłumaczenie czatu Factorio en↔pl przez LibreTranslate lub Google Cloud Translate (wybór backendu w configu). Czyta czat z wyjścia serwera, odsyła tłumaczenie przez RCON jako custom command /fp (zachowuje achievementy, wymaga scenariusza
Maintainers
Readme
@rithien/factorio-translate — tłumacz czatu Factorio (en↔pl)
Plugin Clusterio: tłumaczy czat w obie strony en↔pl
przez wybrany backend — LibreTranslate albo
Google Cloud Translate (v2 Basic).
Backend wybiera się jednym polem konfiguracji (translator_backend — dropdown w web panelu clusterio).
Plugin czyta wiadomości czatu z wyjścia serwera, wykrywa język (/detect), i jeśli
confidence > min_confidence — tłumaczy (/translate) i odsyła wynik na serwer przez RCON jako:
<player> [<lang>] <tłumaczenie>gdzie <lang> (en lub pl) to język, w którym jest <tłumaczenie>. Przykład: gracz pisze po
polsku „cześć" → na czacie pojawia się Bob [en] hi.
Bez patchowania save'a — brak folderu module/, działa niezależnie od factorio.enable_save_patching.
Wysyłka tłumaczenia idzie przez RCON jako custom command /fp <text> (zdefiniowany w
scenariuszu factorio-polska w
commands/fp.lua). W przeciwieństwie do wbudowanego /sc (silent-command), custom commandy
nie wyłączają achievementów w save'ie.
Wymaga scenariusza factorio-polska 0.1.0+ — bez niego RCON dostanie "Unknown command: fp" i tłumaczenia nie pojawią się na czacie. Plugin nie ubije serwera — sytuacja jest po prostu logowana.
Opis kanałów komunikacji z grą: Plugin_comunication.md. Wytyczne
implementacyjne: CLAUDE.md.
Struktura
factorio-translate/
├── index.js # eksport `plugin` (nazwa, entrypointy, instanceConfigFields)
├── instance.js # InstancePlugin: onOutput ([CHAT]) -> detect/translate -> sendRcon(/fp ...); sanitizeForFp
├── translators/ # warstwa backendów (wspólny kontrakt: configured / detect / translate)
│ ├── index.js # createTranslator(opts) — fabryka wybierająca backend wg translator_backend
│ ├── http.js # wspólny postJson() — fetch + timeout + normalizacja błędów (nie loguje klucza)
│ ├── libretranslate.js # klient LibreTranslate
│ └── google.js # klient Google Cloud Translation API v2 (Basic)
├── controller.js # ControllerPlugin — stub (na razie nic istotnego)
├── package.json # Node >= 18 (globalny fetch)
├── test/plugin.js # mocha
├── Plugin_comunication.md # dokumentacja kanałów komunikacji z grą
├── CLAUDE.md # wytyczne implementacyjne
└── (CELOWO brak module/) # save NIE jest patchowanyInstalacja
Wymagania wstępne
- Działający klaster Clusterio 2.x (kontroler + co najmniej jeden host z instancją Factorio).
- Node.js ≥ 18 (plugin używa globalnego
fetch). - Scenariusz factorio-polska 0.1.0+ na danej instancji — definiuje komendę
/fp. - Dostęp do jednego z backendów tłumaczeń:
- LibreTranslate — własna instancja albo publiczna/płatna z kluczem, lub
- Google Cloud Translate — projekt Google Cloud z włączonym Cloud Translation API i kluczem API.
- Plugin nie wymaga
factorio.enable_save_patching— działa niezależnie od tej opcji.
1. Zainstaluj plugin z npm
W katalogu instalacji Clusterio (ten z node_modules/@clusterio/..., config-controller.json itd.):
npm install @rithien/factorio-translate2. Zarejestruj plugin w każdym komponencie
Uruchamiane z katalogu instalacji Clusterio:
npx clusteriocontroller plugin add @rithien/factorio-translate
npx clusteriohost plugin add @rithien/factorio-translate
npx clusterioctl plugin add @rithien/factorio-translate(clusterioctl rejestruj tylko jeśli używasz go z tej samej maszyny — przyda się do ustawiania
konfiguracji w kroku 4.)
3. Zrestartuj kontroler i hosta
Zatrzymaj i uruchom ponownie procesy kontrolera oraz hosta, żeby załadowały plugin — np. przez swój
menedżer usług (systemctl restart clusterio-controller / ...-host), albo Ctrl+C i ponownie
npx clusteriocontroller run / npx clusteriohost run. Instancje Factorio nie muszą być wyłączone
podczas restartu hosta, ale plugin „wejdzie" do działającej instancji dopiero przy jej restarcie
(krok 5).
Sprawdź, że plugin się załadował — w logu kontrolera/hosta powinno pojawić się odwołanie do
factorio-translate, a controller.js zaloguje (na poziomie verbose)
factorio-translate (chat translator): kontroler zainicjowany. Plugin widać też na liście pluginów
w web UI.
4. Skonfiguruj instancję
Wybierz backend polem factorio-translate.translator_backend (libretranslate — domyślny — albo
google) i uzupełnij pola odpowiednie dla wybranego backendu. W web UI to pole jest dropdownem.
Backend LibreTranslate (minimum to translator_api_url — dopóki jest puste, backend nieaktywny):
npx clusterioctl instance config set <instancja> factorio-translate.translator_backend libretranslate
npx clusterioctl instance config set <instancja> factorio-translate.translator_api_url https://libretranslate.example
# jeśli Twoja instancja LibreTranslate wymaga klucza:
npx clusterioctl instance config set <instancja> factorio-translate.translator_api_key <klucz>Backend Google Cloud Translate (minimum to google_api_key — dopóki jest puste, backend nieaktywny):
npx clusterioctl instance config set <instancja> factorio-translate.translator_backend google
npx clusterioctl instance config set <instancja> factorio-translate.google_api_key <klucz-google-cloud># (opcjonalnie, wspólne dla obu backendów) pozostałe pola — zob. tabela "Konfiguracja" niżej, np.:
npx clusterioctl instance config set <instancja> factorio-translate.min_confidence 90Zmiany backendu / URL / kluczy / timeoutu są podchwytywane na żywo (bez restartu instancji). Konfigurację można też edytować w web UI w ustawieniach instancji.
5. Uruchom (lub zrestartuj) instancję ze scenariuszem factorio-polska
npx clusterioctl instance start <instancja>Od tego momentu wiadomości z czatu w obsługiwanych językach (en/pl) z confidence > min_confidence
będą tłumaczone i wysyłane na czat w formacie <player> [<lang>] <tłumaczenie>.
Aktualizacja / odinstalowanie
- Aktualizacja:
npm install @rithien/factorio-translate@latestw katalogu Clusterio, zrestartuj kontroler i hosta, zrestartuj instancję. - Odinstalowanie:
plugin remove @rithien/factorio-translatew każdym komponencie (analogicznie doplugin add), zrestartuj kontroler i hosta.
Konfiguracja (instancja, prefiks factorio-translate.)
| Pole | Typ | Domyślnie | Opis |
|---|---|---|---|
| enabled | boolean | true | Czy plugin jest aktywny na tej instancji |
| translator_backend | enum | libretranslate | Backend tłumaczeń: libretranslate lub google (dropdown w web panelu) |
| translator_api_url | string | "" | [libretranslate] Bazowy URL LibreTranslate, bez końcowego /. Puste = backend nieaktywny |
| translator_api_key | string | "" | [libretranslate] Klucz API; puste = bez klucza. Nie jest logowany |
| google_api_key | string | "" | [google] Klucz Google Cloud (Cloud Translation API). Puste = backend nieaktywny. Nie jest logowany |
| min_confidence | number | 90 | Próg confidence z /detect (0–100); tłumaczymy gdy confidence > min_confidence. Google: 0–1 → ×100, brak → 100 |
| request_timeout_ms | number | 5000 | Timeout pojedynczego żądania HTTP |
| min_message_length | number | 1 | Minimalna długość wiadomości, by ją tłumaczyć |
| translate_server_messages | boolean | false | Czy tłumaczyć też wiadomości od <server> |
| max_output_length | number | 500 | Sanity-cap na długość /fp <text> (anti-flood + RCON line) |
Zmiana translator_backend / translator_api_url / translator_api_key / google_api_key /
request_timeout_ms jest podchwytywana na żywo (bez restartu instancji).
Jak to działa (przepływ jednej wiadomości)
onOutputdostaje linię zparsed.type === "action" && parsed.action === "CHAT"; treść to"<player>: <text>"— rozdzielana na pierwszym": ".- Guardy: aktywny?
textniepusty i ≥min_message_length? nadawca ≠<server>(chyba żetranslate_server_messages)?textnie pasuje do^\S+ \[(en|pl)\](anty-pętla)? POST /detect→{ language, confidence }.confidence <= min_confidence→ koniec.language === "pl"→target = "en";language === "en"→target = "pl"; inny język → koniec.POST /translate(source,target,format: "text") →translatedText.sendRcon('/fp <player> [<target>] <translatedText>', true). Linia przepuszczona przezsanitizeForFp()(strip znaków sterujących + cap długości) —/fppo stronie Lua robi tylkogame.print(cmd.parameter), więc nie ma interpretacji Lua i nie potrzeba escape'owania.
Błąd backendu tłumaczeń (sieć, timeout, 4xx/5xx) → log warn i pomięcie tej wiadomości; serwer nieruszony.
Krok 3–5 wykonuje wybrany backend (LibreTranslate /detect + /translate, albo Google Cloud Translation v2
/detect + /translate) — instance.js widzi tylko wspólny kontrakt detect() / translate().
Dlaczego /fp zamiast /sc
Wbudowane /sc (silent-command) i /c ustawiają trwałą flagę "Cheats have been enabled" na
save'ie — permanentnie wyłączają achievementy. Custom commandy zarejestrowane przez
commands.add_command w Lua nie liczą się jako cheats. Scenariusz factorio-polska rejestruje
/fp (server-only, RCON-only) jako trywialny alias dla game.print(cmd.parameter). Plugin po
prostu wysyła /fp <text> i tekst trafia do czatu bez setowania cheat flagi.
Patrz scenario/commands/fp.lua w repo factorio-polska.
Uwagi
- Plugin nie kasuje oryginalnej wiadomości — dokłada tłumaczenie obok.
- Kolejność wysłanych tłumaczeń może odbiegać od kolejności wpisania (różna latencja API + długie komendy RCON). Dla czatu akceptowalne.
- LibreTranslate: dla sensownego limitu zapytań użyj własnej instancji albo płatnego klucza — publiczne instancje bywają mocno rate-limitowane (HTTP 429 → wiadomość zostanie pominięta).
- Google Cloud Translate: używany jest API v2 (Basic) z uwierzytelnianiem kluczem API (
?key=), nie v3 (OAuth2 / konto serwisowe). Cloud Translation to usługa płatna — pilnuj limitów/budżetu w Google Cloud. Klucz najlepiej ogranicz (restrict key) do Cloud Translation API.
Testy
npm install
npm test