Tłumaczenie WordPressa (obsługa wielu języków), funkcje gettext i edytor Poedit


Ludzie mają ogromny problem. Nie znają angielskiego.

Nie znają też polskiego.

I nie znają i nie wiedzą o wielu innych rzeczach

Dopóki…

Ktoś nie napisze artykułu. Takiego jak ten.

Wciąż i wciąż na nowo widać niedopracowania w tłumaczeniach. Czy zastanawiałeś się dlaczego lingwiści nie nadążają z tłumaczeniami lub robią to niedbale?

Wątki dotyczące polskich aktualizacji są dłuższe niż wszystkie problemy na forum razem wzięte. Słowo „problem” jest drugie co do wielkości po WordPressie w chmurze tagów a za nim w kolejce stoi słynny „Read More„.

Próbuję więc ustalić przyczynę.

 

Jeżeli kiedykolwiek:

to czytaj kolejne akapity tego artykułu a dowiesz się:

  1. z czym to się je,
  2. czym smaruje i
  3. jak przyprawa,

bowiem przeszedłem przez podobne piekło i teraz postanawiam podzielić się swoja historią.

A to było tak…

 

Wszystko kręci się wokół magicznych funkcji gettext.


Jakiekolwiek nie jest twoje pojęcie na ten temat, wiedz tylko tyle, że jeżeli nie możesz przetłumaczyć danego słowa w programie Poedit, lub na twojej stronie niektóre frazy nadal pozostają nieprzetłumaczone, oznacza to, że w pliku źródłowym, nie zostały one przygotowane pod tłumaczenie.

Czyli mówiąc kolokwialnie. Ciąg tekstowy nie został „opakowany” funkcją tłumaczącą gettext.

O tym jak to zrobić dowiesz się za chwile.

Jeżeli natomiast nie przygotujesz wtyczki pod tłumaczenie, ktoś będzie musiał zrobić to za ciebie inaczej skazujesz użytkowników na używanie jej w oryginalnym języku. I tu znów koło zapętla się do angielskiego, którego nikt nie zna, bowiem przepisy nakazują jako domyślnego języka używać angielskiego.

Zacznijmy zatem naszą przygodę od omówienia pewnych stałych składników, które niezmiennie będziesz musiał powtarzać jak mantre w tłumaczeniach.

 

Warunek 1: Text Domain


Text-domain to unikatowy identyfikator motywu. Przyjęła się zasada, że text-domain musi znajdować się w każdym z tłumaczonych tekstów (nawet jeżeli jest to jeden wyraz, lub wyraz ten jak np. słowo: „Settings„, znajduje się w źródle) oraz każdy pojedynczy wyraz, który jest stałym elementem motywu musi być przetłumaczony.

Dzięki niemu WordPress rozpoznaje dany pakiet językowy z pośród wszystkich załadowanych do systemu i pilnuje, aby dany ciąg tekstowy nie pojawił się np. w innym motywie lub wtyczce, lecz był przypisany do tej właściwej. Inaczej mówiąc:

Wszystkie frazy oznaczone tym identyfikatorem należą do motywu/wtyczki z tym identyfikatorem i do każdej frazy słowa należy go dopisywać.

Text-domain tworzymy zgodnie z zasadą nazewnictwa, o której już wcześniej pisałem.

Przykład prawidłowego użycia poniżej.

Funkcja wyjściowa:

__( $text, $text-domain );

Po podstawieniu danych:

__( 'Tekst, który ma być tłumaczony', 'text-domain' );

Tekst, który ma być tłumaczony jest zawsze w języku angielskim. Jest to zasada, która obowiązuje wszystkie motywy i wtyczki znajdujące się w repozytorium WordPressa.

 

Warunek 2: Informacja o identyfikatorze musi znaleźć się w nagłówku:


* Text Domain: text-domain
* Domain Path: /languages
*/

i na koniec…

 

Warunek 3: Pakiet językowy musi być załadowany do systemu:


add_action('after_setup_theme', 'my_theme_setup');
 function my_theme_setup(){
 load_theme_textdomain('text-domain', get_template_directory() . '/languages');
 }

a w przypadku wtyczek:

add_action( 'plugins_loaded', 'myplugin_load_textdomain' );
 /**
 * Load plugin text-domain.
 *
 * @since 1.0.0
 */
 function myplugin_load_textdomain() {
    load_plugin_textdomain( 'text-domain', false, 
    plugin_basename( dirname( __FILE__ ) ) . '/languages' );
 }
Uwaga! Brak plików językowych (.mo, po. czy .pot) w tym folderze nie oznacza, że frazy się nie pojawią. Jednak funkcja ładująca obowiązkowo musi tu się znaleźć i obowiązkowo musi być wywołana zaczepem akcji after_setup_theme.

Dopiero teraz można się zabrać za tłumaczenie.

 

Jak przygotować kod źródłowy pod edytor Poedit.


WordPress  ma w zwyczaju definiować własne funkcje. Również i w tym przypadku przygotował on dla nas szereg rozwiązań, które musimy wdrożyć, aby tłumaczenie zadziałało.

 

Oto pełna lista funkcji jakich musisz użyć, aby przygotować motyw/wtyczkę pod tłumaczenie:

Funkcje podstawowe:

Funkcje ucieczkowe (escaping)

Funkcje daty i numerów

 

Omówienie


Najprostsze z funkcji tłumaczących a zarazem najczęściej używane to:

Literka e w funkcji sugeruje, że dany tekst będzie wyświetlony natomiast podwójna linia zwraca tekst. Różnica jest identyczna jak w przypadku użycia echo i return w PHP.

Identyczny schemat myślenia będzie miał miejsce w kilku pozostałych funkcjach . Ilekroć spotkasz się z:

chodzi o zwrócenie lub wyświetlenie tekstu.

 

Funkcję _e() stosuje się najczęściej w szablonach motywu takich jak index.php, page.php, header.php czyli w plikach głównie opartych o HTML + PHP gdzie zachodzi konieczność chwilowego przejścia z HTML na PHP i z powrotem.

Przykład:

<p><?php _e('Sorry, no posts matched your criteria.', 'wp-less-is-more'); ?></p>
 <?php endif; ?>
</div>
Ważna uwaga! Funkcja ta wyświetli tekst pomimo braku tłumaczenia w motywie.

Jest bardzo przydatna, bowiem zapobiega to pewnemu konfliktowi kiedy, teksty w motywie zostały jedynie „opakowane”, ale plik .pot, .po lub .mo nie zostały jeszcze utworzone i załadowane do WP. Inaczej mówiąc, twój motyw nie musi posiadać paczki językowej a i tak będzie wyświetlał teksty – oczywiście w oryginalnym języku czyli to co zostało wpisane w kodzie.

Natomiast funkcji __() używa się kiedy chcesz przekazać tłumaczony tekst np. do zmiennej, aby go można było potem użyć dalej.

Najczęściej spotkasz tą funkcje w plikach funkcyjnych, opcjach motywu itd.:

register_sidebar (  array (
 'name' => __( 'Main Widget Area', 'less-is-more'),
 'id' => 'sidebar',
 'description' => __( 'Page Template + Sidebar .',  'less-is-more' ), ) );
$translated = __( 'Hello World!', 'mytextdomain' );
echo $translated;
Uwaga! Funkcja zwróci tekst, Aby go wyświetlić musisz użyć deklaracji echo.

 

Czy rozumiesz już teraz tą różnicę?

Jedne z funkcji tworzą wartość zmiennych, które można użyć w innym miejscu w skrypcie, inne znów są od razu wyświetlane, na przemiennie z PHP tak jakby były normalnymi znacznikami HTML.

 

Jest jeszcze jedna bardzo ważna rzecz, o której należy wspomnieć.

W funkcjach tłumaczących zabrania się używania zmiennych i czystego kodu HTML zamiast tego należy stosować podstawianie zmiennych używając znaczników konwersji: procenta plus znak odnoszący się do typu danych: % + typ

 

Jak podstawiać zmienne w ciągach tekstowych?


Realizujemy to dzięki  funkcjom printf  i sprintf a charakter ich działania możesz porównać w odniesieni do wspomnianych już  wcześniej _e() i __() czyli printf wyświetli tekst a sprintf zwróci tekst np. do zmiennej.

Przykład.

Załóżmy, że chcesz umieścić w tekście swój adres i link do mapki w google. Normalny człowiek próbowałby zrobić to tak:

<p><?php 
$miasto = 'Kraków';
$kod = "12-345";
$link = '<a href="google.pl/mapy/etc';
_e("Twoje miasto to: $miasto a kod pocztowy: $kod. Zobacz mapkę: $link". "text-domain");  ?></p>

lub też tak:

_e("Twoje miasto to:", "text-domain")  . $miasto . _e(" a kod pocztowy:", "text-doamin") . $kod . _e(" Zobacz mapkę: <a href="google.pl/mapy/etc", "text-domain") ;

Zarówno jeden jak i drugi przykład jest niedozwolony a drugi szczególnie długi.

Dlatego o wiele lepiej jest stosować funkcję printf i podstawiać zmienne używając znaczników konwersji :

printf ( __('Twoje miasto to: %1$s a kod pocztowy: %2$s. Zobacz mapkę: <a href="%3$s">KLIK</a>.', 'text-domain'), $miasto, $kod, $link);

Czy widzisz tą różnice?
W efekcie końcowym, tłumaczona fraza (w Poedit) zamiast wyglądać tak:

Twoje miasto to: $miasto a kod pocztowy: $kod. Zobacz mapkę: <a href=”google.pl/mapy/etc

będzie wyglądała tak:

Twoje miasto to: %1$s a kod pocztowy: %2$s. Zobacz mapkę: <a href=”%3$s”>Kliknij tutaj</a>.

Pisałem wcześniej, że zabrania się używania znaczników HTML. Oczywiście sytuacje są różne i czasami wyjątkowo, w zależności od kontekstu, zajdzie potrzeba, aby w tekście umieścić link. Nie jest to przestępstwo, kiedy również i kotwica ma być możliwa do tłumaczenia. Wtedy taki wyjątek dopuszcza się. Jednak, żeby praktyce stało się zadość, staraj się całkowicie je wyeliminować. Znając zasadę podstawiania i fakt jak prosto działa funkcja printf, można całkowicie pozbyć się HTML’a z tekstów do tłumaczenia:

printf ( __('Zobacz mapkę: %s',  'text-domain'), $link);

lub:

printf ( __('Zobacz mapkę: %s',  'text-domain'), '<a href="google.pl/mapy/etc');

A właściwie to powinno być tak:

printf ( __('Zobacz mapkę: %s',  'text-domain'), 
esc_html__('<a href="google.pl/mapy/etc">Kliknij tu</a>') );

lub jeszcze lepiej tak:

printf ( __('Zobacz mapkę: <a href="%s">Kliknij tutaj</a>',  'text-domain'), 
esc_url('google.pl/mapy/etc') );

 

Czy zauważyłeś w tym jeszcze coś dziwnego?

Kiedy w tekście musimy podstawić tylko jedną zmienną, używamy wieloznacznika: %s. Litera s po znaku procent oznacza typ zmiennej.

s = string

Więcej na ten temat przeczytasz w manualu PHP i na stronie w3shool.com

Kiedy jednak musimy podstawić więcej niż jedną zmienna, ich kolejność określamy stosując tego typu zapis: %1$s, %2$s, %3$s itd. co odpowiada kolejności ustawionych po sobie „zmiennych”, tuż po zamknięciu funkcji __():

printf ( 
__('Zmienne: %3$s, %2$s, %1$ i jeszcze raz: %2$s', 'text-domain'),
 $jeden, $dwa, $trzy );

Dzięki temu, możemy naprzemiennie ustawiać zmienne w tekście, bez obaw, że przejmą one niewłaściwą wartość. Analogicznie postępujemy z funkcją sprintf, kiedy chcemy przekazać tłumaczony tekst do zmiennej, lub tłumaczony tekst jest częścią tablicy (przykład jak w przypadku register sidebars).

 

Spostrzeżenie/łamigłówka Jak myślisz. dlaczego w tym przypadku użyłem funkcji __() a nie jak to powinno być _e()?

 

Jeszcze kilka przykładów z funkcji podstawowych.

Funkcje gettext byłyby bezużyteczne, gdyby nie dały się odmieniać przez przypadki. W każdym języku występują różne rodzaje, formy, typy ciągów znaków Jednym z nich jest liczba mnoga.

 

Liczba mnoga


Jest nieoderwalnym elementem chociażby komentarzy. Na pewno bardzo dobrze znasz tego typu sytuację kiedy masz:

Do przygotowania tekstu, którego sposób działania zależy od danych z zewnątrz czyli liczby ( komentarzy) również używa się specjalnie przygotowanych funkcji:

Przykład z użyciem hybrydy funkcji _n() i _x() czyli liczba mnoga + kontekst.

<?php _nx( $single, $plural, $number, $context, $domain ) ?>

która w praktyce wygląda tak:

<?php
 printf( _nx( 
'%1$s response to: %2$s',
 '%1$s responses to: %2$s', get_comments_number(), 'comments title', 'wp-less-is-more' ),
 '<span class="badge">' . number_format_i18n( get_comments_number() ) . '</span>',
 '&ldquo;' . get_the_title() . '&ldquo;' );
 ?>

 

Jak widzisz mamy tu liczbę pojedynczą: %1$s response to: %2$s

i mamy liczbę mnogą: %1$s responses to: %2$s.

Wieloznaczniki %1$s%2$s mają za zadanie podstawić liczbę komentarzy i tytuł posta (a prawidłowo dla samej tylko liczby komentarzy powinien być znacznik %d). Używamy funkcji printf, aby podstawić zmienne. Tytuł jest dodatkowo zawarty w cudzysłów:  &ldquo; &ldquo;.

Dlaczego na zewnątrz a nie w środku? Dlatego, że lepiej wygląda:

%1$s responses to: %2$s

niż :

%1$s responses to: &ldquo;%2$s&ldquo;

To i tak dużo niepotrzebnych znaczków, od których tylko język się plączę i wzrok zawodzi.

Zauważ, że funkcja obsługuje tylko dwie formy odmiany rzeczownika: „komentarz”. Gdy mamy jeden komentarz i gdy jest ich więcej niż jeden.

Ale przecież w Polskim mamy 3 formy odmiany. Gdzie się ona zatem podziała?

Odpowiedz brzmi: jest zawarta dopiero w pliku językowym. Język angielski jest nieco prostszym językiem. Stąd tylko 2 formy odmiany. Dopiero gdy będziemy przygotowywać tłumaczenie w programie Poedit, do tłumaczenia dopiszemy trzecią wersję.

A co z liczbą zero?

Nawet gdy paczka językowa nie jest stworzona, musimy w jakiś sposób pokazać, że nie ma jeszcze komentarzy. Można by to zrobić na przykład używając warunku:

if ( have_comments() ) {
//pokarz komentarze
else {
//nie ma jeszcze komentarzy }

 

Funkcje ucieczkowe – ecaping


Escaping to proces przetwarzania danych mający na celu zabezpieczenie się przed ich sfałszowaniem lub niepożądanym działaniem.

W tym przypadku funkcje ucieczkowe przetwarzają dany ciąg znaków na bezpieczne encje, co zabezpiecza stronę przed jej upadkiem.

Przykład:

$html = esc_html( '<a href="http://www.example.com/">A link</a>' );

Wynikiem jest ten ciąg znaków:

&lt;a href=&quot;http://www.example.com/&quot;&gt;A link&lt;/a&gt;

Co w dokumencie jest wyświetlane w ten sposób:

<a href="http://www.example.com/">A link</a>

Zamiast tak:

A link

Czy rozumiesz różnice?

Każda z funkcji ucieczkowych zabezpiecza, aby dany ciąg znaków nie został wyświetlony bezpośrednio a zamiast tego niektóre z nich będą zamienione na bezpieczne encje.

Dlaczego jest to takie ważne?

Jednym z argumentów jest fakt, że w języku HTML występują znaczniki do których możemy wpisywać dane. Są to pola input. Gdybyś wprowadził do niego np. podlinkowany tekst i zapisał do bazy danych a potem zwrócił do tego pola zapisaną z bazy wartość, mogłoby to się skończyć załamaniem strony, bowiem znacznik ten mógłby być nieprawidłowo zamknięty.

Dlatego używa się funkcji ucieczkowych, aby pewne dane mogły być bezpiecznie przekazywane pomiędzy funkcjami, i aby nie narobiły bałaganu na stronie, i w bazie danych.

Przykład:

<a href="<?php echo esc_url( $theme_info->get ('AuthorURI')); ?>" target="_blank">Blog</a>
<p><?php printf( __( 'This is you blog name. \n You can change it on %1$s in 
<i><a href="%2$s" target="_blank">Site_title</a></i> field.', 'less-is-more' ),
 $s_i . 'Settings &rarr; General Settings' . $_s_i,
 esc_url( admin_url( 'options-general.php' ) ) ); ?></p>

 

Funkcje numerów i dat


Nie byłoby tu o czym mówić gdyby nie fakt, że WordPressa używa cały świat.

Funkcje numerów i dat zwracają numery i daty według formatu dla danej lokalizacji i takiej jaka została użyta w ustawieniach. Dla języka polskiego używa się innego typu formatu jak dla angielskiego lub odległego chińskiego. Dlatego z kodzie należy używać funkcji numerów i dat, aby nie były one na sztywno określone tylko językiem PHP.

Przykład ich użycia poznałeś już w przypadku omawiania funkcji odmiany komentarzy.

 

Na podsumowanie tej części artykułu należy powtórzyć raz jeszcze.

Wszystkie frazy w motywie, muszą być przygotowane pod tłumaczenie, to znaczy muszą być opakowane w funkcje gettext, aby podczas skanowania przez program tłumaczący mogły zostać wykryte i wciągnięte na listę fraz do przetłumaczenia.

A teraz, jak już wszystko zostało przygotowane jak należy można zabrać się za tłumaczenie fraz. O tym w kolejnej części artykułu, którą znajdziesz na następnej stronie.

Przejdź do:   


Otagowano: , , , , , , , ,

Kategoria: Konfiguracja | Narzędzia | Wtyczki

2 odpowiedzi do: “Tłumaczenie WordPressa (obsługa wielu języków), funkcje gettext i edytor Poedit“  

  • Michał pisze:

    Dziękuję za poruszenie tego tematu. Często tłumaczę wtyczki i motywy, a ponieważ nie jestem programistą i nie znam php, mam nieraz duże kłopoty z poprawianiem w kodzie błędów związanych z i18n. Czekam na drugą część artykułu. Warto może w niej poruszyć kwestię odpowiedniego przygotowania pliku .pot – mam na myśli na przykład opis nazw funkcji – słów kluczowych w PoEdicie, wytłumaczenie, że np. nie wystarczy podać _n ale trzeba _n:1,2 itp.

    Pozdrawiam
    Michał Maciejewski

    Odpowiedz 

    • Paweł pisze:

      Dziękuję za sugestie @Michał Niewątpliwie poruszę tę kwestię.

      Odpowiedz 


Napisz odpowiedź lub dodaj komentarz


Twój adres e-mail nie będzie opublikowany. Pola oznaczone gwiazdką * są wymagane

Możesz używać tych znaczników HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>