Kwota, która zmienia się po zmianie języka, data wyświetlana w niewłaściwym dniu lub tłumaczenie zmieniające regułę biznesową zwykle wskazują na ten sam problem: aplikacja myli dane z decyzjami dotyczącymi prezentacji. Internacjonalizacja dat i walut w PHP wymaga osobnego traktowania języka, ustawień regionalnych, waluty i strefy czasowej oraz określenia, jaką wartość zachowuje system, a jaką reprezentację widzi każdy użytkownik.
Taki podział ułatwia dodawanie kolejnych regionów bez powielania reguł biznesowych ani ponownej interpretacji danych historycznych. Pomaga też ustalić źródło błędów: może nim być dane wejściowe użytkownika, trwałe przechowywanie danych, konwersja strefy czasowej albo wyłącznie format wyjściowy.
Język, locale, waluta i strefa czasowa to odrębne decyzje

Język określa tekst interfejsu i treści podlegające tłumaczeniu, na przykład etykiety, komunikaty walidacyjne i nazwy produktów. Locale, czyli ustawienia regionalne, wskazuje konwencje prezentacji, takie jak separatory dziesiętne, kolejność elementów daty i nazwy miesięcy. Nie zawsze jest równoważny językowi: dwa regiony posługujące się tym samym językiem mogą używać różnych formatów.
Waluta określa jednostkę, w której wyrażona jest kwota, na przykład EUR lub JPY. Nie można jej wiarygodnie wywnioskować na podstawie języka ani ustawień regionalnych. Użytkownik może korzystać z interfejsu po hiszpańsku, używać określonego formatu regionalnego i dokonać zakupu w innej walucie. Z kolei strefa czasowa określa relację między czasem lokalnym a czasem uniwersalnym oraz zmianami czasu w danym regionie.
Warto jawnie modelować te preferencje. Konto może na przykład mieć przypisany język i strefę czasową, sesja może ustalać preferencje dotyczące formatu, a każde zamówienie powinno zachowywać faktycznie używaną walutę. Obsługiwane wartości i ich wartości domyślne powinny wynikać z decyzji produktowych, a nie z cichych wniosków opartych na lokalizacji sieciowej.
Co przechowywać, a co obliczać na potrzeby wyświetlania
Zapisuj dane potrzebne do odtworzenia pierwotnego zdarzenia, a nie tylko sformatowany już ciąg znaków. Data utworzenia oznacza konkretny moment; zapis taki jak „03/04/2025” jest niejednoznaczny i nie nadaje się na kanoniczną reprezentację. W przypadku zdarzeń globalnych często praktyczne jest przechowywanie chwili w UTC oraz zachowanie odpowiedniej strefy czasowej, jeśli wymaga tego kontekst, na przykład przy spotkaniu umówionym w konkretnym mieście.
W aplikacji PHP DateTimeImmutable pomaga uniknąć przypadkowych modyfikacji podczas konwersji. Przygotowując odpowiedź, konwertuj chwilę do wybranej strefy czasowej, nie zastępując zapisanej wartości. Używaj rozpoznawalnych identyfikatorów stref czasowych, takich jak Europe/Madrid, zamiast zapisywać stałe przesunięcie, na przykład +01:00: przesunięcie nie uwzględnia reguł historycznych ani zmian sezonowych.
W przypadku formatowania regionalnego zachowuj etykietę locale obsługiwaną przez używaną bibliotekę i środowisko, a nieobsługiwane preferencje obsługuj jawnie. Funkcje rozszerzenia intl, takie jak IntlDateFormatter i NumberFormatter, pozwalają stosować konwencje lokalne bez ręcznego łączenia separatorów i nazw miesięcy. Sprawdź, czy rozszerzenie jest dostępne w środowiskach, w których działa aplikacja.
Daty i godziny: konwersja oraz niejednoznaczne przypadki
Chwile zarejestrowane przez system i czas lokalny nie są zamienne. Chwila taka jak „2025-04-10T15:00:00Z” wskazuje jednoznaczny punkt w czasie. Z kolei zapis „2025-10-26 o 02:30 w Europe/Madrid” może być niejednoznaczny podczas przejścia na czas zimowy, ponieważ taka godzina lokalna może wystąpić dwukrotnie. Przy wiosennej zmianie czasu mogą z kolei wystąpić godziny lokalne, które w ogóle nie istnieją.
Aby zaplanować działanie na przyszłą godzinę lokalną, nie zapisuj wyłącznie raz obliczonego timestampu, jeśli zobowiązanie dotyczy czasu lokalnego w danym regionie. Zachowaj żądaną datę i godzinę, strefę czasową oraz zasady rozstrzygania nieistniejących lub powtórzonych godzin. Wybór — odrzucenie, prośba o doprecyzowanie lub wskazanie jednego z wystąpień — zależy od produktu. W przypadku zdarzenia, które już nastąpiło, zapisuj natomiast faktyczny moment.
Określ również, jak interpretujesz daty otrzymywane z formularzy i API. Akceptuj udokumentowane formaty, weryfikuj strefę czasową i odrzucaj niejednoznaczne dane zamiast zgadywać, czy „04/05/2025” oznacza kwiecień, czy maj. W interfejsie wyświetlaj czytelną reprezentację, ale zachowuj dane kanoniczne do sortowania, porównywania i audytu.
Kwoty: precyzja, waluta i formatowanie
Widoczny format nie powinien być źródłem prawdy finansowej. Unikaj używania binarnych liczb zmiennoprzecinkowych do obliczeń pieniężnych: niektórych ułamków dziesiętnych nie da się przedstawić dokładnie, co może prowadzić do błędów zaokrągleń. Powszechną strategią jest przechowywanie kwot jako całkowitych wartości w jednostkach podrzędnych wraz z kodem waluty. Nie wszystkie waluty mają jednak dwa miejsca po przecinku, a niektóre obliczenia wymagają większej precyzji pośredniej niż końcowa pobierana kwota.
Określ precyzję i moment zaokrąglania zgodnie z regułami domeny: dla każdej pozycji, podatku czy całej kwoty. W zamówieniach, płatnościach, zwrotach i obliczeniach zawsze jawnie określaj walutę; nie interpretuj liczby bez wiedzy, do jakiej waluty należy. Jeśli przeliczasz waluty, zachowaj również dane potrzebne do wyjaśnienia przeliczenia, takie jak zastosowany kurs i moment odniesienia, jeśli mają znaczenie dla operacji.
Podczas prezentowania kwoty przekaż jej wartość liczbową i kod waluty do odpowiedniego formattera regionalnego. Reprezentacja może różnić się symbolami, kolejnością i separatorami. Użytkownik powinien móc rozpoznać walutę bez polegania na symbolu, który może być niejednoznaczny. Nie analizuj zlokalizowanego ciągu znaków tak, jakby był uniwersalną liczbą: zweryfikuj dane wejściowe i przed przetwarzaniem przekształć je do kontrolowanej reprezentacji liczbowej.
Tłumaczenie treści bez powielania reguł biznesowych
Tłumacz teksty i dostosowuj formaty, ale utrzymuj w jednym miejscu reguły określające ceny, podatki, spełnianie kryteriów, uprawnienia lub stany. Powielanie logiki dla poszczególnych języków prowadzi do rozbieżnych zachowań, które trudno wykryć. Wybór taryfy może zależeć od rynku, umowy lub polityki handlowej; nie powinien zmieniać się wyłącznie dlatego, że zmieniła się etykieta interfejsu.
Oddziel katalogi tłumaczeń od logiki domenowej. W przypadku edytowalnych i lokalizowanych treści określ, jakie wersje mogą istnieć, jak rozwiązywać brakujące wersje i jakie zachowanie przewiduje polityka zastępcza. Przetłumaczona nazwa nie powinna zastępować stabilnego identyfikatora. W API udokumentuj, czy pola zawierają wartości kanoniczne, zlokalizowane treści czy formaty przygotowane do wyświetlenia.
Testy i stopniowe wdrażanie obsługi regionalnej
Testuj poszczególne decyzje na różnych poziomach. Testy jednostkowe powinny weryfikować konwersje między strefami czasowymi, walidację dat, zaokrąglanie i formatowanie kwot. Uwzględnij przypadki na granicy dnia, zmiany czasu sezonowego, powtórzone lub nieistniejące godziny, miesiące o różnej długości oraz waluty o różnej precyzji. Nie polegaj na domyślnym locale ani strefie czasowej komputera testowego — ustawiaj je jawnie.
Testy integracyjne powinny potwierdzać, że API zapisuje dane kanoniczne, a interfejs prezentuje je zgodnie z uzgodnionymi preferencjami. Sprawdź również nieprawidłowe dane wejściowe, nieznane locale, zmiany preferencji i brak wymaganego rozszerzenia. Jeśli aktualizowane są bazy stref czasowych, zweryfikuj procesy planujące przyszłe zdarzenia oraz oczekiwane wyniki.
Wprowadzaj kolejne regiony stopniowo: najpierw zdefiniuj dane i reguły, potem przetestuj formaty i kompletne przepływy, a na końcu udostępnij rozwiązanie docelowym odbiorcom. Monitorowanie błędów walidacji, różnic w zaokrąglaniu i zadań wykonywanych poza właściwą porą pomaga wykryć problemy niewidoczne na zrzucie ekranu.
Lista kontrolna

- Język: Czy jest oddzielony od locale i czy określono politykę zastępczą dla języka?
- Daty: Czy rozróżnia się chwilę w czasie od zaplanowanej godziny lokalnej?
- Strefa czasowa: Czy zapisywane są regionalne identyfikatory stref i czy rozstrzygane są niejednoznaczne godziny?
- Kwoty: Czy każda kwota ma jawnie określoną walutę i zdefiniowane reguły precyzji?
- Prezentacja: Czy formatowanie odbywa się za pomocą odpowiednich narzędzi i czy widoczny tekst nigdy nie jest używany do obliczeń?
- Testy: Czy obejmują granice dni, zmiany czasu, nieprawidłowe dane wejściowe i różne preferencje?
- Operacja: Czy weryfikowana jest konfiguracja PHP i czy obsługa regionalna jest wdrażana w kontrolowany sposób?
Kluczowe jest zachowanie wyraźnej granicy między danymi rozumianymi przez system a sposobem, w jaki widzi je każda osoba. Dzięki temu internacjonalizacja dat i walut w PHP staje się zestawem reguł, które można sprawdzić, zamiast zbiorem wyjątków rozsianych po aplikacji.



