---
title: Site localization
description: Creating and maintaining site pages in non-English localizations.
linkTitle: Localization
weight: 25
cSpell:ignore: Dowair shortcodes
---

The OTel website uses Hugo's [multilingual framework][] to support page
localizations. English is the default language, with US English as the default
(implicit) localization. A growing number of other localizations are supported,
as can be seen from the languages dropdown menu in the top nav.

## Translation guidance

When translating website pages from English, we recommend that you follow the
guidance offered in this section.

### Summary

#### ✅ Do {#do}

<div class="border-start border-success bg-success-subtle">

- **Translate**:
  - Page content, including:
    - Mermaid [diagram](#images) text fields
    - Code comments from code excerpts (optional)
  - [Front matter][] field values for `title`, `linkTitle`, and `description`
  - **All** page content and front matter unless indicated otherwise
- **Preserve** the _content_, _meaning_, and _style_ of the original text
- **Submit work _incrementally_** via [small pull requests](#small-prs)
- **Ask** [maintainers][] if you have any doubts or questions through:
  - [Slack][] `#otel-docs-localization` or `#otel-comms` channels
  - [Discussion][], issue, or PR comment

[Discussion]:
  https://github.com/open-telemetry/opentelemetry.io/discussions?discussions_q=is%3Aopen+label%3Ai18n

</div>

#### ❌ Do NOT {#do-not}

<div class="border-start border-warning bg-warning-subtle">

- **Translate**:
  - [Alert types](../style-guide/#alerts) such as `TIP`, `WARNING`, etc. This is
    enforced by a [`MARKDOWN` linter][] rule.
  - Code, including code blocks and inline code (like this
    `inline code example`)
  - **File or directory** names of resources in this repository
  - [Front matter][] fields other than those listed in [Do](#do). In particular,
    do not translate `aliases`. When in doubt, ask maintainers.
  - [Links](#links), this includes [heading IDs](#headings) [^*]
  - Markdown elements marked as `notranslate` (usually as a CSS class), in
    particular for [headings](#headings)
- Create **copies of images and other assets**, unless you
  [localize text in them](#images)
- Add new or change:
  - **Content** that would be different from the originally intended meaning
  - Presentation **style**, including: _formatting_, _layout_, and _design_
    style (typography, letter case, and spacing for example).

[^*]: For a possible exception, see [Links](#links).

[`MARKDOWN` linter]: ../pr-checks/#markdown-linter

</div>

#### Use of AI tools {#ai-tools}

If you use generative AI tools (such as ChatGPT, Gemini, or similar) to assist
with translations, you must follow the OpenTelemetry [Generative AI Contribution
Policy][genai-policy] and the Linux Foundation [Generative AI
Policy][lf-ai-policy]. In particular:

- **Disclose** that you used AI by checking the appropriate box in the [pull
  request template][].
- **Review and validate** all AI-generated translations for accuracy. You are
  responsible for the content you submit.
- **Do not submit** AI-generated translations that you cannot review and verify
  yourself (e.g., submissions in languages you are not proficient in). This
  creates a significant review bottleneck, and your PR may be closed to protect
  maintainer bandwidth.

[genai-policy]:
  https://github.com/open-telemetry/community/blob/main/policies/genai.md
[lf-ai-policy]: https://www.linuxfoundation.org/legal/generative-ai
[pull request template]:
  https://github.com/open-telemetry/opentelemetry.io/blob/main/.github/PULL_REQUEST_TEMPLATE.md

### Heading IDs {#headings}

To ensure that heading anchor targets are uniform across localizations, when
translating headings:

- Preserve the heading's explicit ID if it has one. [Heading ID syntax][] is
  written after the heading text using syntax like `{ #some-id }`.
- Otherwise, explicitly declare a heading ID corresponding to the autogenerated
  ID of the original English heading.

[Heading ID syntax]:
  https://github.com/yuin/goldmark/blob/master/README.md#headings

### Links {#links}

Do **not** translate link references. This holds true for external links, and
paths to website pages and section-local resources such as
[images and other assets](#images).

The only exception is for links to external pages (such as
<https://en.wikipedia.org>) that have a version specific to your local. Often
this means replacing the `en` in the URL by your locale's language code.

> [!NOTE]
>
> The OTel website repository has a custom render-link hook that Hugo uses to
> convert absolute link paths referring to documentation pages. **Links of the
> form `/docs/some-page` are made locale specific** by prefixing the path with
> the page language code when rendering the link. For example, the previous
> sample path would become `/ja/docs/some-page` when rendered from a Japanese
> page.

### Link definition labels {#link-labels}

Locale authors can choose or not to translate [labels][] of Markdown [link
definitions][]. If you choose to keep the English label, then follow the
guidance given in this section.

For example, consider the following Markdown:

```markdown
[Hello], world! Welcome to the [OTel website][].

[hello]: https://code.org/helloworld
[OTel website]: https://opentelemetry.io
```

This would be translated in French as:

```markdown
[Bonjour][hello], le monde! Bienvenue sur le [site OTel][OTel website].

[hello]: https://code.org/helloworld
[OTel website]: https://opentelemetry.io
```

[labels]: https://spec.commonmark.org/0.31.2/#link-label
[link definitions]:
  https://spec.commonmark.org/0.31.2/#link-reference-definitions

### Images and other assets {#images}

- Do **not** make copies of image, video, or other non-content asset files
  unless you localize text in the file itself.
  - Hugo is smart about the way that it renders image files that are shared
    across site localizations. That is, Hugo will output a _single_ image file
    and share it across locales. For details, see [Page bundles][].
  - This is enforced by a [`LOCALIZATION` guidelines][l10n-check] check.

- **Do** translate text in [Mermaid][] diagrams.

[l10n-check]: ../pr-checks/#localization
[Mermaid]: https://mermaid.js.org
[Page bundles]: https://gohugo.io/content-management/multilingual/#page-bundles

### Include files {#includes}

**Do** translate page fragments found under `_includes` directories just as you
would translate any other page content.

### Shortcodes

> [!NOTE]
>
> As of February 2025, we are in the process of migrating from shortcodes to
> [include files](#includes) as a means of supporting shared-page content.

Some of the base shortcodes contain English text that you might need to localize
-- this is particularly true of those contained in [layouts/_shortcodes/docs][].

If you need to create a localized version of a shortcode, place it under
`layouts/_shortcodes/xx`, where `xx` is your localization's language code. From
there, use the same relative path as the original base shortcode.

[layouts/_shortcodes/docs]:
  https://github.com/open-telemetry/opentelemetry.io/tree/main/layouts/_shortcodes/docs

## Keeping track of localized-page drift {#track-changes}

One of the main challenges of maintaining localized pages, is identifying when
the corresponding English language pages have been updated. This section
explains how we handle this.

### The `default_lang_commit` front-matter field

When a localized page is written, such as `content/zh/<some-path>/page.md`, this
translation is based on a specific [`main` branch commit][main] of the
corresponding English language version of the page at
`content/en/<some-path>/page.md`. In this repository, every localized page
identifies the English page commit in the localized page's front matter as
follows:

```markdown
---
title: Your localized page title
# ...
default_lang_commit: <most-recent-commit-hash-of-default-language-page>
---
```

The front matter above would be in `content/zh/<some-path>/page.md`. The commit
hash would correspond to the latest commit of `content/en/<some-path>/page.md`
from the `main` branch.

### Tracking changes to English pages

As updates are made to English language pages, you can keep track of the
corresponding localized pages that need updating by running the following
command:

```console
$ npm run check:i18n
> Drifted file: content/zh/docs/platforms/kubernetes/_index.md
...
DRIFTED files: 361 out of 990
```

You can restrict the target pages to one or more localizations by providing
path(s) like this:

```sh
npm run check:i18n -- content/zh
```

### Viewing change details

For any given localized pages that need updating, you can see the diff details
of the corresponding English language pages by using the `diff` subcommand and
providing the paths to your localized pages. For example:

```console
$ npm run check:i18n -- diff content/zh/docs/platforms/kubernetes
# content/zh/docs/platforms/kubernetes/_index.md: drifted from 1ca30b4d
diff --git a/content/en/docs/platforms/kubernetes/_index.md b/content/en/docs/platforms/kubernetes/_index.md
index 3592df5d..c7980653 100644
--- a/content/en/docs/platforms/kubernetes/_index.md
+++ b/content/en/docs/platforms/kubernetes/_index.md
@@ -1,7 +1,7 @@
 ---
 title: OpenTelemetry with Kubernetes
 linkTitle: Kubernetes
-weight: 11
+weight: 350
 description: Using OpenTelemetry with Kubernetes
 ---
```

### Adding `default_lang_commit` to new pages

As you create pages for your localization, remember to add `default_lang_commit`
to the page front matter along with an appropriate commit hash from `main`.

If your page translation is based on an English page in `main` at `<HASH>`, then
run the following command to automatically add `default_lang_commit` to your
page file's front matter using the commit `<HASH>`. You can specify `HEAD` as an
argument if your pages are now synced with `main` at `HEAD`. For example:

```sh
npm run check:i18n -- commit 1ca30b4d --new content/ja
npm run check:i18n -- commit HEAD --new content/zh/docs/concepts
```

To list localization page files with missing hash keys, run:

```sh
npm run check:i18n -- --new
```

### Updating `default_lang_commit` for existing pages

As you update your localized pages to match changes made to the corresponding
English language page, ensure that you also update the `default_lang_commit`
commit hash.

> [!TIP]
>
> If your localized page now corresponds to the English language version in
> `main` at `HEAD`, then run
> `npm run check:i18n -- commit HEAD <PATH-TO-YOUR-PAGE>`: the
> `default_lang_commit` hash is refreshed and the page's
> [drift status](#drift-status) is cleared in the same write.

If you have batch updated all of your localization pages that had drifted, you
can update the commit hash of these files using the `commit` subcommand followed
by a commit hash or 'HEAD' to use `main@HEAD`.

```sh
npm run check:i18n -- commit <HASH> <PATH-TO-YOUR-UPDATED-FILES>
npm run check:i18n -- commit HEAD <PATH-TO-YOUR-UPDATED-FILES>
```

> [!IMPORTANT]
>
> When you use `HEAD` as a hash specifier, the script will use the hash of
> `main` at HEAD in your **local environment**. Make sure that you fetch and
> pull `main`, if you want HEAD to correspond to `main` in GitHub.

### Patching localized pages {#patched}

[Build fixes](#keep-checks-green) sometimes require editing a localized page
without syncing it to its English counterpart: for example, repairing a
shortcode invocation after the shared shortcode changed. Mark each localized
page fixed in this way as **patched**, whether or not the fix spans locales:

- Make only the edits that the fix requires — no other changes to the page.
- Append the `# patched` YAML comment to the page's `default_lang_commit` line:

  ```yaml
  default_lang_commit: abc4567... # patched
  ```

The marker is reserved for such mechanical fixes —
[semantic changes](#semantic-changes) never use it. The marker tells the page's
locale team that the page was fixed without being synced: the hash still records
the last sync point. The marker is dropped the next time the page's hash is
[updated](#updating-default_lang_commit-for-existing-pages).

### Drift status

The `drifted_from_default` front-matter field marks a localized page as drifted:
the page displays an "outdated" banner, and the link checker skips it, so that
stale links on drifted pages don't fail CI. The link checker doesn't wait for
the field: locale copies of English pages changed since the last tree-wide
status sync are skipped as well, as
[drift pending](/site/build/link-checking/#configuration).

The daily [Housekeeping run](/site/build/ci-workflows/#housekeeping) keeps the
field in sync tree wide; PRs don't update the status of pages they don't
otherwise change. Each page that a PR **does** change must leave the PR with an
accurate drift state, as the `I18N check` enforces: either sync the page with
its English counterpart and
[refresh its pin](#updating-default_lang_commit-for-existing-pages) — the status
is cleared in the same write — or record the remaining drift with
`npm run fix:i18n:status -- <PATHS>`. Pins can only point at commits on `main`,
so a page synced to English changes made in the same PR records the remaining
drift until those changes merge.

### Script help

For more details about the script, run `npm run check:i18n -- -h`.

## New localizations

Interested in starting a new localization for the OTel website? Reach out to
maintainers to express your interest, for example through a GitHub discussion or
via the Slack `#otel-docs-localization` channel. This section explains the steps
involved in starting a new localization.

> [!NOTE]
>
> You don't have to be an existing contributor to the OpenTelemetry project to
> start a new localization. However, you cannot be added as a member of the
> [OpenTelemetry GitHub organization](https://github.com/open-telemetry/) or as
> a member of the approvers group for your localization until you satisfy the
> requirements for becoming an established member and approver as outlined in
> the [membership guidelines][].
>
> Before you earn approver status, you can indicate your approval of a
> localization PR by adding an "LGTM" (Looks Good To Me) comment. During this
> startup phase, maintainers will treat your reviews as if you are an approver
> already.

[membership guidelines]:
  https://github.com/open-telemetry/community/blob/main/guides/contributor/membership.md

### 1. Assemble a localization team {#team}

Creating a localization is about growing an active and supportive community. To
start a new localization for the OpenTelemetry website you need:

1. A **localization mentor** who is familiar with your language, such as an
   [active approver][] of the [CNCF Glossary][] or the [Kubernetes website][].
2. At least two potential contributors.

[active approver]: https://github.com/cncf/glossary/blob/main/CODEOWNERS
[CNCF Glossary]: https://glossary.cncf.io/
[Kubernetes website]: https://github.com/kubernetes/website

### 2. Localization kickoff: create an issue {#kickoff}

With a [localization team](#team) in place or coming together, create an issue
with the task list given below:

1. Look up the official [ISO 639-1 code][] for the language you want to add.
   We'll refer to this language code as `LANG_ID` in the remainder of this
   section. If you have doubts about which tag to use, especially when it comes
   to choosing a subregion, ask maintainers.

   [ISO 639-1 code]: https://en.wikipedia.org/wiki/ISO_639-1

2. Identify the GitHub handles of the
   [mentor and potential contributors](#team).

3. Create a [new issue][] containing the following task list in the opening
   comment:

   ```markdown
   - [ ] Language info:
     - ISO 639-1 language code: `LANG_ID`
     - Language name: ADD_NAME_HERE
   - [ ] Locale team info:
     - [ ] Locale mentor: @GITHUB_HANDLE1, @GITHUB_HANDLE2, ...
     - [ ] Contributors: @GITHUB_HANDLE1, @GITHUB_HANDLE2, ...
   - [ ] Read through
         [Localization](https://opentelemetry.io/docs/contributing/localization/)
         and all other pages in the Contributing section
   - [ ] Localize site homepage (only) to YOUR_LANGUAGE_NAME and submit a PR.
         For details, see
         [Localize the homepage](https://opentelemetry.io/docs/contributing/localization/#homepage).
   - [ ] OTel maintainers:
     - [ ] Update Hugo config for `LANG_ID`
     - [ ] Configure cSpell and other tooling support
     - [ ] Create an issue label for `lang:LANG_ID`
     - [ ] Create org-level group for `LANG_ID` approvers
     - [ ] Update components owners for `content/LANG_ID`
   - [ ] Create an issue to track the localization of the **glossary**. Add the
         issue number here. For details, see
         [Localize the glossary](https://opentelemetry.io/docs/contributing/localization/#glossary).
   ```

### 3. Localize the homepage {#homepage}

[Submit a pull request](../pull-requests/) with a translation of the website
[homepage][], and _nothing else_, in the file `content/LANG_ID/_index.md`.
Ensure that maintainers have the necessary permissions to edit your PR, since
they will add additional changes to your PR that are required to get your
localization project started.

[homepage]:
  https://github.com/open-telemetry/opentelemetry.io/blob/main/content/en/_index.md

After your first PR is merged, maintainers will set up the issue label, the
org-level group and the component owners.

### 4. Localize the glossary {#glossary}

The second page to localize is the [Glossary](/docs/concepts/glossary/). This is
a **critical** page for localized readers, since it defines the key terms used
in observability and OpenTelemetry in particular. This is especially critical if
no such terms exist in your language.

For guidance, see the [video][ali-d-youtube] of Ali Dowair's talk at Write the
Docs 2024: [The art of translation: How to localize technical
content][ali-dowair-2024].

[ali-dowair-2024]:
  https://www.writethedocs.org/conf/atlantic/2024/speakers/#speaker-ali-dowair-what-s-in-a-word-lessons-from-localizing-kubernetes-documentation-to-arabic-ali-dowair
[ali-d-youtube]: https://youtu.be/HY3LZOQqdig

### 5. Localize remaining site pages in small increments {#rest}

With terminology established, you can now localize the remaining site pages.

> [!IMPORTANT] Submit small PRs <a id="small-prs"></a>
>
> Localization teams should submit their work in **small increments**. That is,
> keep [PRs][] small, preferably limited to one or a few small files. Smaller
> PRs are easier to review and so typically get merged more quickly.

### OTel maintainer checklist

#### Hugo

Update Hugo config for `LANG_ID`. Add appropriate entries for `LANG_ID` under:

- `languages` in `config/_default/hugo.yaml`
- `module.mounts` via `config/_default/module-template.yaml`. At a minimum, add
  a single `source`-`target` entry for `content`. Consider adding entries for
  `en` fallback pages only once the locale has enough content.

#### Spelling

Look for [cSpell dictionaries][] available as NPM packages
[@cspell/dict-LANG_ID][]. If a dictionary isn't available for your dialect or
region, choose the closest region.

- **If a dictionary is available**:
  - Add the NPM package as a dev dependency, for example:
    `npm install --save-dev @cspell/dict-bn`.
  - In [`.cspell.yml`][], add the package's `cspell-ext.json` under `import:`,
    and add the dictionary's ID (for example `bn`, `es-es`, `pl_pl`) under
    `dictionaries:`.
- **If no dictionary is available** for the language, do not add an `import` for
  it. Add `content/LANG_ID` to the `ignorePaths` list in [`.cspell.yml`][] so
  cSpell does not try to spell-check that locale's Markdown as English.

[cSpell dictionaries]: https://github.com/streetsidesoftware/cspell-dicts
[@cspell/dict-LANG_ID]: https://www.npmjs.com/search?q=%40cspell%2Fdict
[`.cspell.yml`]:
  https://github.com/open-telemetry/opentelemetry.io/blob/main/.cspell.yml

#### Word list

Create `.cspell/LANG_ID-words.txt` for every new locale (empty at first), even
when **Spelling** has no natural-language dictionary to add.

- In [`.cspell.yml`][], register the file and enable it:
  - Under `dictionaryDefinitions`, add an entry with `name` (for example
    `LANG_ID-words`) and `path` (for example `.cspell/LANG_ID-words.txt`).
  - Under `dictionaries`, add the same `name` value as in the step above (not
    the file path).

#### Other tooling support

- Prettier support: if `LANG_ID` isn't well supported by Prettier, add ignore
  rules to `.prettierignore`

## Approver and maintainer guidance

### Enabling auto-merge on locale-only PRs {#auto-merge}

Members of a locale's maintainers team can enable [GitHub auto-merge][] on a
locale-only PR by commenting `/auto-merge` (or `/auto-merge:enable`; use
`/auto-merge:disable` to turn it off). The directive must be on its own line,
with no leading text or whitespace, as the first or last non-blank line of the
comment. It may appear at most once. For example, you can write:

```text
LGTM
/auto-merge
```

This lets established localization teams land their own PRs without waiting on a
docs maintainer. GitHub, branch protection, and CODEOWNERS rules still gate the
merge: the PR only merges once all required reviews are in and checks pass.

An auto-merge comment is honored only when every changed file is owned by a
locale you maintain, so it can't be used to make changes to shared or English
content. For the eligibility rules and command details, see the [helper
README][].

[GitHub auto-merge]:
  https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/automatically-merging-a-pull-request
[helper README]:
  https://github.com/open-telemetry/opentelemetry.io/tree/main/scripts/gh/locale-auto-merge

### PRs should not span locales {#prs-should-not-span-locales}

As a general rule, a PR should not span locales, that is, it should change the
pages of at most one locale. The only exceptions are described in this section.

#### Semantic changes {#semantic-changes}

Approvers should ensure that [PRs][] making **semantic** changes to doc pages do
not span multiple locales. A semantic change is one that impacts the _meaning_
of the page content — what readers understand and act on. Code blocks, commands,
and configuration samples are part of that content: they aren't
[translated](#do-not), but edits to them are semantic changes all the same. Our
docs [localization process](.) ensures that locale approvers will, in time,
review the English-language edits to determine if the changes are appropriate
for their locale, and how best to incorporate them into their locale. If changes
are necessary, the locale approvers will make them via their own locale-specific
PRs.

> [!NOTE] Content-neutral maintenance
>
> The locale-span rule governs page **content**. Maintainers sometimes submit
> content-neutral changes that necessarily span locales: site-wide tooling,
> configuration, front-matter, or markup updates, including
> [drift-status](#drift-status) bookkeeping — the automated PRs and manual
> status-only edits alike. Such changes don't alter the meaning of localized
> pages.

#### Keeping the build green {#keep-checks-green}

A PR that changes localized page **content** may span multiple locales only when
that is strictly required to keep the site build green. Such **build fixes**
repair site-build breakage on localized pages, for example, after a shared
shortcode, include file, or data source changes. A page's
[drift status](#drift-status) only shields it from link checking, not from the
Hugo build. Mark every localized page that you fix as [patched](#patched).

Link-check failures on localized pages are **not** such a case; for how to
resolve them, see
[Link fixes and resource updates](#link-fixes-and-resource-updates).

The same minimum-fix rule applies to drift-status bookkeeping: when a failing
check calls for refreshing `drifted_from_default`, update only the pages that
the failing check reports; the daily
[Housekeeping run](/site/build/ci-workflows/#housekeeping) completes the rest.
Status-only edits are [content-neutral maintenance](#semantic-changes).

Treat any other change to localized page content as a **semantic** change for
that locale. This includes targeted content additions to drifted pages, such as
adding a new glossary term.

#### Link fixes and resource updates {#link-fixes-and-resource-updates}

Changes to the English documentation can result in link-check failures for
non-English locales. This happens when documentation pages, or sections within
them, are moved or deleted; links to moved external resources can fail
similarly. Fix such failures on English pages only; **never edit localized page
content to fix links**. [Drift tracking](#track-changes) flags outdated
localized copies for their locale teams, and reconciliation, link fixes
included, is left to each team.

First, contain the fallout on the English side by fixing the failing links. Some
target fates offer additional mitigations:

- **A page was moved**: ensure that the moved English page declares an
  [alias][aliases] for its old path. The alias keeps previously published links
  to the page working, but only for site visitors: aliases are published as
  server-side redirects, and the link checker resolves links against the built
  site's canonical page paths. Links to the old path therefore still need fixing
  on English pages.
- **A section was moved within its page**: preserve its [heading ID](#headings)
  so that links to the section keep working. Aliases can't help in this case:
  they redirect page paths, not fragments.

The other fates have no such mitigation: a section moved to a different page, a
moved external resource, or a deleted target. Fixing the English links is the
whole English-side fix; for a deleted target, that means choosing a replacement
target or dropping the reference on each linking page.

Then let drift handling cover the localized pages: fixing the English pages
makes their localized copies [drift](#drift-status), and the link checker skips
drifted copies. If a localized page still fails link checking, refresh its drift
status directly:

```sh
npm run fix:i18n:status -- PATHS_TO_FAILING_LOCALIZED_PAGES
```

In the rare case where a failing link exists only in a localized page, report it
and coordinate a fix with its locale team.

Finally, rerun `npm run check:links` and confirm that no link failures remain.

[aliases]: https://gohugo.io/content-management/urls/#aliases
[front matter]: https://gohugo.io/content-management/front-matter/
[main]: https://github.com/open-telemetry/opentelemetry.io/commits/main/
[maintainers]: https://github.com/orgs/open-telemetry/teams/docs-maintainers
[multilingual framework]: https://gohugo.io/content-management/multilingual/
[new issue]: https://github.com/open-telemetry/opentelemetry.io/issues/new
[PRs]: ../pull-requests/
[slack]: https://slack.cncf.io/
