Skip to content

How to translate UI text to other languages

For onboarding, please refer to the section of "Localizing" in project README.

i18n Go API

Public API usage is covered in i18n/doc.go.

Documentation as to how to use the Go code should be written inside doc comments in Go code. It should not be written here.

Primer for translators

If you only want to translate strings without touching code, use Codeberg’s Weblate:

Placeholders and formatting

  • Translations may contain placeholders like {{.Name}}.
  • Keep these placeholders exactly as written, including the curly braces and the dot.
  • You can freely reorder placeholders within the translated text.
  • Example: {{.Count}} items selected can become 選択されたアイテム: {{.Count}} in Japanese.
  • Do not include HTML, write plain text.

Formatting uses Go's text/template package. Digits and number separators are not localised automatically.

Style tips

  • Be clear and concise, match the tone of the UI.
  • Prefer consistent terminology across the app. TODO: Create a catalogue for this purpose.
  • If a single English word is ambiguous, ask for more context or expect a contextual variant to be added using TrC or TrNC, for example context menu versus status.

Weblate and Codeberg synchronization

The PixivFE Weblate project reads from Codeberg's v3 branch. Its components are:

Component Files Translation PR branch
User interface po/*.po, using po/pixivfe.pot weblate-translations-ui
README README.*.md, using README.md weblate-translations-readme
Glossary po/glossary/*.tbx weblate-translations-glossary

Weblate's Gitea integration opens Codeberg pull requests for changes. Review and merge those PRs through the usual checks; Weblate must not write directly to v3. Translation PRs should preserve existing translations, source-location comments in PO files, and localized Markdown rather than replacing it with English source text. A Weblate push does not merge its PR.

Before merging a Weblate PR, commit and push any pending Weblate translations so the PR includes them. Use Create a merge commit in Codeberg, never Squash and merge or Rebase and merge: Weblate needs its original commits in upstream history to recognize that they were integrated. Rewriting them can cause persistent merge conflicts and require a repository reset. See Weblate's guidance on avoiding merge conflicts.

For incoming changes, configure a Codeberg push webhook pointing to https://translate.codeberg.org/hooks/gitea/ (repository admin access is required). Weblate also periodically synchronizes repositories. To pull on demand, use the component's Manage → Repository maintenance → Pull action or the authenticated Weblate repository API with operation=pull. Check its needs_merge, needs_commit, needs_push, and merge_failure fields afterward.

If Weblate reports a divergent repository, compare its outgoing commits and the translation files with v3 before intervening. Prefer a normal Weblate pull/rebase. Do not reset, force-push, or delete branches containing unique translation work. If new UI strings are missing, update po/pixivfe.pot from the source (see below), commit that change to v3, and synchronize Weblate again. New untranslated strings are expected; removing existing translations is not.

Extraction workflow

We use a Go AST-based extractor. Make sure to regenerate templ code before extracting.

Developer commands via build.sh:

Text Only
1
2
3
./build.sh i18n_extract    # generate POT template
./build.sh i18n_merge      # merge POT into all locale PO files
./build.sh i18n_validate   # validate PO files with msgfmt

Typical flow:

  1. Regenerate templ code and the POT: ./build.sh i18n_extract
  2. Merge into all locales: ./build.sh i18n_merge
  3. Validate: ./build.sh i18n_validate
  4. Commit changes to the POT and any updated PO files.

Gitlab CI TODO

  • Add a job that:
  • Runs ./build.sh i18n_extract and fails if it produces a git diff to the POT.
  • Runs ./build.sh i18n_merge and ./build.sh i18n_validate.
  • Commits updated PO files in a separate MR?
  • NOTE: Ensure gettext tools msgmerge and msgfmt are available in the runner image.

Notes

  • We intentionally do not compile or load .mo files. Parsing .po is sufficient and keeps the build simpler. If we later measure performance issues attributable to .po parsing, we can revisit.
  • Tag translations for user-generated content live in i18n/data/tag_translations.yaml and are surfaced via i18n.TrTagName. They are intentionally separate from UI message catalogues and may move to another package later.

References

gettext format:

  • https://docs.weblate.org/en/latest/formats/gettext.html#example-files
  • https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html
  • http://ftp.twaren.net/Unix/GNU/old-gnu/Manuals/gettext-0.11.2/html_node/gettext_9.html

templ guides:

  • Implicit ctx context.Context variable: https://templ.guide/syntax-and-usage/context/
  • Implicit children templ.Component variable: https://templ.guide/syntax-and-usage/template-composition/#children
  • templ.NopComponent: https://pkg.go.dev/github.com/a-h/templ#pkg-variables