W poprzednich wpisach opisaliśmy budowę kalendarza w JavaScript oraz jego rozbudowę o dodatkowe funkcje. W pierwszym artykule, Kalendarz w HTML, CSS i jQuery, przedstawiliśmy krok po kroku, jak stworzyć podstawowy kalendarz. Następnie, w Refaktoryzacja i rozbudowa kalendarza w HTML, CSS i jQuery, omówiliśmy, jak ulepszyć ten kalendarz, dodając nowe funkcje i poprawiając jego strukturę.
Teraz zajmiemy się kolejnym etapem rozbudowy kalendarza – dodaniem tłumaczeń pobieranych dynamicznie za pomocą AJAX z API zwracającego dane w formacie JSON. Dzięki temu nasz kalendarz będzie mógł automatycznie dostosowywać się do wybranego przez użytkownika języka, co znacznie zwiększy jego funkcjonalność i dostępność.
Czym jest AJAX, JSON i API?
Przed przystąpieniem do implementacji, warto dokładniej zrozumieć działanie technologii, które będziemy wykorzystywać. AJAX umożliwia nam wykonywanie zapytań do serwera w tle, bez konieczności przeładowywania strony. To sprawia, że aplikacje webowe mogą działać szybciej i bardziej responsywnie.
JSON jest idealnym formatem do przesyłania danych, ponieważ jest zwięzły i łatwy do parsowania. Struktura JSON przypomina obiekty w JavaScript, co ułatwia jego integrację z istniejącym kodem.
API pozwala na komunikację między różnymi aplikacjami, umożliwiając przesyłanie danych i wykonywanie operacji na serwerze. W naszym projekcie, API zwróci odpowiednie tłumaczenia w formacie JSON, które nasz kalendarz pobierze i wykorzysta za pomocą AJAX.
Dzięki wykorzystaniu AJAX, JSON i API, nasz kalendarz będzie mógł pobierać tłumaczenia w locie, co umożliwi użytkownikom natychmiastową zmianę języka bez konieczności odświeżania strony.
Dodanie elementu do zmiany języka
Wprowadzenie dynamicznych tłumaczeń jest kluczowym ulepszeniem w naszym projekcie kalendarza, które znacznie zwiększa jego dostępność i użyteczność dla użytkowników z różnych regionów świata. Do realizacji tego celu, niezbędne jest wprowadzenie elementu interfejsu użytkownika, który umożliwi wybór języka.
W naszym przykładzie, na samym początku pliku script.js
, definiujemy zmienną lang
, która przechowuje aktualnie wybrany język. Domyślnie ustawiona jest na 'pl'
(polski), co odzwierciedla domyślne ustawienie dla naszych użytkowników:
var lang = 'pl'; // Domyślny język
Następnie, w sekcji tworzenia fragmentu dokumentu, dodajemy nowy element div
z identyfikatorem language-panel
. Wewnątrz tego elementu umieszczamy element select
z identyfikatorem change-lang
, który pozwala użytkownikom na wybór języka. Do tego select
dodajemy opcje wyboru języka polskiego oraz angielskiego:
// Wyświetlenie panelu do zmiany języka
fragment.append($('<div>').attr('id', 'language-panel')
.append($('<select>').attr('id', 'change-lang')
.append($('<option>').val('pl').text('Polski'))
.append($('<option>').val('en').text('English'))
.val(lang)));
Element select
nie tylko umożliwia wybór języka, ale także jest kluczowy w obsłudze zmiany języka bez konieczności przeładowania strony. Za pomocą jQuery, możemy dodać obsługę zdarzenia zmiany wybranego elementu, co pozwoli na dynamiczne pobranie odpowiednich tłumaczeń z serwera.
Stylizacja panelu zmiany języka
Oprócz dodania funkcjonalności umożliwiającej zmianę języka, kluczowe jest także zadbanie o estetykę i ergonomię elementu interfejsu. W sekcji CSS projektu dodaliśmy stylizacje, które nie tylko poprawiają wygląd, ale także funkcjonalność elementu do zmiany języka.
#language-panel {
display: flex;
justify-content: flex-end;
align-items: center;
grid-column: span 7;
background-color: darkgrey;
}
#change-lang {
height: 25px;
padding: 5px;
font-size: 12px;
background-color: lightgrey;
border-radius: 5px;
margin-right: 10px;
}
Definicja stylów dla #language-panel
ma na celu umieszczenie go na początku kontenera gridowego kalendarza, zachowując przy tym jego funkcjonalność i dostępność. Ustawienia takie jak display: flex;
, justify-content: flex-end;
oraz align-items: center;
pozwalają na wygodne umiejscowienie elementu w prawym górnym rogu kalendarza, co jest intuicyjne dla użytkownika.
Element #change-lang
, czyli rozwijana lista wyboru języka, została zaprojektowana tak, aby była wygodna w użyciu i jednocześnie estetycznie dopasowana do reszty interfejsu. Wysokość (height: 25px;
), padding (padding: 5px;
), rozmiar czcionki (font-size: 12px;
) oraz kolor tła (background-color: lightgrey;
) są dobrze zbalansowane, co zapewnia wygodę użytkowania, niezależnie od rozmiaru urządzenia. Zaokrąglenie rogów (border-radius: 5px;
) dodaje nowoczesności i łagodzi kształt elementu. Ostatni styl, margin-right: 10px;
, zapewnia odpowiednią przestrzeń od krawędzi kontenera, co ułatwia dostęp do listy rozwijanej.
Implementacja początkowej funkcji zmiany języka bez użycia AJAX
W dalszej części rozbudowy naszego kalendarza dodajemy funkcjonalność dynamicznej zmiany języka. W tej sekcji, zanim użyjemy AJAX-a i stworzymy kod API, wprowadzamy tłumaczenia bezpośrednio w skrypcie JavaScript. Pozwoli to nam zrozumieć mechanizm zmiany języka, zanim zamienimy te dane na pobierane z zewnętrznego źródła.
Definiujemy obiekt translations
, który będzie przechowywać tłumaczenia dla różnych elementów kalendarza, takich jak nazwy miesięcy i dni tygodnia:
var translations = {};
Następnie implementujemy funkcję changeTranslations(lang)
, która będzie ustawiać odpowiednie tłumaczenia w zależności od wybranego języka:
function changeTranslations(lang) {
if (lang === 'pl') {
translations = {
'month_january': 'Styczeń', 'month_february': 'Luty', 'month_march': 'Marzec',
'month_april': 'Kwiecień', 'month_may': 'Maj', 'month_june': 'Czerwiec',
'month_july': 'Lipiec', 'month_august': 'Sierpień', 'month_september': 'Wrzesień',
'month_october': 'Październik', 'month_november': 'Listopad', 'month_december': 'Grudzień',
'day_monday': 'Pon', 'day_tuesday': 'Wto', 'day_wednesday': 'Śro',
'day_thursday': 'Czw', 'day_friday': 'Pią', 'day_saturday': 'Sob', 'day_sunday': 'Nie'
};
} else if (lang === 'en') {
translations = {
'month_january': 'January', 'month_february': 'February', 'month_march': 'March',
'month_april': 'April', 'month_may': 'May', 'month_june': 'June',
'month_july': 'July', 'month_august': 'August', 'month_september': 'September',
'month_october': 'October', 'month_november': 'November', 'month_december': 'December',
'day_monday': 'Mon', 'day_tuesday': 'Tue', 'day_wednesday': 'Wed',
'day_thursday': 'Thu', 'day_friday': 'Fri', 'day_saturday': 'Sat', 'day_sunday': 'Sun'
};
}
generateCalendar(month, year);
}
Przy ładowaniu strony, zamiast bezpośrednio wywoływać generateCalendar(month, year);
, uruchamiamy changeTranslations(lang);
, co pozwala na wczytanie odpowiednich tłumaczeń i wygenerowanie kalendarza:
changeTranslations(lang);
Podmieniamy sztywne nazwy miesięcy i dni tygodnia na dynamiczne tłumaczenia, wykorzystując nowo utworzoną zmienną translations
:
// Tablica z nazwami miesięcy
var monthName = [
translations['month_january'], translations['month_february'], translations['month_march'],
translations['month_april'], translations['month_may'], translations['month_june'],
translations['month_july'], translations['month_august'], translations['month_september'],
translations['month_october'], translations['month_november'], translations['month_december']
];
// Tablica z nazwami dni tygodnia
var dayName = [
translations['day_monday'], translations['day_tuesday'], translations['day_wednesday'],
translations['day_thursday'], translations['day_friday'], translations['day_saturday'],
translations['day_sunday']
];
Dodajemy zdarzenie onchange
dla elementu #change-lang
, które wywołuje funkcję changeTranslations(lang);
przy każdej zmianie języka:
// Onchange do zmiany tłumaczenia
$(document).on('change', '#change-lang', function() {
lang = $(this).val();
changeTranslations(lang);
});
Implementacja dynamicznych tłumaczeń w naszym kalendarzu przebiega etapami. Na początku wprowadziliśmy tłumaczenia bezpośrednio w kodzie, aby zrozumieć mechanizm działania. Następnie dodaliśmy funkcję changeTranslations
, która umożliwia aktualizację kalendarza na podstawie wybranego języka.
Tworzenie API do obsługi tłumaczeń
Po początkowej implementacji funkcji zmiany języka bezpośrednio w kodzie JavaScript, następnym krokiem w rozbudowie naszego kalendarza jest przeniesienie tłumaczeń do bazy danych i stworzenie API, które pozwoli na ich dynamiczne ładowanie za pomocą AJAX. Umożliwi to łatwiejszą aktualizację i zarządzanie tłumaczeniami.
Najpierw musimy stworzyć bazę danych oraz tabelę, która będzie przechowywać tłumaczenia. Oto kwerendy SQL do stworzenia bazy danych o nazwie calendar
oraz tabeli translations
i wypełnienie jej tłumaczeniami polskimi i angielskimi:
CREATE DATABASE calendar;
USE calendar;
CREATE TABLE translations (
id INT AUTO_INCREMENT PRIMARY KEY,
key_name VARCHAR(50) NOT NULL,
lang VARCHAR(10) NOT NULL,
value VARCHAR(255) NOT NULL
);
INSERT INTO translations (key_name, lang, value) VALUES
('month_january', 'pl', 'Styczeń'),
('month_february', 'pl', 'Luty'),
('month_march', 'pl', 'Marzec'),
('month_april', 'pl', 'Kwiecień'),
('month_may', 'pl', 'Maj'),
('month_june', 'pl', 'Czerwiec'),
('month_july', 'pl', 'Lipiec'),
('month_august', 'pl', 'Sierpień'),
('month_september', 'pl', 'Wrzesień'),
('month_october', 'pl', 'Październik'),
('month_november', 'pl', 'Listopad'),
('month_december', 'pl', 'Grudzień'),
('month_january', 'en', 'January'),
('month_february', 'en', 'February'),
('month_march', 'en', 'March'),
('month_april', 'en', 'April'),
('month_may', 'en', 'May'),
('month_june', 'en', 'June'),
('month_july', 'en', 'July'),
('month_august', 'en', 'August'),
('month_september', 'en', 'September'),
('month_october', 'en', 'October'),
('month_november', 'en', 'November'),
('month_december', 'en', 'December'),
('day_monday', 'pl', 'Pon'),
('day_tuesday', 'pl', 'Wto'),
('day_wednesday', 'pl', 'Śro'),
('day_thursday', 'pl', 'Czw'),
('day_friday', 'pl', 'Pią'),
('day_saturday', 'pl', 'Sob'),
('day_sunday', 'pl', 'Nie'),
('day_monday', 'en', 'Mon'),
('day_tuesday', 'en', 'Tue'),
('day_wednesday', 'en', 'Wed'),
('day_thursday', 'en', 'Thu'),
('day_friday', 'en', 'Fri'),
('day_saturday', 'en', 'Sat'),
('day_sunday', 'en', 'Sun');
Następnie tworzymy skrypt PHP, który będzie obsługiwał zapytania do naszego API. Skrypt będzie pobierał tłumaczenia z bazy danych i zwracał je w formacie JSON.
<?php
header("Access-Control-Allow-Origin: *");
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "calendar";
// Połączenie z bazą danych
$conn = new mysqli($servername, $username, $password, $dbname);
// Sprawdzenie połączenia
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
// Pobranie języka z żądania
$lang = isset($_GET['lang']) ? $_GET['lang'] : 'pl';
// Przygotowanie i wysłanie zapytania do bazy
$stmt = $conn->prepare("SELECT key_name, value FROM translations WHERE lang = ?");
$stmt->bind_param("s", $lang);
$stmt->execute();
$result = $stmt->get_result();
// Wypełnienie tablicy tłumaczeniami
$translations = array();
while($row = $result->fetch_assoc()) {
$translations[$row['key_name']] = $row['value'];
}
// Zwrot tłumaczeń JSONem z opcją JSON_UNESCAPED_UNICODE
echo json_encode($translations, JSON_UNESCAPED_UNICODE);
$stmt->close();
$conn->close();
?>
Ten skrypt PHP łączy się z bazą danych MySQL przy użyciu podanych parametrów: nazwy serwera ($servername
), nazwy użytkownika ($username
), hasła ($password
) i nazwy bazy danych ($dbname
). Po nawiązaniu połączenia, skrypt sprawdza, czy nie wystąpił błąd podczas łączenia się z bazą danych. Jeśli połączenie się nie powiedzie, skrypt zwraca komunikat o błędzie i przerywa działanie.
Następnie skrypt pobiera parametr lang
z żądania GET, który określa, w jakim języku mają być zwrócone tłumaczenia. Jeśli parametr lang
nie jest podany, domyślnie ustawiany jest język polski ('pl'
).
Kolejnym krokiem jest przygotowanie zapytania SQL, które selekcjonuje odpowiednie tłumaczenia z tabeli translations
na podstawie podanego języka. Skrypt przygotowuje zapytanie SQL (SELECT key_name, value FROM translations WHERE lang = ?
) i wiąże parametr języka ($lang
) z zapytaniem przy użyciu metody bind_param
. Następnie wykonuje zapytanie za pomocą metody execute
.
Po wykonaniu zapytania skrypt pobiera wyniki i zapisuje je w tablicy asocjacyjnej $translations
, gdzie klucze to nazwy kluczy tłumaczeń (key_name
), a wartości to odpowiednie tłumaczenia (value
).
Na końcu skrypt koduje tablicę $translations
do formatu JSON i zwraca ją jako odpowiedź. Skrypt zamyka połączenie z bazą danych, wywołując metody close
dla zapytania ($stmt
) i połączenia ($conn
).
Testowanie API
Najlepszym sposobem na testowanie tego projektu jest użycie lokalnego serwera, takiego jak XAMPP. XAMPP umożliwia uruchamianie serwera Apache z obsługą PHP i MySQL, co jest idealne do testowania aplikacji webowych.
- Pobierz i zainstaluj XAMPP: Możesz pobrać XAMPP ze strony apachefriends.org.
- Skonfiguruj XAMPP:
- Umieść pliki projektu w katalogu
htdocs
w folderze instalacyjnym XAMPP (zazwyczajC:\xampp\htdocs
). - Uruchom serwer Apache i MySQL za pomocą panelu kontrolnego XAMPP.
- Umieść pliki projektu w katalogu
- Stwórz bazę danych:
- Otwórz
phpMyAdmin
(dostępny podhttp://localhost/phpmyadmin
). - Wykonaj kwerendę SQL do stworzenia bazy danych i tabeli z tłumaczeniami, jak opisano powyżej.
- Otwórz
Jeśli używasz XAMPPa możesz przetestować działanie naszego API wpisując w przeglądarce adres: http://localhost/lang.php powinieneś otrzymać JSONa z polskim tłumaczeniem (dostępne także w taki sposób: http://localhost/lang.php?lang=pl):
{"month_january":"Styczeń","month_february":"Luty","month_march":"Marzec","month_april":"Kwiecień","month_may":"Maj","month_june":"Czerwiec","month_july":"Lipiec","month_august":"Sierpień","month_september":"Wrzesień","month_october":"Październik","month_november":"Listopad","month_december":"Grudzień","day_monday":"Pon","day_tuesday":"Wto","day_wednesday":"Śro","day_thursday":"Czw","day_friday":"Pią","day_saturday":"Sob","day_sunday":"Nie"}
Natomiast angielskie tłumaczenie uzyskamy pod tym samym adresem ale z parametrem lang=en: http://localhost/lang.php?lang=en
Opisaliśmy, jak stworzyć API w PHP, które dynamicznie zwraca tłumaczenia z bazy danych MySQL. Dzięki temu nasz kalendarz za chwilę będzie mógł dynamicznie pobierać tłumaczenia za pomocą AJAX, co znacznie zwiększa jego elastyczność i dostępność.
Implementacja dynamicznego ładowania tłumaczeń za pomocą AJAX
Po stworzeniu API w PHP, które zwraca tłumaczenia z bazy danych w formacie JSON, możemy teraz zmodyfikować nasz skrypt JavaScript, aby korzystał z tego API. Zastąpimy statyczne tłumaczenia w funkcji changeTranslations
na dynamiczne ładowane za pomocą AJAX.
Zamiast przechowywać tłumaczenia bezpośrednio w kodzie JavaScript, będziemy teraz ładować je dynamicznie z naszego API za pomocą AJAX. Dodamy również obsługę błędów, aby informować użytkownika w przypadku problemów z pobieraniem tłumaczeń.
Zaktualizowana funkcja changeTranslations
:
function changeTranslations(lang) {
$.ajax({
url: 'http://localhost/lang.php',
data: { lang: lang },
dataType: 'json',
success: function(data) {
translations = data;
generateCalendar(month, year);
},
error: function(jqXHR, textStatus, errorThrown) {
alert("Wystąpił błąd podczas pobierania tłumaczeń: " + textStatus);
console.error("Error details:", errorThrown);
}
});
}
Funkcja changeTranslations(lang)
pobiera tłumaczenia w zależności od wybranego języka za pomocą zapytania AJAX. Wewnątrz funkcji wywoływana jest metoda $.ajax
, która wysyła żądanie GET na adres http://localhost/lang.php
z parametrem lang
, określającym język tłumaczeń. Parametr dataType
jest ustawiony na json
, co oznacza, że oczekujemy odpowiedzi w formacie JSON.
Jeśli żądanie zakończy się sukcesem, funkcja anonimowa przypisana do klucza success
zostanie wywołana z pobranymi danymi. Dane te są przypisywane do zmiennej translations
, a następnie wywoływana jest funkcja generateCalendar(month, year)
, aby zaktualizować kalendarz z nowymi tłumaczeniami.
W przypadku, gdy zapytanie nie powiedzie się, funkcja anonimowa przypisana do klucza error
zostanie wywołana. W tej funkcji błąd jest logowany w konsoli za pomocą console.error
, a użytkownikowi wyświetlany jest komunikat alertujący o niepowodzeniu ładowania tłumaczeń i sugerujący ponowną próbę później.
Teraz możemy przetestować naszą aplikację, aby upewnić się, że tłumaczenia poprawnie ładują się z API i wyświetlają w kalendarzu. Poniżej znajduje się pełny kod JavaScript z aktualizacją funkcji changeTranslations
.
$(document).ready(function() {
// Dane startowe
var date = new Date();
var month = date.getMonth() + 1; // Pobranie aktualnego miesiąca i korekta indeksu (getMonth zwraca miesiące od 0 do 11)
var year = date.getFullYear(); // Pobranie aktualnego roku w formacie czterocyfrowym
var lang = 'pl'; // Domyślny język
var translations = {};
changeTranslations(lang);
// Funkcja do pobierania tłumaczeń z api i ich zmiany
function changeTranslations(lang) {
$.ajax({
url: 'http://localhost/lang.php',
data: { lang: lang },
dataType: 'json',
success: function(data) {
translations = data;
generateCalendar(month, year);
},
error: function(xhr, status, error) {
console.error('AJAX Error:', status, error);
alert('Nie udało się załadować tłumaczeń. Spróbuj ponownie później.');
}
});
}
function generateCalendar(month, year) {
var day = date.getDate(); // Pobranie aktualnego dnia miesiąca
// Obliczenie pierwszego dnia miesiąca
var startDay = new Date(year, month - 1, 1).getDay(); // .getDay() zwraca indeksy dni tygodnia 0-6, gdzie 0 to niedziela
if (startDay === 0) { startDay = 7; } // Zamiana numeru dla niedzieli na format 1-7
// Pobranie maksymalnego dnia w miesiącu
var maxDay = new Date(year, month, 0).getDate();
// Tablica z nazwami miesięcy
var monthName = [
translations['month_january'], translations['month_february'], translations['month_march'],
translations['month_april'], translations['month_may'], translations['month_june'],
translations['month_july'], translations['month_august'], translations['month_september'],
translations['month_october'], translations['month_november'], translations['month_december']
];
// Tworzenie fragmentu dokumentu
var fragment = $(document.createDocumentFragment());
// Wyświetlenie panelu do zmiany języka
fragment.append($('<div>').attr('id', 'language-panel')
.append($('<select>').attr('id', 'change-lang')
.append($('<option>').val('pl').text('Polski'))
.append($('<option>').val('en').text('English'))
.val(lang)));
// Wyświetlanie nazwy miesiąca i roku oraz elementów nawigacyjnych
fragment.append($('<div>').text("<").addClass('nav prev'));
fragment.append($('<div>').text(monthName[month - 1] + " " + year).addClass('month-year'));
fragment.append($('<div>').text(">").addClass('nav next'));
// Tablica z nazwami dni tygodnia
var dayName = [
translations['day_monday'], translations['day_tuesday'], translations['day_wednesday'],
translations['day_thursday'], translations['day_friday'], translations['day_saturday'],
translations['day_sunday']
];
// Wyświetlenie nazw dni tygodnia z odpowiednią klasą dla weekendu
for (let i = 0; i <= 6; i++) {
divDay = $('<div>').text(dayName[i]).addClass('day-name');
if (i === 5) { divDay.addClass('saturday'); }
if (i === 6) { divDay.addClass('sunday'); }
fragment.append(divDay);
}
// Wyświetlenie pustych dni na początku miesiąca
for (let i = 1; i < startDay; i++) {
divDay = $('<div>').addClass('none');
fragment.append(divDay);
}
// Wyświetlenie dni miesiąca
for (let i = 1; i <= maxDay; i++) {
var ii = i + startDay - 1;
divDay = $('<div>').text(i);
if (i === day && month === (new Date()).getMonth() + 1 && year === (new Date()).getFullYear()) {
divDay.addClass('today');
} else {
if (ii % 7 === 0) { divDay.addClass('sunday'); }
if (ii % 7 === 6) { divDay.addClass('saturday'); }
}
fragment.append(divDay);
}
// Dodanie całego fragmentu do elementu #calendar
$("#calendar").empty().append(fragment);
}
// Onclick dla przycisków nawigujących kalendarzem
$(document).on('click', '.nav', function() {
if ($(this).hasClass('prev')) {
month--;
if (month < 1) {
month = 12;
year--;
}
} else if ($(this).hasClass('next')) {
month++;
if (month > 12) {
month = 1;
year++;
}
}
generateCalendar(month, year);
});
// Onchange do zmiany tłumaczenia
$(document).on('change', '#change-lang', function() {
lang = $(this).val();
changeTranslations(lang);
});
});
Zakończenie
Implementacja dynamicznego ładowania tłumaczeń za pomocą AJAX znacząco podnosi funkcjonalność i dostępność naszego kalendarza. Dzięki wykorzystaniu technologii takich jak AJAX, JSON i API, nasza aplikacja staje się bardziej responsywna i przyjazna dla użytkownika, pozwalając na łatwe dostosowanie języka interfejsu w locie.
Podsumowując, dzięki integracji dynamicznych tłumaczeń nasza aplikacja kalendarza staje się bardziej wszechstronna i dostępna dla użytkowników z różnych części świata. Kontynuując rozwój projektu, możemy dodać jeszcze więcej funkcji i usprawnień, które sprawią, że nasz kalendarz będzie jeszcze bardziej funkcjonalny i przyjazny dla użytkownika.
Zobacz commit na GitHub i zapoznaj się z pełnym kodem projektu, aby dokładniej zrozumieć, jak wszystkie elementy współpracują ze sobą. Dziękuję za śledzenie cyklu artykułów o rozbudowie kalendarza w HTML, CSS i jQuery.
Linki do poprzednich wpisów:
Zapraszam do śledzenia kolejnych wpisów, w których będę omawiać kolejne zaawansowane techniki i narzędzia do tworzenia nowoczesnych aplikacji webowych.