🇬🇧 Go to english version of this post / Przejdź do angielskiej wersji tego wpisu
BiLangPost to mój kolejny, mały i niezbyt skomplikowany projekcik (wiecie jak bardzo takie lubię 😉). Jest to narzędzie, które ułatwia pisanie dwujęzycznych postów. A przyczyną powstania jest to, że w momencie dołączenia do Mastodona postanowiłem, że będę tam pisał zarówno po polsku jak i po angielsku. Językiem angielskim posługuję się dość biegle w mowie i piśmie, ale jestem również fanem automatyzacji wszystkiego tego co może zostać zautomatyzowane, a z tłumaczeniami jest tak, że zawsze lepiej jest poprawić nieidealne tłumaczenie niż napisać coś dwa razy (najpierw w jednym języku, a później w drugim). Z uwagi na powyższe przemyślałem sprawę, siadłem do edytora kodu i stworzyłem przy użyciu języka PHP tytułowe narzędzie.
Zasada działania polega na tym, że wpisujemy wiadomość w języku źródłowym i wskazujemy język docelowy, a BiLangPost tłumaczy nam treść wiadomości źródłowej na docelową i następnie skleja jedną z drugą, dodatkowo to formatując, tak aby powstał z tego gotowy post (albo toot) do publikacji np. w mediach społecznościowych. Cały mechanizm tłumaczący działający pod spodem to DeepL, z którym komunikuję się poprzez API, tak jak to opisałem w tym wpisie. Skoro już jesteśmy przy temacie API DeepL’a to, jak pewnie wiecie z podlinkowanego wpisu, jest ono darmowe do pewnego limitu, po którego przekroczeniu wchodzi już płatny plan, który niestety do najtańszych nie należy. Z uwagi na to, nie byłem w stanie udostępnić swojego prywatnego tokenu API do użytku przez to darmowe narzędzie, bo po prostu mój limit zostałby bardzo szybko wykorzystany, a dla mnie samego wystarcza mi w zupełności ten darmowy pakiet. Stąd każdy kto chce skorzystać z BiLangPost musi używać swojego własnego klucza. Oczywiście podany przez użytkownika klucz nie jest przeze mnie nigdzie zapisywany, czy też wykorzystywany w jakikolwiek inny sposób niż do wykonania pracy BiLangPost, która została mu zlecona w danej sesji.
Jak zawsze zajrzyjmy do kodu
Cały kod jest oczywiście otwarty i dostępny do wglądu na GitHub. Merytoryczną część dotyczącą tłumaczenia, tj. jak prawidłowo komunikować się z API DeepL’a opisałem już na blogu, więc nie będę tego powtarzał. Jednakże jest w kodzie BiLangPost jedna rzecz, którą chciałbym omowić. Jest to mechanizm, który stworzyłem, aby bez użycia ciasteczek i/lub sesji użytkownik nie musiał za każdym razem podawać ręcznie swojego tokenu API i wybierać języków do tłumaczenia. Wymyśliłem to tak, że biorę zmienną przechowującą podany przez użytkownika token API oraz zmienne określające wybrane języki (źródłowy i docelowy) i zapisuje to wszystko do jednej zmiennej rozdzielając średnikami. Następnie wartość tej zmiennej szyfruję przy użyciu funkcji openssl_encrypt() i zapisuję jako globalną zmienną typu $_GET (to ta, która jest przechowywana w adresie URL). W ten sposób użytkownik może użyć przy następnej wizycie tego specjalnego adresu URL, w którym zagnieżdżona jest zmienna z potrzebnymi informacjami, i nie musieć konfigurować wszystkiego od nowa. Jest to rozwiązanie jednocześnie wygodne jak i względnie bezpieczne, bo token API użytkownika nie lata jako adres URL w formacie plaintext (z ang. jawnego tekstu).
Wspomniana wcześniej funkcja openssl_encrypt() przyjmuje 3 główne parametry:
- $data – dane, które mają zostać zaszyfrowane,
- $cipher_algo – deklaracja algorytmu szyfrującego jaki ma zostać użyty (ja wybrałem AES-128-CTR),
- $passphrase – klucz szyfrujący, bez którego odszyfrowanie nie jest możliwe.
Zaszyfrowaną wartość można oczywiście odszyfrować używając lustrzanej funkcji openssl_decrypt(), która przyjmuje analogiczne parametry.
<?php
// Zmienne niezbędne do procesu szyfrowania
$passphrase = "[klucz do zaszyfrowania zmiennej GET z ustawieniami]";
$cipher_algo = "AES-128-CTR";
// Deklaracja zmiennej do przechowywania ustawień
$settings = "";
[...]
?>
<!-- Formularz HTML -->
<form action="/?set=<?php echo $set; ?>" method="post">
[...]
<button type="submit" name="PreparePost" value="PreparePost">Prepare post!</button>
</form>
<?php
[...]
// Jeżeli formularz HTML został wysłany
if(isset($_POST['PreparePost']))
{
// Proces szyfrowania
$settings = $token.";".$lang1.";".$lang2;
$set = openssl_encrypt($settings, $cipher_algo, $passphrase);
}
[...]
// Jeżeli zmienna globalna (GET) set nie jest pusta
if(!empty($_GET['set']))
{
// Proces odszyfrowania
$set = addslashes(strip_tags($_GET['set']));
$decrypted_set = openssl_decrypt($set, $cipher_algo, $passphrase);
$explode = explode(";", $decrypted_set);
$token = $explode[0];
$lang1 = $explode[1];
$lang2 = $explode[2];
}
[...]
?>
Obsługa obszarów tekstowych
Front-end nigdy nie był moją mocną stroną i trochę wstyd się przyznać, ale jestem kompletnie niezaznajomiony z językiem Javascript. W obszarze HTML, PHP czy nawet MySQL poruszam się bez większych oporów, ale JS był dla mnie od zawsze jednym wielkim znakiem zapytania. Jakoś nigdy nie miałem czasu, aby przysiąść i się z nim zapoznać. Niejednokrotnie jest to dla mnie sporym problemem, bo trzeba przyznać, że o Javascript oparte jest 99% Internetu. Za każdym razem, gdy na którejś ze swoich strony muszę zrobić coś napisanego w JS to szukam po prostu podobnych, gotowych rozwiązań, które modyfikuję i uzyskuję to czego potrzebuję. Podobnie było w przypadku BiLangPost, gdzie potrzebowałem nauczyć się obsługi obszarów tekstowych (po ang. Text Area), a konkretnie chciałem zrobić trzy rzeczy:
- powiększać dynamicznie obszar tekstowy, w przypadku gdy wprowadzony tekst przestanie się w nim mieścić,
- zliczać ilość znaków wprowadzonych w obszar tekstowy tak, aby użytkownik na bieżąco widział czy mieści się w limicie znaków, który sobie założył,
- dodać przycisk umożliwiający skopiowanie jednym kliknięciem zawartości całego obszaru tekstowego.
Bazowy kod HTML do modyfikacji:
<textarea
id="textarea"
name="message"
placeholder="Write here in your native language..."
>[Zawartość]</textarea>
<button type="button" name="CopyButton">Copy</button>
Załatwienie tematu samo skalującego się obszaru (powiększanie po przekroczeniu domyślnego rozmiaru) załatwia się dość prosto, bo poprzez dodanie parametru w elemencie <textarea>.
<textarea [...] oninput='this.style.height = "";this.style.height = this.scrollHeight + "px"'></textarea>
Jeżeli dobrze rozumiem zapis to chodzi po prostu o to, że parametr wysokości obszaru tekstowego jest na bieżąco, przy wprowadzaniu kolejnych znaków do obszaru, nadpisywany wartością równą wysokości paska przewijania (po ang. scrollbar).
W przypadku liczenia ilości znaków wewnątrz obszaru stworzyłem funkcję, która inicjuje zmienną counter (z ang. licznik), w której zapisuje aktualną długość ciągu wprowadzonego wewnątrz wskazanego obszaru tekstowego. Wywołanie tej funkcji następuje poprzez wydarzenie (po ang. event) o nazwie onKeyUp, który w prostych słowach oznacza gdy przycisk klawiatury zostanie zwolniony, czyli wydarzenie tego typu występuje, gdy naciśniemy na klawiaturze przycisk i go puścimy (dokładnie w momencie puszczenia). Na koniec musimy jeszcze wyświetlić obliczoną wartość pod obszarem tekstowym.
<textarea onKeyUp="count_it()" [...]></textarea>
<div>Characters: <span id="counter"></span></div>
<script>
function count_it()
{
document.getElementById('counter').innerHTML = document.getElementById('textarea').value.length;
}
count_it();
</script>
Na koniec pozostaje nam jeszcze przycisk do kopiowania zawartości obszaru tekstowego jednym kliknięciem. Skrypt obsługujący tą funkcjonalność składa się z funkcji, w której w pierwszej kolejności definiujemy, o który obszar tekstowy chodzi, wskazując go po jego identyfikatorze (Id). Następnie zawartość obszaru zostaje zaznaczona i następnym poleceniem ta zaznaczona zawartość zostaje skopiowana do schowka użytkownika. Na końcu funkcji znajduje się jeszcze bonus w postaci zmiany tekstu wewnątrz przycisku (etykieta) po jego naciśnięciu. Tekst Copy (z ang. Kopiuj) zmienia się na Copied! (z ang. Skopiowano!). Po zakończeniu pisania skryptu należy jeszcze pamiętać, aby do przycisku dodać parametr id oraz onclick. Ten drugi informuje interpreter o tym co ma się zdarzyć po naciśnięciu przycisku, tj. w tym konkretnym przypadku ma zostać wywołana funkcja copy(), wcześniej przez nas napisana.
<button [...] id="CopyButton" onclick="copy()">Copy</button>
<script>
function copy()
{
let textarea = document.getElementById("textarea");
textarea.select();
document.execCommand("copy");
var btn = document.getElementById("CopyButton");
btn.innerHTML = "Copied!";
}
</script>
BiLangPost czeka aby służyć
Na koniec tego wpisu chciałem tylko jeszcze po raz ostatni zaprosić na stronę dedykowaną narzędziowi BiPangPost!
> https://bilangpost.tomaszdunia.pl <
Przykładowy toot napisany przy użyciu BiLangPost wygląda tak:
Pingback: BiLangPost – tool for publishing bilingual posts [ENG 🇬🇧] – Tomasz Dunia Blog