# Site localization

> Creating and maintaining site pages in non-English localizations.

---

LLMS index: [llms.txt](/llms.txt)

---

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
1       1       content/en/docs/platforms/kubernetes/_index.md - content/zh/docs/platforms/kubernetes/_index.md
...
```

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 `-d` flag and providing
the paths to your localized pages, or omit the paths to see all. For example:

```console
$ npm run check:i18n -- -d content/zh/docs/platforms/kubernetes
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 -- -n -c 1ca30b4d content/ja
npm run check:i18n -- -n -c HEAD content/zh/docs/concepts
```

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

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

### 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 erase the commit hash value in the front matter, and
> run the **add** command given in the previous section to automatically refresh
> the `default_lang_commit` field value.

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

```sh
npm run check:i18n -- -c <hash> <PATH-TO-YOUR-NEW-FILES>
npm run check:i18n -- -c HEAD <PATH-TO-YOUR-NEW-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 and check fixes](#keep-checks-green) sometimes require editing a
localized page without syncing it to its English counterpart — for example,
retargeting a link after an English page was moved. 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

Run `npm run fix:i18n:status` to set the `drifted_from_default` front-matter
field on those target localization pages that have drifted. This field displays
an "outdated" banner at the top of the page, and causes the link checker to skip
the page, so that stale links on drifted pages don't fail CI. For details about
how the link checker handles drifted pages, see
[Link checking](/site/build/link-checking/).

### 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 the automated
> [drift-status](#track-changes) bookkeeping PRs. Such changes don't alter the
> meaning of localized pages.

#### Keeping the build and checks 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 and its checks green:

- **Link fixes**: repairing link-check failures on localized pages after an
  English page is moved or deleted, or an external resource has moved. See
  [Link fixes and resource updates](#link-fixes-and-resource-updates).
- **Build fixes**: repairing site-build breakage on localized pages, for
  example, after a shared shortcode, include file, or data source changes. A
  page's [drift status](#track-changes) only shields it from link checking, not
  from the Hugo build.

In both cases, mark every localized page that you fix as [patched](#patched).

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. A moved or deleted page's localized copies themselves don't need to
be touched: [drift tracking](#track-changes) flags them for their locale teams.
What may need fixing are the localized pages that **link** to such targets.
Proceed according to the fate of the link target:

- **A page was moved**:

  1. 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.
  2. Update the link to the new path on each non-English page that fails link
     checking, and mark each edited page as [patched](#patched).
     ([Drifted](#track-changes) pages are skipped by the link checker, so this
     typically applies to in-sync pages.)

- **A section was moved**: aliases can't help in this case, since they redirect
  page paths, not fragments. If the section moved within its page, preserve its
  [heading ID](#headings) so that links to the section keep working. Otherwise,
  update links to the section on each page that fails link checking, and mark
  each edited localized page as [patched](#patched).

- **A page or section was deleted**: choosing a replacement or dropping the
  reference is a [semantic change](#semantic-changes) for each affected locale,
  so don't patch such links. Fixing the English pages that linked to the deleted
  target makes their localized copies drift: once your English edits are
  committed, run `npm run fix:i18n:status -- <PATHS-TO-LOCALIZED-COPIES>` to
  refresh the [drift status](#drift-status) of those copies. The link checker
  then skips those pages, and reconciliation is left to each page's locale team.
  In the rare case where a failing link exists only in a localized page,
  coordinate a fix with its locale team.

- **An external resource was moved**, but is otherwise semantically unchanged
  (such as a relocated GitHub file): update the link on each page that fails
  link checking, marking each edited localized page as [patched](#patched).

In all cases, 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/
