Skip to content

Commit

Permalink
gtk: add localization support, take 3
Browse files Browse the repository at this point in the history
This is my third (!) attempt at implementing localization support.
By leveraging GTK builder to do most of the `gettext` calls, I
can avoid the whole mess about missing symbols on non-glibc platforms.

Added some documentation too for contributors and translators,
just for good measure.
  • Loading branch information
pluiedev committed Feb 26, 2025
1 parent 4e5e4a7 commit 69882c0
Show file tree
Hide file tree
Showing 10 changed files with 514 additions and 0 deletions.
2 changes: 2 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub fn build(b: *std.Build) !void {

// Ghostty resources like terminfo, shell integration, themes, etc.
const resources = try buildpkg.GhosttyResources.init(b, &config);
const i18n = try buildpkg.GhosttyI18n.init(b, &config);

// Ghostty dependencies used by many artifacts.
const deps = try buildpkg.SharedDeps.init(b, &config);
Expand Down Expand Up @@ -39,6 +40,7 @@ pub fn build(b: *std.Build) !void {
if (config.app_runtime != .none) {
exe.install();
resources.install();
i18n.install();
}

// Libghostty
Expand Down
4 changes: 4 additions & 0 deletions nix/devShell.nix
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
gobject-introspection,
libadwaita,
blueprint-compiler,
gettext,
adwaita-icon-theme,
hicolor-icon-theme,
harfbuzz,
Expand Down Expand Up @@ -127,6 +128,9 @@ in
# wasm
wabt
wasmtime

# Localization
gettext
]
++ lib.optionals stdenv.hostPlatform.isLinux [
# My nix shell environment installs the non-interactive version
Expand Down
2 changes: 2 additions & 0 deletions nix/package.nix
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
libadwaita,
blueprint-compiler,
libxml2,
gettext,
wrapGAppsHook4,
gsettings-desktop-schemas,
git,
Expand Down Expand Up @@ -86,6 +87,7 @@ in
wrapGAppsHook4
blueprint-compiler
libxml2 # for xmllint
gettext
]
++ lib.optionals enableWayland [
wayland-scanner
Expand Down
56 changes: 56 additions & 0 deletions po/README_CONTRIBUTORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Localizing Ghostty: The Contributors' Guide

Ghostty uses the `gettext` library/framework for localization, which has the
distinct benefit of being able to be consumed directly by our two main
app runtimes: macOS and GTK (Linux). The core would ideally remain agnostic
to localization efforts, as not all consumers of libghostty would be interested
in localization support. Thus, implementors of app runtimes are left responsible
for any localization that they may add.

## GTK

In the GTK app runtime, translations are sourced either from Zig source code or
from Blueprint files (under `src/apprt/gtk/ui/`). Placing translatable strings
in the latter is preferable as Blueprints have native syntax for translatable
strings (`_("")`) and are in general much easier to manage than translations
in Zig code.

Provide context to translators by adding a `// Translators: ` comment on the
line above the string in question, such as:
```
// Translators: This is the name of the button that opens the about dialog.
title: _("About Ghostty");
```

By default identical strings are collapsed together into one translatable entry.
To avoid this, assign a *context* to the string:
```
label: C_("menu action", "Copy");
```

All translatable strings are extracted into the *translation template file*,
located under `po/com.mitchellh.ghostty.pot`. **This file must stay in sync with
the list of translatable strings present in source code or Blueprints at all times.**
A CI action would be run for every PR, which checks if the translation template
requires any updates. You can update the translation template by running
`zig build update-translations`, which would also synchronize translation files
for other locales (`.po` files) to reflect the state of the template file.

During the build process, each locale in `.po` files is compiled
into binary `.mo` files, stored under `share/locale/<LOCALE>/LC_MESSAGES/com.mitchellh.ghostty.mo`.
This can be directly accessed by `libintl`, which provide the various `gettext`
C functions that can be called either by Zig code directly, or by the GTK builder
(recommended).

> [!NOTE]
> For the vast majority of users, no additional library needs to be installed
> in order to get localizations, since `libintl` is a part of the GNU C standard
> library. For users using alternative C standard libraries like musl, they must
> use a stub implementation such as [`gettext-tiny`](https://github.com/sabotage-linux/gettext-tiny)
> that offer no-op symbols for the translation functions, or by using a build of
> `libintl` that works for them.
## macOS

> [!NOTE]
> The localization system is not yet implemented for macOS.
148 changes: 148 additions & 0 deletions po/README_TRANSLATORS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# Localizing Ghostty: The Translators' Guide

First of all, thanks for helping us localize Ghostty!

To begin contributing, please make sure that you have installed the `gettext`
package on your system, which should be available under the name `gettext`
for most Linux and macOS package managers.

On Windows `gettext` is available through the `scoop` and `winget` package
managers or through Cygwin or [MSYS2](https://packages.msys2.org/base/gettext).

> [!WARNING]
> Unlike what some tutorials suggest, **we do not recommend installing `gettext`
> through GnuWin32**, since it hasn't been updated since 2010 and very likely
> does not work on modern Windows versions.
You can verify that `gettext` has been successfully installed by running the
command `gettext -V`. If everything went correctly, you should see an output like this:

```console
$ gettext -V
gettext (GNU gettext-runtime) 0.21.1
Copyright (C) 1995-2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Written by Ulrich Drepper.
```

With this, you're ready to localize.

## Locating translation files

All translation files lie in the `po/` directory, including the main *template*
file called `com.mitchellh.ghostty.pot`. **You may not edit this file.** The
template is generated automatically from Ghostty's code and resources, and are
intended to be regenerated by source contributors.

Instead, only edit the translation file corresponding to your language/locale,
identified via the its *locale name*: for example, `de_DE.UTF-8.po` would be
the translation file for German (language code `de`) as spoken in Germany
(country code `DE`). The GNU `gettext` manual contains
[further information about locale names](https://www.gnu.org/software/gettext/manual/gettext.html#Locale-Names-1),
including a list of language and country codes.

> [!NOTE]
> Ghostty enforces the convention that all parts of the locale, including the
> language code, country code, encoding (which is *always* UTF-8), and possible
> variants **must** be communicated in the file name. Files like `pt.po` are not
> acceptable, while `pt_BR.UTF-8.po` is. This is to allow us to more easily
> accomodate regional variants of a language in the future, and to reject
> translations that may not be applicable to all speakers of a language (e.g.
> an unqualified `zh.po` may contain terminology specific to Chinese speakers
> in Mainland China, which are not found in Taiwan. Using `zh_CN.UTF-8.po`
> would allow that difference to be communicated.)
> [!NOTE]
> If the translation file for your locale does not yet exist, see the
> ["Creating new translation files" section](#creating-new-translation-files)
> of this document on how to create one.
The `.po` file contains a list of entries that look like this:
```po
#. Translators: the category in the right-click context menu that contains split items for all directions
#: src/apprt/gtk/ui/1.0/menu-surface-context-menu.blp:38
# 译注:其他终端程序对 Split 皆有不同翻译,此处采取最直观的翻译方式
msgid "Split"
msgstr "分屏"
```

The `msgid` line contains the original string in English, and the `msgstr` line
should contain the translation for your language.

Lines beginning with `#` are comments, of which there are several kinds:

- **Ignore** comments that begin with `#:`: those are source comments that link
the string to source code or resource files.

- **Do** pay attention to comments beginning with `#.`, which are comments left
by develoeprs providing more context to the string.

- You may also leave comments beginning with `# `, which are specific to your
locale, and are intended to be read by other translators.

The first entry of the `.po` file has an empty `msgid`. This entry is special
as it stores the metadata related to the `.po` file itself, which usually does
not need to be modified.

## Creating new translation files

You can use the `msginit` tool to create new translation files.

Run the command below, optionally replacing `$LANG` with the name of a locale
that is *different* to your system locale, or if the `LANG` environmental
variable is not set.

> [!WARNING]
> **Make sure your selected locale uses the UTF-8 encoding, as it is the sole
> encoding supported by Ghostty.**
> If your locale historically uses a different encoding, such as ISO-8859-1
> for Western European languages or Shift-JIS for Japanese, you need to manually
> instruct `msginit` to use UTF-8.
```console
$ msginit -i po/com.mitchellh.ghostty.pot -l $LANG -o "po/$LANG.po"
```

`msginit` may prompt you for other information such as your email address,
which should be filled in accordingly. You can then add your translations
within the newly created translation file.

Afterwards, you need to update the list of known locales within Ghostty's
build system. To do so, open `src/build/GhosttyI18n.zig` and find the list
of locales under the `locale` variable, then append the full locale name
into the list. You should then be able to run `zig build` and see your
translations in action.

## Style Guide

These are general style guides for translations. Naturally, the specific
recommended standards will differ based on the specific language/locale,
but these should serve as a baseline for the tone and voice of any translation.

- Prefer an instructive, yet professional tone. In languages that exhibit
distinctions based on formality, prefer the formality that is expected
of instructive material on the internet. The user should be considered
an equal peer of the program and the translator, not an esteemed customer.

- Use simple to understand language and avoid jargon. Explain concepts that
may be familiar in an English-speaking context, but are uncommon in your
language.

- **Do not overly literally translate foreign concepts to your language.**
Care should be taken so that your translations make sense to a reader without
any background knowledge of the English source text. To *localize* is to
transfer a concept between languages, not to translate each word at face value.

- Be consistent with stylistic and tonal choices. Consult the translations
made by previous translators, and try to emulate them. Do not overwrite
someone else's hard work without justification.

- Make Ghostty fit in with surrounding applications. Follow existing
translations for terms and concepts if possible, even if they may be suboptimal.
Follow the writing styles proscribed by the human interface guidelines of
each platform Ghostty is available for, including the [GNOME Human Interface Guidelines](https://developer.gnome.org/hig/guidelines/writing-style.html)
on Linux, and [Apple's Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/writing)
on macOS.

Loading

0 comments on commit 69882c0

Please sign in to comment.