Good day, for the past 3 weeks I have been busy translating the popular documentation platform known as MDN web docs into the Spanish version, to help Spanish-speaking readers as it had been outdated for years with respect to the English version.
This has been part of my open-source journey. In this article, I want to talk about the process, to enable future contributors to seamlessly contribute one day.
Here are the rules to follow when trying to do the Spanish-translation work. Everything that I am going to write here is already mentioned inside of the rules.
So, what is our job here ?
- There’s a page in English for example: Django’s “Admin site” feature.
- There’s already a Spanish version of that page, but it’s old and out of date.
- Your job is to go through the whole Spanish file, compare it to the whole English file, and rewrite/update it so it matches: same sections, same order, nothing left in English
Here is one rule to just bear in mind
is this text something a human reads (translate it) or something the code/interpreter looks up by exact name (leave it untouched)
File Paths
This is the most important one; File paths (bolded or inline) are never translated. It stays the same. Below, we will discuss the only thing that changes, which is en-US to es
English: docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Spanish: docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Front-matter
- Final front-matter should only contain:
title,slug,l10n.sourceCommit(plusshort-titlewhen the English file has one). - Remove
page-type,sidebar,original_slug. -
title(andshort-title): translate to Spanish. -
slug: These are file paths, not visible text, so they stay exactly as in English do not translate. -
l10n.sourceCommit: set to the exact SHA given for that English source commit.
Links
External links: These are links point to external web pages and they should be left as is. For example: https://validator.w3.org/ or https://developer.flutterwave.com/docs/testing
Internal links: All the internal links that point to pages with in the MDN web docs website have to be changed from /en-US/ to /es/.
For example:
Original (English source):
/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Enter fullscreen mode Exit fullscreen mode
This has to be changed to point to a spanish file now.
/es/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Enter fullscreen mode Exit fullscreen mode
Link Text: (ONLY the visible label) gets translated when it’s an internal MDN link.
For example
Original (English source):
[Object basics](/en-US/docs/Learn_web_development/Core/Scripting/Object_basics)
Enter fullscreen mode Exit fullscreen mode
We have to translate The link text “Object basics” to “Fundamentos de objetos”
Translated (Spanish):
[Fundamentos de objetos](/es/docs/Learn_web_development/Core/Scripting/Object_basics)
Enter fullscreen mode Exit fullscreen mode
Anchors: when you have an anchor like this, #heading_about_a_paragraph. Two things are involved:
- You need to check if that section that the anchor is referring to exists.
- If it exists, then you should translate it to Spanish
#encabezado_sobre_un_párrafo
Bold / italics
- Keep the bold/italic markers in the same place, only translate the text content.
- Text content that were bold or italics in English stay bold or italics in Spanish.
Original (English source):
_this is supposed to be italic_ and **this is supposed to be bold**
Enter fullscreen mode Exit fullscreen mode
Translated (Spanish):
_esto se supone que debe estar en cursiva_ and **esto se supone que debe estar en negrita**
Enter fullscreen mode Exit fullscreen mode
Image(s)
Never copy images into the files/es/ folder because the build pulls them from the English folder automatically. Just leave the image file path as is
Only the alt text (the […] part) gets translated; the filename or file path stays exactly as-is.
For example:
Original (English source)

Enter fullscreen mode Exit fullscreen mode
Translated (Spanish)

Enter fullscreen mode Exit fullscreen mode
Callouts
In GitHub markdown, callouts such as
[!NOTE]and[!WARNING]stay untranslated because translating them breaks the styled box.ONLY the user-facing text content contained within the box gets translated.
For example,
Original (English source)
>[!NOTE]
> See also the [glossary entry on API](/en-US/docs/Glossary/API) for more information.
Enter fullscreen mode Exit fullscreen mode
Translated (Spanish)
>[!NOTE]
> Consulta también la [entrada del glosario sobre API](/es/docs/Glossary/API) para más información.
Enter fullscreen mode Exit fullscreen mode
Notice: [!NOTE] remains untouched
Code
Plain Code blocks: These are code blocks that contain only literal data: no comments, no prose mixed in so there’s nothing to translate at all.
https://example.com/articles/
https://example.com/articles/23
https://example.com/articles/?page=2
Enter fullscreen mode Exit fullscreen mode
This stays exactly the same in the Spanish file — same URLs, same formatting, no changes whatsoever. There’s no comment explaining anything and no prose sentence inside it.
name
age
city
greet
Enter fullscreen mode Exit fullscreen mode
These are actual JavaScript property names being listed for the reader to look at, not descriptive text, so translating “city” to “ciudad” or “age” to “edad” would misrepresent what the code actually contains.
HTML table structure:
Table tags (<table>, <tr>, <th>, etc.) stay exactly the same, only the visible text inside is translated.
For example:
Original (English source):
<table class="properties">
<tbody>
<tr>
<th scope="row">Prerequisites</th>
<td>
A basic understanding of <a href="/en-US/docs/Learn_web_development/Core/Scripting">JavaScript basics</a>.
</td>
</tr>
<tr>
<th scope="row">Objective</th>
<td>To understand JavaScript's prototype-based inheritance model.</td>
</tr>
</tbody>
</table>
Enter fullscreen mode Exit fullscreen mode
Translated (Spanish):
<table class="properties">
<tbody>
<tr>
<th scope="row">Prerrequisitos</th>
<td>
Un entendimiento básico de <a href="/es/docs/Learn_web_development/Core/Scripting">los fundamentos de JavaScript</a>.
</td>
</tr>
<tr>
<th scope="row">Objetivo</th>
<td>Comprender el modelo de herencia basado en prototipos de JavaScript.</td>
</tr>
</tbody>
</table>
Enter fullscreen mode Exit fullscreen mode
Kumascript macros {{…}}
Macro names and their arguments (page paths) are never translated. The macro names such as (
NextMenu,PreviousMenuNext) stay in English because they’re internal MDN template functions, not prose.{{glossary("term")}}: if the English term doesn’t naturally read well in Spanish, add a second argument with the translated display text —{{glossary("term", "término")}}. If the term is used as-is in Spanish (e.g.HTTP,JavaScript,Python), leave it with no second argument.Navigation macros (
{{NextMenu(...)}},{{PreviousMenuNext(...)}}) always left fully untouched whether in English or Spanish.
{{NextMenu("Learn_web_development/Core/Scripting/Object_prototypes", "Learn_web_development/Core/Scripting")}}
{{PreviousMenuNext("Learn_web_development/Core/Scripting/Object_prototypes", "Learn_web_development/Core/Scripting/Object_building_practice", "Learn_web_development/Core/Scripting")}}
Enter fullscreen mode Exit fullscreen mode
Headings
- Translate headings to match the reference heading list given in the issue, using consistent verb style (gerund/imperative) across the doc. When you finish translating, review your work and make sure you have same headings, same order, same structure as the English source and Nothing is left untranslated
English (Original Source)
## Summary
## Basics: anatomy of a URL
## Scheme
## Authority
## Path to resource
## Parameters
## Anchor
## How to use URLs
## Absolute URLs vs. relative URLs
## URL usernames and passwords
## Semantic URLs
## See also
Enter fullscreen mode Exit fullscreen mode
Translated (Spanish)
## Resumen
## Conceptos básicos: anatomía de una URL
## Esquema
## Autoridad
## Ruta al recurso
## Parámetros
## Ancla
## Cómo utilizar las URL
## URL absolutas frente a URL relativas
## Nombres de usuario y contraseñas en las URL
## URL semánticas
## Véase también
Enter fullscreen mode Exit fullscreen mode
- Watch for accented characters affecting auto-generated anchor targets (e.g. “Validación” becomes anchor
validación_html, not validacion_html) because a missing accent in a link breaks the anchor.
Link: /es/docs/Learn_web_development/Core/Forms/Form_validation#validación_html
For example;
English (Original source)
## Validation HTML
Here's a small but easy-to-miss detail about headings and links. When MDN builds a page, it auto-generates an anchor ID for every heading by lowercasing the text and replacing spaces with underscores. The catch is that it keeps accented characters exactly as they are.
Enter fullscreen mode Exit fullscreen mode
Translated Spanish
## Validación HTML
Aquí hay un detalle pequeño pero fácil de pasar por alto sobre los encabezados y los enlaces. Cuando MDN construye una página, genera automáticamente un ancla para cada encabezado convirtiendo el texto a minúsculas y reemplazando los espacios con guiones bajos. La particularidad es que mantiene los caracteres acentuados exactamente como están.
Enter fullscreen mode Exit fullscreen mode
Comments
Comments inside of Code blocks
This has two (2) cases:
- A comment that is explaining something to the user (reader) should be translated.
- A comment that does not add any additional useful information should be discarded.
Useful comment(s)
A comment explaining something (translate it):
This comment is real prose explaining why the line exists or what it does. A Spanish-speaking reader benefits from having this translated
English (Original Source)
# Number of visits to this view, as counted in the session
num_visits = request.session.get('num_visits', 0)
Enter fullscreen mode Exit fullscreen mode
Translated (Spanish)
# Número de visitas a esta vista, contado en la sesión
num_visits = request.session.get('num_visits', 0)
Enter fullscreen mode Exit fullscreen mode
Throwaway comment(s)
Example 1
INSTALLED_APPS = [
# ...
'django.contrib.sessions',
]
Enter fullscreen mode Exit fullscreen mode
Here, # ... isn’t explaining anything — it’s just a visual placeholder telling the reader “there’s more code here we’re not showing.” There’s no real content to translate; translating ... doesn’t mean anything different in Spanish. Best left as-is.
Example 2
const wheels = 'alloy'; // or comment
Enter fullscreen mode Exit fullscreen mode
This kind of comment is minor, vague, and doesn’t add much context either way, so it’s genuinely a coin-flip: leave it in English since it’s a throwaway aside, or translate it for full consistency with the rest of the document.
Keywords, Variables, Functions:
DO NOT TRANSLATE ANY OF THESE
Variable names — e.g. numOfVisits, myCar, count
Function/method names — e.g. getPrototypeOf(), index()
Class names — e.g. Person, Author
Property/dictionary keys — e.g. city, wheels, my_car['wheels']
Exception names — e.g. KeyError
Boolean values (true, false): remain untranslated in both English and Spanish
English (Original Source)
// Uses 'toString()' by default, returns true
Enter fullscreen mode Exit fullscreen mode
Translated (Spanish)
// Usa 'toString()' por defecto, devuelve true
Enter fullscreen mode Exit fullscreen mode
Methods
Method names mentioned inside comments stay untouched.
English (Original Source)
// Calls 'getPrototypeOf()' to find the parent
Enter fullscreen mode Exit fullscreen mode
Translated (Spanish)
// Llama a 'getPrototypeOf()' para encontrar al padre
Enter fullscreen mode Exit fullscreen mode
Console.log()
There are two (2) incidents here:
- For literal strings printed to the user, you should translate it. An example is
English (Original Source):
console.log("Greetings from Madrid");
Enter fullscreen mode Exit fullscreen mode
Translated Spanish:
console.log("Saludos desde Madrid");
Enter fullscreen mode Exit fullscreen mode
- short/generic literal, here it’s a judgment call, and the practical approach is to flag it for the reviewer
Example:
function greet() {
console.log("hello!");
}
Enter fullscreen mode Exit fullscreen mode
Pluralization
If translating a word breaks a language feature (e.g. pluralization), adapt the word so the mechanism still works (e.g. “time” becomes “vece”).
“time” plural form is “times” in English, but the literal spanish translation would be wrong (broken) spanish.
if we were to follow a literal translation of “time” into spanish “vez” literal plural translation “vezs” (which is wrong spanish)
“vez” correct plural form in spanish is “vece”.
num_visits = 3, Here, pluralize looks at num_visits and, if it’s more than 1, appends an “s” to whatever comes right before the template tag — turning “time” into “times”.
English (Original Source)
You have visited this page {{ num_visits }} time{{ num_visits|pluralize }}.
Enter fullscreen mode Exit fullscreen mode
Adapted translation (mechanism still works):
Has visitado esta página {{ num_visits }} vece{{ num_visits|pluralize }}.
Enter fullscreen mode Exit fullscreen mode
More Information
- Run Prettier on the finished file, when you are done translating.
npx prettier --write files/es/learn_web_development/core/scripting/object_prototypes/index.md
Enter fullscreen mode Exit fullscreen mode
- Check for invisible/zero-width characters (e.g. using a script scanning for
U+200B,U+200C,U+200D,U+FEFF)
import unicodedata
import sys
INVISIBLE_CHARS = (0x200B, 0x200C, 0x200D, 0xFEFF)
def check_file(path):
with open(path, encoding="utf-8") as f:
for line_number, line in enumerate(f, 1):
found = [
unicodedata.name(char, "?")
for char in line
if ord(char) in INVISIBLE_CHARS
]
if found:
print(line_number, found, repr(line[:40]))
if __name__ == "__main__":
check_file(sys.argv[1])
Enter fullscreen mode Exit fullscreen mode