Jak przetłumaczyć plik HTML bez psucia tagów, atrybutów i linków
Co tłumaczyć w pliku HTML, czego nie ruszać i jak to sprawdzić. Z przykładową stroną, darmowym narzędziem do weryfikacji i testem lokalnego LLM w 9 przebiegach.

Aby przetłumaczyć plik HTML, tłumacz tekst, który czytają ludzie, oraz atrybuty zawierające słowa skierowane do czytelnika, takie jak alt, title, placeholder, aria-label i meta opis strony. Znaczniki pozostaw bez zmian: nazwy tagów, href, src, id, class, skrypty, style i wszystko oznaczone translate="no". Następnie zmień jedną wartość, o której łatwo zapomnieć — atrybut lang w elemencie <html> — aby przeglądarki, czytniki ekranu i wyszukiwarki wiedziały, w jakim języku jest teraz strona.
Zachowanie nienaruszonych tagów okazało się łatwiejszą częścią. Przetłumaczyliśmy 59-wierszową stronę pomocy HTML na uproszczony chiński dziewięć razy za pomocą lokalnego modelu, stosując trzy metody, i sprawdziliśmy każde wyjście skryptem. Każdy przebieg zachował wszystkie tagi, linki i identyfikatory. Co poszło nie tak, było cichsze: model przetłumaczył wiersz wewnątrz bloku kodu <pre> we wszystkich sześciu przebiegach, w których wysłano cały plik, pozostawił aria-label nawigacji po angielsku we wszystkich sześciu, a metoda, która z założenia najbezpieczniej zachowuje znaczniki — tłumaczenie fragmentów tekstu po kolei — wygenerowała zdanie, które mówiło czytelnikowi, by zrobił coś złego, we wszystkich trzech swoich przebiegach.
Szybka odpowiedź: co tłumaczyć, a czego nie ruszać
| Część pliku | Przykład z naszej próbki | Tłumaczyć? |
|---|---|---|
| Widoczny tekst | nagłówki, akapity, komórki tabel, tekst linków, <label> | Tak |
<title> i meta opis | <title>Export Your Library | Shelfmark Help</title> | Tak. Oba pojawiają się w wynikach wyszukiwania i na kartach przeglądarki. |
og:title i inne meta tagi społecznościowe | <meta property="og:title" content="Export Your Library"> | Tak, tylko wartość content |
alt, title, placeholder, aria-label | alt="The Export dialog with EPUB selected", aria-label="Breadcrumb" | Tak |
lang w <html> | <html lang="en"> | Zmień na język docelowy, na przykład zh-Hans |
| Nazwy tagów i atrybutów | <a>, <img>, width, sam aria-label | Nie |
href, src, id, class, for, name, type, data-* | href="#choose-format", id="q" | Nie |
<code>, <pre>, <script>, <style> | shelfmark export --format epub | Nie. O komentarzach w kodzie i przykładowym wyjściu zdecyduj osobno. |
| Tekst ustawiany przez CSS | .note::before { content: "Note: "; } | Tak, ale znajduje się w arkuszu stylów, więc trzeba to zaplanować |
| Dane strukturalne JSON-LD | "name": "Export your Shelfmark library" | Przetłumacz wartości name i description; zachowaj klucze i poprawność JSON |
Elementy z translate="no" | <span translate="no">Shelfmark</span> | Nie |
| Komentarze HTML | <!-- Generated by the help-center build... --> | Opcjonalne. Czytelnicy ich nie widzą. |
| Odniesienia do znaków | <library>, , © | Zachowaj zakodowane znaki jako zakodowane. < nie może stać się prawdziwym <. |
Ta lista nie jest naszym wymysłem. Sekcja standardu HTML dotycząca atrybutu translate definiuje „atrybuty podlegające tłumaczeniu”: title w każdym elemencie, alt w obrazach, placeholder w polach wejściowych, content w elementach meta, których nazwa jest znana jako podlegająca tłumaczeniu, oraz lang, który – jak stwierdza – musi zostać zmieniony, aby pasował do nowego języka. Wymienia także aria-label z ARIA jako przykład atrybutu, który mogą dodawać inne specyfikacje, i stwierdza, że w elemencie translate="no" tekst i wartości atrybutów „pozostają bez zmian podczas lokalizacji strony”.
Co testowaliśmy
Napisaliśmy sample-source.html, stronę centrum pomocy dla tej samej fikcyjnej aplikacji do czytania, Shelfmark, której użyliśmy w naszym teście tłumaczenia Markdown. Zawiera ona po jednym przykładzie każdego wiersza z powyższej tabeli oraz kilka pułapek:
- zdanie podzielone przez znaczniki inline: „Zanim wyeksportujesz, zrób kopię zapasową notatek. Eksport kopiuje twoje książki, ale nie usuwa niczego z Shelfmark”, z nazwą produktu wewnątrz
translate="no"; - zdanie z dwoma elementami inline
<code>, których kolejność ma znaczenie: „Wpisz ścieżkę do folderu<library>po--out”; - blok
<pre><code>z poleceniem i jedną linią przykładowego wyniku,# Export finished: 128 books; - reguła CSS, która wstawia słowo „Uwaga:” przed akapitem;
- link w obrębie strony do
#choose-format, linkmailto:, formularz wyszukiwania i blok JSON-LD.
Następnie poprosiliśmy lokalny model o przetłumaczenie go na uproszczony chiński na trzy sposoby:
- Cały plik. Wklej cały plik i poproś o zwrot przetłumaczonego pliku.
- Z instrukcjami. To samo plus osiem reguł: zachowaj każdą nazwę znacznika i atrybutu, nie zmieniaj wartości
href,src,id,classi podobnych, przetłumacz wymienione atrybuty tekstowe i znaczniki meta, pozostaw<code>,<pre>,<script>i<style>w spokoju, z wyjątkiem JSON-LDnameidescription, respektujtranslate="no", zachowaj odniesienia do znaków i komentarze oraz ustawlangnazh-Hans. - Segmenty. Podejście narzędzi wyodrębniających tekst z HTML: skrypt dzieli plik na znaczniki i tekst, pomija
<code>,<pre>,<script>,<style>i elementytranslate="no", wysyła pozostałe fragmenty tekstu i atrybuty tekstowe do modelu jako numerowaną listę JSON, a tłumaczenia zapisuje z powrotem do nietkniętego znacznika. Sam także ustawialang="zh-Hans".
Model to Qwen 3.6 27B (Q4_K_M) w LM Studio, z wyłączonym myśleniem i temperaturą 0,3, na Apple M4 Max z 64 GB pamięci. Każdą metodę uruchomiliśmy trzy razy, łącznie dziewięć uruchomień, 27 października 2026 r. Skrypt to run_llm_html_test.py, a wszystkie dziewięć wyników opublikowano bez edycji; README zawiera listę wszystkich plików. Jeśli wcześniej nie konfigurowałeś lokalnego modelu, nasz przewodnik po tłumaczeniu z lokalnym LLM omawia ustawienia.
To był mały test jednego modelu, jednej pary językowej i jednej strony. Pokazuje, jakie problemy się zdarzają i jak je wychwycić. Nie klasyfikuje modeli ani narzędzi.
Wynik: znaczniki przetrwały; trzy decyzje nie
Każde wyjście sprawdziliśmy za pomocą check_html_translation.py, narzędzia korzystającego wyłącznie z biblioteki standardowej Pythona. Pełne wyniki znajdują się w check-results.txt, a results.csv zawiera jeden wiersz na przebieg.
| Sprawdzenie | Cały plik (3 przebiegi) | Z instrukcją (3 przebiegi) | Segmenty (3 przebiegi) |
|---|---|---|---|
Te same 58 tagów w tej samej kolejności; każdy href, src, id, class bez zmian | 3 z 3 | 3 z 3 | 3 z 3 |
Tekst translate="no" zachowany, link w obrębie strony nadal działa, <library> nadal zakodowany | 3 z 3 | 3 z 3 | 3 z 3 |
Przykładowa linia wyjściowa wewnątrz <pre> przetłumaczona | 3 z 3 | 3 z 3 | 0 z 3 (pominięte celowo) |
CSS content: "Note: " przetłumaczony | 3 z 3 | 0 z 3 | 0 z 3 (pominięte celowo) |
JSON-LD name i description przetłumaczone | 3 z 3 | 3 z 3 | 0 z 3 (pominięte celowo) |
aria-label="Breadcrumb" przetłumaczone | 0 z 3 | 0 z 3 | 3 z 3 |
Zdanie z dwoma elementami inline <code> mówi to, co powinno | 3 z 3 | 3 z 3 | 0 z 3 |
| Spacje między chińskim tekstem wokół tagu inline | brak | 0, 5 i 4 | 6 w każdym przebiegu |
lang na <html> | zh-CN (nie proszono) | zh-Hans | zh-Hans (ustawione przez skrypt) |
Każdy z dziewięciu przebiegów zachował te same 58 tagów w tej samej kolejności, każdy href, src, id i class, cel linku w obrębie strony, komentarz, escapowany <library> oraz nazwę produktu wewnątrz translate="no". Jeśli obawialiście się, że model zepsuje znaczniki prostej strony, to tego nie zrobił. Problemy dotyczyły wyłącznie tego, co tłumaczyć.
1. Przykładowe wyjście wewnątrz <pre> zostało przetłumaczone
Blok kodu w naszym przykładzie kończy się linią wyjścia programu:
<pre><code>shelfmark export --format epub --covers --out ~/Books/export
# Export finished: 128 books</code></pre>
We wszystkich sześciu przebiegach całościowych i z instrukcjami model zwrócił # 导出完成:128 本书 w tej linii, w tym w trzech przebiegach, w których prompt mówił, aby nie zmieniać niczego wewnątrz <pre>. Sama komenda nigdy nie została ruszona. Model potraktował tę linię jako komentarz, a tłumaczenie komentarzy to rozsądny wybór w samouczku. Ale ta linia to to, co wypisuje program, a czytelnik, który porówna ją ze swoim terminalem, zobaczy angielski. Zdecyduj przed tłumaczeniem, czy przykładowe wyjście i komentarze w kodzie są tłumaczone, i powiedz o tym narzędziu, albo całkowicie zabezpiecz bloki <pre>.
Przebiegi całościowe przetłumaczyły także CSS content: "Note: " w bloku <style>, podczas gdy przebiegi z instrukcjami, którym kazano zostawić <style> w spokoju, zachowały go po angielsku. Żadne z tych rozwiązań samo w sobie nie jest błędne. Tłumaczenie arkusza stylów jest poprawne dla strony, na której CSS jest inline, a błędne dla witryny, w której jeden wspólny arkusz stylów obsługuje wszystkie języki. Tak czy inaczej, to decyzja, którą ktoś musi podjąć, a nie szczegół, który model powinien zgadywać.
2. Etykiety dostępności zostały pominięte
Nawigacja okruszkowa to <nav aria-label="Breadcrumb">. Czytnik ekranu odczytuje tę etykietę; widzący czytelnicy nigdy jej nie widzą. We wszystkich sześciu przebiegach całościowych i z instrukcjami pozostała po angielsku, mimo że prompt z instrukcjami wymieniał aria-label wśród atrybutów do przetłumaczenia. W jednym przebiegu z instrukcjami aria-label="Search" przycisku wyszukiwania również pozostało po angielsku.
Metoda segmentowa tłumaczyła ją za każdym razem, ponieważ jej skrypt zbierał każdą wartość aria-label, alt, title i placeholder i prosił o każdą z nich osobno. To najsilniejszy argument za celowym wyodrębnianiem atrybutów tekstowych, zamiast liczenia na to, że model je zauważy.
3. Tłumaczenie fragmentów zachowało tagi, ale zepsuło zdanie
Metoda segmentowa jest z założenia bezpieczna dla znaczników: model nigdy nie widzi tagu, więc nie może go uszkodzić. Przeszła wszystkie kontrole strukturalne we wszystkich trzech przebiegach. Dała też jedyne tłumaczenie w teście, które przekazało czytelnikowi błędne instrukcje.
Zdanie angielskie brzmi: „Type the path to the <library> folder after --out.” Wbudowany <code> dzieli je na trzy fragmenty: „Type the path to the”, „folder after” i kropkę. Chiński układa te słowa w innej kolejności, więc tłumacz musi przenieść dwa elementy kodu. Metoda segmentowa nie może ich przenieść. Wszystkie trzy przebiegi odwróciły instrukcję. Przebieg 2 dał to (przebiegi 1 i 3 mówią 后 zamiast 之后, z tym samym znaczeniem):
在 <code><library></code> 之后输入 <code>--out</code>文件夹的路径。
To znaczy „po <library> wpisz ścieżkę folderu --out”, czyli odwrotność instrukcji. Wszystkie sześć przebiegów cało-plikowych i z instrukcjami przeniosło elementy kodu i zrobiło to poprawnie:
在 <code>--out</code> 后面输入 <code><library></code> 文件夹的路径。
To samo dzielenie pozostawiło zbędne spacje tam, gdzie tag styka się z tekstem chińskim, np. 前往 <b>设置</b> i <b>书库</b> ,, we wszystkich trzech przebiegach segmentowych, po sześć miejsc w każdym. Przeglądarka pokazuje je jako widoczne przerwy, także przed chińską interpunkcją. Przebiegi cało-plikowe nie miały ich wcale; dwa z trzech przebiegów z instrukcjami miały cztery lub pięć.
Kontroler strukturalny nie wychwyci odwróconego zdania, bo każdy tag jest dokładnie tam, gdzie był. Nasz kontroler zgłasza, gdy wbudowany kod zmienia kolejność, co jest normalne w wielu językach, ale to ostrzeżenie nie odróżnia dobrego od złego. W tym teście zadziałało przy wszystkich sześciu poprawnych tłumaczeniach i milczało przy trzech błędnych, bo błędne zachowały angielską kolejność. Tylko człowiek czytający zdanie może to stwierdzić. Rozwiązaniem jest wysyłanie tłumaczowi całych zdań wraz z ich wbudowanymi tagami, co robiły przebiegi cało-plikowe, i sprawdzanie struktury po tłumaczeniu.
Mniejsze różnice
lang: przebiegi cało-plikowe zmieniły go nazh-CNsamodzielnie. Przebiegi z instrukcjami użyłyzh-Hans, zgodnie z poleceniem. Oba są poprawnymi tagami języka;zh-Hansnazywa pismo (chiński uproszczony),zh-CNnazywa region. Wytyczne W3C dotyczące deklaracji języka zalecają deklarowanie języka na elemencie<html>, a nie w meta taguContent-Language.- JSON-LD: przebiegi cało-plikowe i z instrukcjami przetłumaczyły wartości
nameidescriptioni zachowały poprawny JSON. Nasz skrypt segmentowy pomijał zawartość<script>, więc jego dane strukturalne pozostały po angielsku, co jest też zachowaniem narzędzia ignorującego skrypty. - Odniesienia znakowe: przebiegi cało-plikowe i z instrukcjami zachowały
›, ,©i odniesienie do emoji w zapisie. Pięć z tych sześciu przebiegów przepisało zakres2–5chińskimi słowami (2 到 5 lub 2 至 5); jeden przebieg z instrukcjami napisał zwykły myślnik zamiast odniesienia, choć jego prompt mówił, by zachować odniesienia. Nasz skrypt segmentowy zdekodował je wszystkie i zapisał z powrotem znaki, co wyświetla się tak samo. Ten, który ma znaczenie,<library>wewnątrz<code>, pozostał zakodowany we wszystkich dziewięciu przebiegach.
Przed tłumaczeniem: przygotuj plik
- Oznacz, co nie może się zmienić. Dodaj
translate="no"do nazw produktów, numerów modeli, ciągów przypominających kod w prozie i wszystkiego innego, co ma pozostać bez zmian. To część HTML, nic nie kosztuje, a model uszanował to we wszystkich sześciu przebiegach, którym wysłano cały plik. Narzędzia segmentowe, które to czytają, jak nasz skrypt, całkowicie pomijają te elementy. Nasz przewodnik po tym, czego nie tłumaczyć wyjaśnia, jak zdecydować, co trafi na tę listę. - Ustal politykę dotyczącą kodu. Czy komentarze w kodzie są tłumaczone? Czy przykładowe wyniki? Czy arkusz stylów jest wspólny dla wszystkich języków? Zapisz odpowiedź wraz z nazwami, które nie mogą się zmienić, w słowniku tłumaczeniowym i przekaż ją temu, kto lub co tłumaczy.
- Zachowaj identyfikatory w języku źródłowym. Linki w obrębie strony wskazują na wartości
id. Jeśli ktoś przetłumaczyid, każdy link do niego się zepsuje. Żaden z naszych przebiegów tego nie zrobił, ale tłumacz, który „pomocnie” zlokalizuje kotwice, zrobi to. - Zbierz ukryty tekst. Wypisz atrybuty zawierające słowa:
alt,title,placeholder,aria-label,valuena przyciskach, meta tagi i JSON-LD, aby zostały sprawdzone i nie zapomniane. - Nie tłumacz ciągów JavaScript w miejscu. Komunikaty ustawiane przez skrypt, takie jak alert czy etykieta przycisku tworzona w kodzie, wymagają osobnego kroku lokalizacji w kodzie. Żaden z sześciu przebiegów cało-plikowych ani z instrukcjami nie zmienił naszego bloku skryptu, co jest poprawne, ale oznacza, że te ciągi pozostają po angielsku.
Po tłumaczeniu: sprawdź
-
Uruchom kontrolę strukturalną.
python3 check_html_translation.py source.html translated.html --lang zh-Hans --keep ShelfmarkPorównuje tagi i ich kolejność, każdy atrybut, który nie jest tekstem dla czytelnika, zawartość
<pre>,<script>i<style>, wbudowany kod, klucze JSON-LD, tekst ztranslate="no", komentarze i linki w obrębie strony. Ostrzega o atrybutach tekstowych wciąż po angielsku, wartościlanginnej niż oczekiwana, widocznym tekście wyglądającym na nieprzetłumaczony, wbudowanym kodzie, który zmienił kolejność, oraz spacjach między tekstem chińskim, japońskim lub koreańskim a wbudowanym tagiem. Nie ocenia jakości tłumaczenia. -
Przeczytaj każde zdanie zawierające wbudowany tag. To tutaj znaczenie się zepsuło w naszym teście. Nie polegaj na ostrzeżeniu kontrolera „wbudowany kod zmienił kolejność”, by znaleźć te zdania: w naszym teście błędne tłumaczenia to te, które zachowały oryginalną kolejność.
-
Otwórz stronę w przeglądarce. Kliknij linki w obrębie strony, przejdź tabulatorem po formularzu z czytnikiem ekranu, jeśli możesz, i wypatruj przerw wokół pogrubionego i podlinkowanego tekstu.
-
W językach pisanych od prawej do lewej ustaw także
dir="rtl"na elemencie<html>. Nasz przewodnik po tłumaczeniu książek RTL opisuje, co jeszcze się zmienia. -
W XHTML, np. w plikach rozdziałów wewnątrz EPUB, potwierdź też, że plik nadal jest poprawnym XML, na przykład za pomocą
xmllint --noout chapter.xhtml. Przeglądarka wybaczy niezamknięty tag; czytnik e-booków może nie. Nasza lista kontrolna QA tłumaczenia EPUB obejmuje resztę e-booka.
Gdzie pasuje BookTranslator
Strona przesyłania BookTranslatora przyjmuje pliki .html, .htm i .xhtml obok książek i dokumentów, więc możesz przesłać zapisaną stronę lub statyczny artykuł pomocy jako cały plik. Nie przepuściliśmy tego przykładu przez BookTranslatora na potrzeby tego artykułu i nie testowaliśmy, jak radzi sobie z kolejnością kodu wbudowanego, treścią CSS ani JSON-LD, więc sprawdź jego wynik tak samo, jak sprawdziłbyś wynik każdego innego narzędzia. Cenę widzisz przed zapłatą.
Niezależnie od tego, czego użyjesz, wykonaj kroki przygotowawcze przed tłumaczeniem i sprawdzenia po nim. Strona z nienaruszonymi wszystkimi tagami wciąż może kazać czytelnikowi wpisać coś niewłaściwego. Jeśli Twój HTML to w rzeczywistości dokumentacja napisana w Markdownie, nasz przewodnik po tłumaczeniu Markdowna omawia format źródłowy.
Pobierz pliki testowe
Wszystko z tego testu można swobodnie wykorzystać:
- sample-source.html: 59-wierszowy angielski przykład;
- check_html_translation.py: narzędzie do sprawdzania struktury (tylko biblioteka standardowa Pythona);
- run_llm_html_test.py: trzy metody, dla dowolnego lokalnego serwera zgodnego z OpenAI;
- dziewięć wyników w folderze outputs, wraz z check-results.txt i results.csv.
Powiązane wpisy





