Cursalia is multilingual: every course, every button, and every message exists in Spanish, Catalan, and English (es/ca/en), and the student switches languages with a switcher in the header. That multilingualism, which looks like a matter of translation, has an accessibility side that almost nobody attends to and that directly affects Marta. When her NVDA screen reader encounters a text, it needs to know which language to read it in to use the correct voice and pronunciation: an English text read with Spanish phonetic rules sounds like incomprehensible gibberish. And the visible text isn't enough: alt text, aria-labels, dates, text direction… everything has its nuance. In this lesson we connect accessibility and language, drawing on WCAG criteria we had so far only named (3.1.1 and 3.1.2) and on Cursalia's language switcher as our common thread.
Contents
- Internationalization (i18n) and localization (l10n): the difference
- The
langattribute on<html>(WCAG 3.1.1) langon fragments in another language (WCAG 3.1.2)- Text direction:
dirand logical CSS properties - Translating what you can't see too:
alt,aria-label,title, status messages - Localized formats: dates, numbers, and currency
- Multimedia by language: subtitles and transcripts
- Text in images and cultural considerations
- Common mistakes and tips
- Exercises
- Conclusion
- Internationalization (i18n) and localization (l10n): the difference
Two terms that get confused constantly:
| Term | What it is | Example in Cursalia |
|---|---|---|
| Internationalization (i18n) | Designing the application so that it can adapt to several languages and regions, without touching the code | Extracting all text into translation files; not embedding strings in the HTML |
| Localization (l10n) | Adapting the application to specific languages/regions | Translating into Catalan, formatting dates in the local style, adjusting iconography |
i18n is the infrastructure; l10n is each specific adaptation. Our focus here isn't how to build that infrastructure (that's architecture), but how to make the result accessible in each language. And it all starts with telling the machine which language each thing is in.
- The
lang attribute on <html> (WCAG 3.1.1)
lang attribute on <html> (WCAG 3.1.1)The WCAG 3.1.1 (Language of Page, level A) criterion requires that the primary language of each page be declared programmatically. This is done with the lang attribute on the <html> tag:
This is critical for Marta. With lang="ca", NVDA loads the Catalan voice and pronunciation rules; without it (or with a wrong lang), it would read Catalan with the system's default voice, producing an unintelligible sound. It also affects: font selection, hyphenation, typographic quotation marks, and spell checkers.
In Cursalia, the lang of <html> must change with the language switcher. If the student switches from Spanish to English, translating the text is not enough: you also have to update the attribute:
// When using Cursalia's language switcher
function changeLanguage(code) { // 'es' | 'ca' | 'en'
document.documentElement.setAttribute('lang', code);
// …and load that language's translations
}Always use correct BCP 47 codes: es, ca, en (and regional variants when they matter, such as en-GB or es-MX). A lang="english" or lang="castellano" is not valid and the reader ignores it.
lang on fragments in another language (WCAG 3.1.2)
lang on fragments in another language (WCAG 3.1.2)The WCAG 3.1.2 (Language of Parts, level AA) criterion goes a step further: when a fragment in another language appears within a page in one language, that fragment must be marked with its own lang. This is extremely common in Cursalia: a course page in Spanish that cites the original title in English, or a foreign technical term.
<!-- Page in Spanish with a quote in English -->
<p lang="es">
El curso se inspira en la charla
<span lang="en">The Future of Accessible Design</span>,
muy recomendable.
</p>Without the lang="en" on the <span>, NVDA would read "The Future of Accessible Design" with Spanish phonetics: something like "te futur of accesible desing". With it, it momentarily switches to the English voice, reads the phrase correctly, and returns to Spanish. It's exactly what a bilingual person would do.
Compare:
<!-- Bad: the English name will be read with Spanish phonetics -->
<p lang="es">Hemos añadido un curso de <strong>machine learning</strong>.</p>
<!-- Good: the foreign fragment is marked -->
<p lang="es">Hemos añadido un curso de <strong lang="en">machine learning</strong>.</p>Practical nuance: you don't need to mark words that have already been fully absorbed into the language (established loanwords such as "software" or "email" in Spanish). The criterion targets fragments that are in another language, not every everyday foreign word. Apply common sense: if a human reader would switch accent to read it, mark it.
- Text direction:
dir and logical CSS properties
dir and logical CSS propertiesAlthough es/ca/en are written left-to-right (LTR), thinking about internationalization means preparing the ground for right-to-left (RTL) languages such as Arabic or Hebrew, in case Cursalia expands. The dir attribute declares the direction:
<html lang="ar" dir="rtl"> <!-- Arabic: right to left -->
<html lang="es" dir="ltr"> <!-- Spanish: left to right -->The classic mistake is laying out with physical CSS properties (margin-left, padding-right, left), which end up reversed in RTL. The modern solution is logical properties, which refer to the start and end of the reading flow and adapt to the direction on their own:
/* Bad: physical, doesn't adapt to RTL */
.course-card { margin-left: 1rem; text-align: left; }
/* Good: logical, correct in LTR and RTL without changing anything */
.course-card { margin-inline-start: 1rem; text-align: start; }| Physical property | Equivalent logical property |
|---|---|
margin-left |
margin-inline-start |
padding-right |
padding-inline-end |
left / right |
inset-inline-start / inset-inline-end |
text-align: left |
text-align: start |
border-left |
border-inline-start |
Using logical properties is also an accessibility matter: it ensures that the visual order matches the reading order in any language, something Sofía (low vision, who follows the text with the magnifier) especially appreciates.
- Translating what you can't see too:
alt, aria-label, title, status messages
alt, aria-label, title, status messagesThis is the most-forgotten point. When Cursalia is translated, the visible text gets translated… and the text for assistive technologies is left in the original language. Result: Marta, with Cursalia in Catalan, hears buttons and descriptions in Spanish. All text intended for people—visible or not—must be translated:
| Element | Often forgotten | Must be translated |
|---|---|---|
Image alt |
Yes | Yes (Module 5) |
aria-label / aria-labelledby |
Yes | Yes |
title on icons and links |
Yes | Yes |
| Live region messages (07-01) | Very often | Yes |
| Subtitle text | Sometimes | Yes (see §7) |
<!-- Bad: the aria-label was left in Spanish with the interface in English -->
<html lang="en">
<button aria-label="Añadir a favoritos" class="fav-btn">★</button>
<!-- Good: the accessible name matches the interface language -->
<html lang="en">
<button aria-label="Add to favourites" class="fav-btn">★</button>And very importantly, going back to 07-01: live region messages are translated too. The "12 courses found", the "Enrollment complete!", or the "Page loaded" from the route announcer in 07-02 must come from the translation system, not be hard-coded strings. If Marta uses Cursalia in English, she should hear "12 courses found", not "12 cursos encontrados".
- Localized formats: dates, numbers, and currency
A date like 07/06/2026 is ambiguous: July 6 or June 7? It depends on the local convention. And for Marta, how that format is read aloud also changes. The solution is not to format by hand, but to use the browser's Intl API, which produces the correct format per language:
const date = new Date('2026-07-06');
// Spanish: "6 de julio de 2026"
new Intl.DateTimeFormat('es', { dateStyle: 'long' }).format(date);
// English (GB): "6 July 2026"
new Intl.DateTimeFormat('en-GB', { dateStyle: 'long' }).format(date);
// Course price in euros, Spanish format: "49,99 €"
new Intl.NumberFormat('es', { style: 'currency', currency: 'EUR' }).format(49.99);For maximum clarity with screen readers, pair readable dates with a <time> element carrying a machine-format datetime, so the exact and unambiguous date is available:
<!-- Visible and localized + unambiguous machine value -->
<time datetime="2026-07-06">6 July 2026</time>Be careful with numbers too: in Spanish the thousands separator is the period and the decimal is the comma (1.234,50); in English it's the reverse (1,234.50). A screen reader can read them very differently. Letting Intl handle it prevents "1,5" from being read as "one thousand five hundred" in one language and "one point five" in another.
- Multimedia by language: subtitles and transcripts
In Module 5 (05-03) you made the videos in Cursalia's player accessible with subtitles and transcripts, thinking above all of Diego (deafness). Internationalization adds a layer: each language needs its own track. A video can have several <track> tracks, each with its srclang and its label:
<video controls>
<source src="lesson-01.mp4" type="video/mp4">
<track kind="subtitles" src="subs-es.vtt" srclang="es" label="Español" default>
<track kind="subtitles" src="subs-ca.vtt" srclang="ca" label="Català">
<track kind="subtitles" src="subs-en.vtt" srclang="en" label="English">
</video>The srclang lets the player and assistive technologies know the language of each track; the label is what the user sees in the subtitles menu. Ideally, the default track matches the language the student has selected in Cursalia. The same applies to transcripts: offer them in every available language, not just the original. Diego, watching Cursalia in Catalan, expects subtitles in Catalan, not in English.
- Text in images and cultural considerations
Avoid text embedded in images
We already warned in 05-04: text inside an image is not selectable, doesn't scale well, and is invisible to the screen reader except through its alt. Internationalization gives one more, compelling reason: embedded text can't be translated without redoing the image. A catalog banner with "Summer sale!" written inside the .png forces you to generate three images (es/ca/en) and keep them in sync. The correct solution is real text (HTML) over the image via CSS: it translates itself, scales with Sofía's zoom, and Marta can read it.
<!-- Bad: the text lives inside the PNG, doesn't translate or scale -->
<img src="summer-sale-banner.png" alt="Summer sale! 20% off">
<!-- Good: background image + real, translatable text on top -->
<div class="banner" style="background-image: url(banner-background.jpg)">
<h2>{{ t('summer_sale_title') }}</h2>
<p>{{ t('summer_sale_desc') }}</p>
</div>Cultural considerations and iconography
Localizing isn't just translating words. Icons and symbols don't mean the same everywhere, and a confusing icon is a cognitive barrier—think of Hugo (dyslexia/ADHD), for whom an ambiguous icon is more of a burden than a help. Best practices:
- Pair icons with text (or with a translated
aria-label): don't rely on a symbol being understood the same way in every culture. - Be careful with colors: their connotations vary culturally. Don't use color alone to convey meaning (the perennial rule, Module 4).
- Review gestures, flags, and visual metaphors: a flag to choose a language is problematic (a language doesn't equal a country; English isn't "only" British or American). Better to use the name of the language written in its own tongue: "Español", "Català", "English".
<!-- Cursalia's language switcher: names, not flags -->
<nav aria-label="Select language">
<button lang="es" aria-current="true">Español</button>
<button lang="ca">Català</button>
<button lang="en">English</button>
</nav>Note two details in the switcher: each button carries its own lang (so NVDA pronounces "Català" in Catalan and "English" in English) and aria-current="true" marks the active language, so Marta knows which one is selected.
Common Mistakes and Tips
- Not updating
langon<html>when changing languages. You translate the text but leavelang="es": NVDA keeps reading English content with a Spanish voice. The switcher must change thelang. - Forgetting
langon foreign fragments (3.1.2). Course titles or terms in another language are read with the wrong phonetics. Mark the<span lang="…">. - Translating only the visible text.
alt,aria-label,title, and live region messages are left in the original language. All text for people gets translated. - Laying out with physical properties.
margin-leftand friends break in RTL. Use logical properties (margin-inline-start). - Formatting dates and numbers by hand. They produce ambiguity and misreadings. Use
Intland<time datetime>. - Text embedded in images. It doesn't translate, doesn't scale, isn't read. Use real text over the image.
- Flags as a language switcher. Language ≠ country. Use the name of the language in its own tongue, with its
lang. - Tip: add a per-language check to Module 6's Definition of Done: switch the selector to
ca/enand verify with NVDA thatlangchanges, that thealt/aria-labeltext is translated, and that dates are read correctly.
Exercises
Exercise 1. Marta uses Cursalia in English (<html lang="en">) and, on a course page, hears the title "Introducción a Python" read by a voice that mangles the phonetics. On top of that, the favorite button says "Añadir a favoritos" instead of "Add to favourites". Identify the two multilingual accessibility problems and how you'd fix them.
Exercise 2. You have this course start date written by hand in the HTML: <span>06/07/2026</span>. Explain why it's problematic for accessibility and internationalization, and rewrite it correctly for a user with the interface in Spanish.
Exercise 3. The marketing team wants a "Last spots!" banner in the catalog, and proposes uploading it as a .png image with the text inside, in all three languages. Give two reasons (one about accessibility, one about internationalization) to reject it and describe the correct alternative.
Solutions
Solution 1. Problem 1: the title "Introducción a Python" is a Spanish fragment within an English page and is not marked with lang="es", so NVDA reads it with English phonetics (WCAG 3.1.2). It's fixed by wrapping it: <span lang="es">Introducción a Python</span>. Problem 2: the favorite button's aria-label wasn't translated when the language was changed; it's still in Spanish ("Añadir a favoritos") with the interface in English. It's fixed by making that aria-label come from the translation system and show "Add to favourites" when the interface is in English. In short: mark fragments in another language and translate the non-visible text too.
Solution 2. 06/07/2026 is ambiguous (July 6 or June 7?), depends on the local convention, and a screen reader can read it confusingly or incorrectly. The right approach is to give an unambiguous machine value with <time datetime> and a visible, localized text with Intl:
This way the sighted user sees the date in Spanish format without ambiguity, the reader has the exact date in datetime, and if the language is changed, the visible text can be regenerated with Intl.DateTimeFormat for the new locale.
Solution 3. Accessibility reason: the text embedded in the .png can't be read by the screen reader (it would depend solely on the alt), doesn't scale with Sofía's zoom, and can't be selected. Internationalization reason: the text inside the image doesn't translate; it would force you to keep three images in sync (es/ca/en) and redo them on any change. Correct alternative: use a background image without text and overlay real translatable HTML text (with the strings in the i18n system), which is read, scales, and translates automatically.
Conclusion
Accessibility and multilingualism are intertwined: for Marta to hear each language in its correct voice, you must declare lang on <html> (3.1.1) and on each foreign fragment (3.1.2); for the layout to work in any reading direction, logical CSS properties; and you have to translate everything intended for people—alt, aria-label, title, the live region messages from 07-01, the per-language subtitles from 05-03—format dates and numbers with Intl, avoid text embedded in images (05-04), and mind iconography and culture. Cursalia's language switcher isn't just a translator: it's an accessibility switch.
With this, Cursalia is now an accessible, dynamic, app-like (SPA), and multilingual platform. All that's left is to look ahead: where is web accessibility heading? WCAG 3.0, the evolution of ARIA, the legal push, the arrival of AI, accessibility beyond the web, and the "shift-left" cultural change. All of that—and the close of your journey through this course—is the final lesson: 07-04: Future Trends in Web Accessibility.
Web Accessibility Course
Module 1: Introduction to Web Accessibility
- What Is Web Accessibility?
- The Importance of Web Accessibility
- Overview of Accessibility Laws and Standards
- Introduction to WCAG
Module 2: Understanding Disabilities and Assistive Technologies
Module 3: Principles of Accessible Design
- Perceivable: Making Content Available to the Senses
- Operable: User Interface and Navigation
- Understandable: Information and Operation
- Robust: Compatibility with Current and Future Technologies
Module 4: Implementing Accessibility in HTML and CSS
Module 5: Accessibility in JavaScript and Multimedia
- Creating Accessible JavaScript Widgets
- Keyboard Accessibility
- Accessible Video and Audio Content
- Providing Text Alternatives for Images
Module 6: Accessibility Testing and Evaluation
- Manual Testing Techniques
- Automated Testing Tools
- User Testing with Assistive Technologies
- Interpreting Accessibility Reports
