A program written in php, that does the following: it reads text files from different subdirectories (named fi, fr, en, ru) , where the name is in form of a date e.g. 2025-01-02.txt. in the subdirectories all files have this same name format, but the language, depending on which directory the file is placed, is either finnish english french and russian. But these could be any languages, of course. The program reads in the files, and creates a complete html with all the txt files in chronological order and displays them in the chosen language (existing in context as $lang variable which is also the name of the subdirectory). On the top of each text is the date, which is generated from the standardized txt filename, which is by itself in date format. In the html there is also a line as a seperator. Below the header is a "listen" button, by pressing the button, the text will be read aloud the specific text in the respectively chosen language.
Why this is good: One can add and insert whenever wanted new text files and the web page will automatically updated without any changes to the program itself. The text files are simple unformatted .txt files, which makes them uncomplicated to be generated and transferred to the server by ftp.
The structure of the directory in the server:
π Directory Structure /home/users/CarlCoder/sites/your_site_name/www/letters/
β
βββ letters.php # Main PHP page (loads texts, formats HTML, adds listen buttons)
βββ templates/
β βββ header.php # Includes session handling, language selection, and page metadata
β
βββ en/ # English text files
β βββ 2025-01-02.txt
β βββ 2025-01-09.txt
β βββ ...
β
βββ fi/ # Finnish text files
β βββ 2025-01-02.txt
β βββ 2025-01-09.txt
β βββ ...
β
βββ fr/ # French text files
β βββ 2025-01-02.txt
β βββ 2025-01-09.txt
β βββ ...
β
βββ ru/ # Russian text files
βββ 2025-01-02.txt
βββ 2025-01-09.txt
βββ ...
βοΈ How It Works
Language Selection The active language is stored in a PHP session variable $_SESSION['lang']. It can be changed through a dropdown menu in header.php. The value determines which subdirectory (fi/, en/, fr/, ru/) the text files are read from.
File Naming Convention Each file is named in YYYY-MM-DD.txt format. The filename itself determines the displayed date above the text.
Content Generation
The program reads all .txt files from the selected language folder.
For each file:
A formatted, localized date (dayβmonthβyear order) is displayed as a title.
The file content is shown in a handwriting-style font inside a paragraph.
A horizontal line
separates each text.
A βπ§ Listenβ button appears below each text, which reads the content aloud in the chosen language. Each listening button is individual for every text example speakChild6, where 6 is a running index from 0,1,2,3.... n
π Listen
Technologies Used
PHP 8+
IntlDateFormatter for localized date formatting
JavaScript (for the βlistenβ button, using SpeechSynthesis API in the browser)
HTML + CSS + Google Fonts (Caveat handwriting font)
π Text-to-Speech βListenβ Function
Each displayed letter has its own βListenβ button that allows users to hear the text read aloud in the currently selected language.
Because multiple letters appear on the same page, each one receives a unique JavaScript function β for example:
speakChild0(lang)
speakChild1(lang)
speakChild2(lang)
etc.
These functions are generated by PHP inside the loop that renders each text block. Each function knows which HTML element corresponds to its text and which language to use for speech.
π‘ Example JavaScript (for one letter)
<script> function speakChild6(lang) { // Find the text container for this specific letter const element = document.querySelector(".letter-1970-09-20"); if (!element) return; // Extract the text content const text = element.innerText; // Inner function that actually performs the speech function doSpeak() { let voices = speechSynthesis.getVoices(); // Try to find a voice that matches the chosen language let voice = voices.find(v => v.lang === lang); // Fallback: find a Google voice for the same language code prefix if (!voice) { voice = voices.find( v => v.name.toLowerCase().includes("google") && v.lang.startsWith(lang.split("-")[0]) ); } // Create the utterance const utterance = new SpeechSynthesisUtterance(text); // Assign language and voice if (voice) { utterance.voice = voice; utterance.lang = voice.lang; } else { utterance.lang = lang; // fallback to requested language code } // Stop any current speech before starting new one speechSynthesis.cancel(); speechSynthesis.speak(utterance); } // If voices are not yet loaded (browser async issue) if (speechSynthesis.getVoices().length === 0) { speechSynthesis.onvoiceschanged = doSpeak; } else { doSpeak(); } } </script>π§ How It Works
Each text block is wrapped in an element with a unique class, for example
Each button calls its own function:
π§ Listen
The function:
Reads the elementβs text via innerText
Finds a matching speechSynthesis voice for the active language (fi-FI, fr-FR, etc.)
Cancels any currently playing speech
Speaks the letter content aloud
Works only on desktop browsers (Chrome, Edge, Firefox). Mobile browsers often block the speechSynthesis API.
The available voices depend on the userβs system and browser. Some languages may not have natural-sounding voices.
For the best results, use Google Chrome and enable βGoogleβ voices in your OS language settings.