# Internationalization (i18n)

Halo themes support i18n via `.properties` files under an `i18n/` directory. Thymeleaf provides the `#messages` object for reading translations in templates.

---

## Directory Structure

```
my-theme/
├── i18n/
│   ├── default.properties      # Fallback / default language
│   ├── zh_CN.properties        # Simplified Chinese
│   ├── zh_TW.properties        # Traditional Chinese
│   └── es.properties           # Spanish
├── templates/
└── theme.yaml
```

> Halo uses `default.properties` as the fallback when no locale-specific file matches the user's preference.

---

## Properties File Format

Simple key-value pairs:

```properties
# default.properties
page.author.title=Author: {0}
common.previousPage=Previous
common.nextPage=Next
common.noPosts=No posts yet.
```

```properties
# zh_CN.properties
page.author.title=作者：{0}
common.previousPage=上一页
common.nextPage=下一页
common.noPosts=暂无文章。
```

Placeholders `{0}`, `{1}` ... are filled by the arguments passed to the message function.

---

## Using i18n in Templates

### `#messages.msg(key)` — Get a message

```html
<h1 th:text="${#messages.msg('page.author.title', author.spec.displayName)}"></h1>
```

### `#messages.msgOrNull(key)` — Get a message or null if missing

```html
<span th:text="${#messages.msgOrNull('custom.label') ?: 'Default Label'}"></span>
```

### Thymeleaf shorthand `#{key}` — Standard expression

```html
<!-- Equivalent to #messages.msg('common.previousPage') -->
<a th:text="#{common.previousPage}">Previous</a>

<!-- With arguments -->
<title th:text="|#{page.author.title(${author.spec.displayName})} - ${site.title}|"></title>
```

---

## `#locale` — Current Locale

Useful for rendering language-specific UI or setting `<html lang>`:

```html
<html th:lang="${#locale.toLanguageTag()}">
  <!-- Language selector -->
  <select>
    <option value="en" th:selected="${#locale.toLanguageTag() == 'en'}">English</option>
    <option value="zh-CN" th:selected="${#locale.toLanguageTag() == 'zh-CN'}">简体中文</option>
  </select>
</html>
```

---

## Frontend i18n Pattern

Some themes also need translations in JavaScript. A common pattern is to inject the needed strings into a global object via inline script:

```html
<script th:inline="javascript">
  window.i18nResources = {
    "common.previousPage": `[(#{common.previousPage})]`,
    "common.nextPage": `[(#{common.nextPage})]`,
  };
</script>
```

> Note: `[(#{key})]` is Thymeleaf's unescaped inlining syntax. It evaluates the expression and inserts the raw value into the script.

---

## Online Docs

https://raw.githubusercontent.com/halo-dev/docs/refs/heads/main/docs/developer-guide/theme/global-variables.md
