mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
gtk: add localization support, take 3
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.
This commit is contained in:
@ -21,6 +21,15 @@ All issues are actionable. Pick one and start working on it. Thank you.
|
|||||||
If you need help or guidance, comment on the issue. Issues that are extra
|
If you need help or guidance, comment on the issue. Issues that are extra
|
||||||
friendly to new contributors are tagged with "contributor friendly".
|
friendly to new contributors are tagged with "contributor friendly".
|
||||||
|
|
||||||
|
**I'd like to translate Ghostty to my language!**
|
||||||
|
|
||||||
|
We have written a [Translator's Guide](po/README_CONTRIBUTORS.md) for
|
||||||
|
everyone interested in contributing translations to Ghostty.
|
||||||
|
Translations usually do not need to go through the process of issue triage
|
||||||
|
and you can submit pull requests directly, although please make sure that
|
||||||
|
our [Style Guide](po/README_CONTRIBUTORS.md#style-guide) is followed before
|
||||||
|
submission.
|
||||||
|
|
||||||
**I have a bug!**
|
**I have a bug!**
|
||||||
|
|
||||||
1. Search the issue tracker and discussions for similar issues.
|
1. Search the issue tracker and discussions for similar issues.
|
||||||
|
@ -11,6 +11,7 @@ pub fn build(b: *std.Build) !void {
|
|||||||
|
|
||||||
// Ghostty resources like terminfo, shell integration, themes, etc.
|
// Ghostty resources like terminfo, shell integration, themes, etc.
|
||||||
const resources = try buildpkg.GhosttyResources.init(b, &config);
|
const resources = try buildpkg.GhosttyResources.init(b, &config);
|
||||||
|
const i18n = try buildpkg.GhosttyI18n.init(b, &config);
|
||||||
|
|
||||||
// Ghostty dependencies used by many artifacts.
|
// Ghostty dependencies used by many artifacts.
|
||||||
const deps = try buildpkg.SharedDeps.init(b, &config);
|
const deps = try buildpkg.SharedDeps.init(b, &config);
|
||||||
@ -39,6 +40,7 @@ pub fn build(b: *std.Build) !void {
|
|||||||
if (config.app_runtime != .none) {
|
if (config.app_runtime != .none) {
|
||||||
exe.install();
|
exe.install();
|
||||||
resources.install();
|
resources.install();
|
||||||
|
i18n.install();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Libghostty
|
// Libghostty
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
gobject-introspection,
|
gobject-introspection,
|
||||||
libadwaita,
|
libadwaita,
|
||||||
blueprint-compiler,
|
blueprint-compiler,
|
||||||
|
gettext,
|
||||||
adwaita-icon-theme,
|
adwaita-icon-theme,
|
||||||
hicolor-icon-theme,
|
hicolor-icon-theme,
|
||||||
harfbuzz,
|
harfbuzz,
|
||||||
@ -127,6 +128,9 @@ in
|
|||||||
# wasm
|
# wasm
|
||||||
wabt
|
wabt
|
||||||
wasmtime
|
wasmtime
|
||||||
|
|
||||||
|
# Localization
|
||||||
|
gettext
|
||||||
]
|
]
|
||||||
++ lib.optionals stdenv.hostPlatform.isLinux [
|
++ lib.optionals stdenv.hostPlatform.isLinux [
|
||||||
# My nix shell environment installs the non-interactive version
|
# My nix shell environment installs the non-interactive version
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
libadwaita,
|
libadwaita,
|
||||||
blueprint-compiler,
|
blueprint-compiler,
|
||||||
libxml2,
|
libxml2,
|
||||||
|
gettext,
|
||||||
wrapGAppsHook4,
|
wrapGAppsHook4,
|
||||||
gsettings-desktop-schemas,
|
gsettings-desktop-schemas,
|
||||||
git,
|
git,
|
||||||
@ -63,6 +64,7 @@ in
|
|||||||
../dist/linux
|
../dist/linux
|
||||||
../images
|
../images
|
||||||
../include
|
../include
|
||||||
|
../po
|
||||||
../pkg
|
../pkg
|
||||||
../src
|
../src
|
||||||
../vendor
|
../vendor
|
||||||
@ -86,6 +88,7 @@ in
|
|||||||
wrapGAppsHook4
|
wrapGAppsHook4
|
||||||
blueprint-compiler
|
blueprint-compiler
|
||||||
libxml2 # for xmllint
|
libxml2 # for xmllint
|
||||||
|
gettext
|
||||||
]
|
]
|
||||||
++ lib.optionals enableWayland [
|
++ lib.optionals enableWayland [
|
||||||
wayland-scanner
|
wayland-scanner
|
||||||
|
72
po/README_CONTRIBUTORS.md
Normal file
72
po/README_CONTRIBUTORS.md
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
# 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, translable strings are mainly sourced from Blueprint
|
||||||
|
files (located under `src/apprt/gtk/ui`). Blueprints have a native syntax for
|
||||||
|
translatable strings, which look like this:
|
||||||
|
|
||||||
|
```zig
|
||||||
|
// Translators: This is the name of the button that opens the about dialog.
|
||||||
|
title: _("About Ghostty");
|
||||||
|
```
|
||||||
|
|
||||||
|
The `// Translators:` comment provides additional context to the translator
|
||||||
|
if the string itself is unclear as to what its purpose is or where it's located.
|
||||||
|
|
||||||
|
By default identical strings are collapsed together into one translatable entry.
|
||||||
|
To avoid this, assign a _context_ to the string:
|
||||||
|
|
||||||
|
```zig
|
||||||
|
label: C_("menu action", "Copy");
|
||||||
|
```
|
||||||
|
|
||||||
|
Translatable strings can also be sourced from Zig source files. This is useful
|
||||||
|
when the string must be chosen dynamically at runtime, or when it requires
|
||||||
|
additional formatting. The `i18n.` prefix is necessary as `_` is not allowed
|
||||||
|
as a bare identifier in Zig.
|
||||||
|
|
||||||
|
```zig
|
||||||
|
const i18n = @import("i18n.zig");
|
||||||
|
|
||||||
|
const text = if (awesome)
|
||||||
|
i18n._("My awesome label :D")
|
||||||
|
else
|
||||||
|
i18n._("My not-so-awesome label :(");
|
||||||
|
|
||||||
|
const label = gtk.Label.new(text);
|
||||||
|
```
|
||||||
|
|
||||||
|
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.
|
183
po/README_TRANSLATORS.md
Normal file
183
po/README_TRANSLATORS.md
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
# Localizing Ghostty: The Translators' Guide
|
||||||
|
|
||||||
|
First of all, thanks for helping us localize Ghostty!
|
||||||
|
|
||||||
|
To begin contributing, please make sure that you have installed `gettext`
|
||||||
|
on your system, which should be available under the `gettext` package
|
||||||
|
for most Linux and macOS package managers.
|
||||||
|
|
||||||
|
You can install `gettext` on Windows in a few ways:
|
||||||
|
|
||||||
|
- Through [an installer](https://mlocati.github.io/articles/gettext-iconv-windows.html);
|
||||||
|
- Through package managers like Scoop (`scoop install gettext`) and
|
||||||
|
WinGet (`winget install gettext`) which repackages the aforementioned installer;
|
||||||
|
- Through Unix-like environments like [Cygwin](https://cygwin.com/cygwin/packages/summary/gettext.html)
|
||||||
|
and [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!
|
||||||
|
|
||||||
|
## Editing translation files
|
||||||
|
|
||||||
|
All translation files lie in the `po/` directory, including the main _template_
|
||||||
|
file called `com.mitchellh.ghostty.pot`. **Do not edit this file.** The
|
||||||
|
template is generated automatically from Ghostty's code and resources, and are
|
||||||
|
intended to be regenerated by code contributors. If there is a problem with
|
||||||
|
the template file, please reach out to a code contributor.
|
||||||
|
|
||||||
|
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]
|
||||||
|
>
|
||||||
|
> 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 皆有不同翻译,此处采取最直观的翻译方式
|
||||||
|
msgctxt "Context menu"
|
||||||
|
msgid "Split"
|
||||||
|
msgstr "分屏"
|
||||||
|
```
|
||||||
|
|
||||||
|
The `msgid` line contains the original string in English, and the `msgstr` line
|
||||||
|
should contain the translation for your language. Occasionally there will also
|
||||||
|
be a `msgctxt` line that differentiates strings that are identical in English,
|
||||||
|
based on its context.
|
||||||
|
|
||||||
|
Lines beginning with `#` are comments, of which there are several kinds:
|
||||||
|
|
||||||
|
- Pay attention to comments beginning with `#.`. They are comments left
|
||||||
|
by _developers_ providing more context to the string.
|
||||||
|
|
||||||
|
- Comments that begin with `#:` are _source comments_: they link
|
||||||
|
the string to source code or resource files. You normally don't need to
|
||||||
|
consider these in your translations.
|
||||||
|
|
||||||
|
- You may also leave comments of your own to be read by _other translators_,
|
||||||
|
beginning with `# `. These comments are specific to your locale and don't
|
||||||
|
affect translators in other locales.
|
||||||
|
|
||||||
|
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. You usually do
|
||||||
|
not need to modify it.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ msginit -i po/com.mitchellh.ghostty.pot -l $LANG -o "po/$LANG.po"
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
>
|
||||||
|
> Ghostty enforces the convention that all parts of the locale, including the
|
||||||
|
> language code, country code, encoding, and possible regional 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 accommodate 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.)
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
>
|
||||||
|
> **Make sure your selected locale uses the UTF-8 encoding, as it is the sole
|
||||||
|
> encoding supported by Ghostty and its dependencies.**
|
||||||
|
>
|
||||||
|
> For backwards compatibility reasons, some locales may default to a non-UTF-8
|
||||||
|
> encoding when an encoding is not specified. For instance, `de_DE` defaults
|
||||||
|
> to using the legacy ISO-8859-1 encoding, which is incompatible with UTF-8.
|
||||||
|
> You need to manually instruct `msginit` to use UTF-8 in these instances,
|
||||||
|
> by appending `.UTF-8` to the end of the locale name (e.g. `de_DE.UTF-8`).
|
||||||
|
|
||||||
|
`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.
|
||||||
|
|
||||||
|
```zig
|
||||||
|
const locales = [_][]const u8{
|
||||||
|
"zh_CN.UTF-8",
|
||||||
|
// <- Add your locale here
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You should then be able to run `zig build` and see your translations in action.
|
||||||
|
|
||||||
|
## Style Guide
|
||||||
|
|
||||||
|
These are general style guidelines 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 substantial justification.
|
||||||
|
|
||||||
|
- **Make Ghostty fit in with surrounding applications.**
|
||||||
|
|
||||||
|
Follow existing translations for terms and concepts if possible, even when
|
||||||
|
they are suboptimal. Follow the writing styles prescribed 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.
|
178
po/com.mitchellh.ghostty.pot
Normal file
178
po/com.mitchellh.ghostty.pot
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) YEAR Mitchell Hashimoto
|
||||||
|
# This file is distributed under the same license as the com.mitchellh.ghostty package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
|
#
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: com.mitchellh.ghostty\n"
|
||||||
|
"Report-Msgid-Bugs-To: m@mitchellh.com\n"
|
||||||
|
"POT-Creation-Date: 2025-02-26 14:34+0100\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"Language: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:5
|
||||||
|
msgid "Change Terminal Title"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:6
|
||||||
|
msgid "Leave blank to restore the default title."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:9
|
||||||
|
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:10
|
||||||
|
msgid "Cancel"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:10
|
||||||
|
msgid "OK"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:6
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:6
|
||||||
|
msgid "Copy"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:11
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:11
|
||||||
|
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:11
|
||||||
|
msgid "Paste"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:18
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:73
|
||||||
|
msgid "Clear"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:23
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:78
|
||||||
|
msgid "Reset"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:30
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:42
|
||||||
|
msgid "Split"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:33
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:45
|
||||||
|
msgid "Change Title…"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:38
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:50
|
||||||
|
msgid "Split Up"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:43
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:55
|
||||||
|
msgid "Split Down"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:48
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:60
|
||||||
|
msgid "Split Left"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:53
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:65
|
||||||
|
msgid "Split Right"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:59
|
||||||
|
msgid "Tab"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:62
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:30
|
||||||
|
#: src/apprt/gtk/Window.zig:239
|
||||||
|
msgid "New Tab"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:67
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:35
|
||||||
|
msgid "Close Tab"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:73
|
||||||
|
msgid "Window"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:76
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:18
|
||||||
|
msgid "New Window"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:81
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:23
|
||||||
|
msgid "Close Window"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:89
|
||||||
|
msgid "Config"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:92
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:90
|
||||||
|
msgid "Open Configuration"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:97
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:95
|
||||||
|
msgid "Reload Configuration"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:85
|
||||||
|
msgid "Terminal Inspector"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:102
|
||||||
|
msgid "About Ghostty"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:107
|
||||||
|
msgid "Quit"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:6
|
||||||
|
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:6
|
||||||
|
msgid "Authorize Clipboard Access"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:7
|
||||||
|
msgid ""
|
||||||
|
"An application is attempting to read from the clipboard. The current "
|
||||||
|
"clipboard contents are shown below."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:10
|
||||||
|
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:10
|
||||||
|
msgid "Deny"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:11
|
||||||
|
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:11
|
||||||
|
msgid "Allow"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:7
|
||||||
|
msgid ""
|
||||||
|
"An application is attempting to write to the clipboard. The current "
|
||||||
|
"clipboard contents are shown below."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:6
|
||||||
|
msgid "Warning: Potentially Unsafe Paste"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:7
|
||||||
|
msgid ""
|
||||||
|
"Pasting this text into the terminal may be dangerous as it looks like some "
|
||||||
|
"commands may be executed."
|
||||||
|
msgstr ""
|
@ -76,6 +76,7 @@ parts:
|
|||||||
- libxml2-utils
|
- libxml2-utils
|
||||||
- git
|
- git
|
||||||
- patchelf
|
- patchelf
|
||||||
|
- gettext
|
||||||
override-build: |
|
override-build: |
|
||||||
craftctl set version=$(git describe --abbrev=8)
|
craftctl set version=$(git describe --abbrev=8)
|
||||||
$CRAFT_PART_SRC/../../zig/src/zig build -Dpatch-rpath=\$ORIGIN/../usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:/snap/core24/current/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR -Doptimize=ReleaseFast
|
$CRAFT_PART_SRC/../../zig/src/zig build -Dpatch-rpath=\$ORIGIN/../usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:/snap/core24/current/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR -Doptimize=ReleaseFast
|
||||||
|
@ -33,6 +33,7 @@ const ConfigErrorsWindow = @import("ConfigErrorsWindow.zig");
|
|||||||
const ClipboardConfirmationWindow = @import("ClipboardConfirmationWindow.zig");
|
const ClipboardConfirmationWindow = @import("ClipboardConfirmationWindow.zig");
|
||||||
const Split = @import("Split.zig");
|
const Split = @import("Split.zig");
|
||||||
const c = @import("c.zig").c;
|
const c = @import("c.zig").c;
|
||||||
|
const i18n = @import("i18n.zig");
|
||||||
const version = @import("version.zig");
|
const version = @import("version.zig");
|
||||||
const inspector = @import("inspector.zig");
|
const inspector = @import("inspector.zig");
|
||||||
const key = @import("key.zig");
|
const key = @import("key.zig");
|
||||||
@ -93,6 +94,11 @@ quit_timer: union(enum) {
|
|||||||
pub fn init(core_app: *CoreApp, opts: Options) !App {
|
pub fn init(core_app: *CoreApp, opts: Options) !App {
|
||||||
_ = opts;
|
_ = opts;
|
||||||
|
|
||||||
|
// This can technically be placed *anywhere* because we don't have any
|
||||||
|
// localized log messages. It just has to be placed before any localized
|
||||||
|
// widgets are drawn.
|
||||||
|
try i18n.init(core_app.alloc);
|
||||||
|
|
||||||
// Log our GTK version
|
// Log our GTK version
|
||||||
log.info("GTK version build={d}.{d}.{d} runtime={d}.{d}.{d}", .{
|
log.info("GTK version build={d}.{d}.{d} runtime={d}.{d}.{d}", .{
|
||||||
c.GTK_MAJOR_VERSION,
|
c.GTK_MAJOR_VERSION,
|
||||||
|
35
src/apprt/gtk/i18n.zig
Normal file
35
src/apprt/gtk/i18n.zig
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
//! I18n support for the GTK frontend based on gettext/libintl
|
||||||
|
//!
|
||||||
|
//! This is normally built into the C standard library for the *vast* majority
|
||||||
|
//! of users who use glibc, but for musl users we fall back to the `gettext-tiny`
|
||||||
|
//! stub implementation which provides all of the necessary interfaces.
|
||||||
|
//! Musl users who do want to use localization should know what they need to do.
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const global = &@import("../../global.zig").state;
|
||||||
|
const build_config = @import("../../build_config.zig");
|
||||||
|
|
||||||
|
const log = std.log.scoped(.i18n);
|
||||||
|
|
||||||
|
pub fn init(alloc: std.mem.Allocator) !void {
|
||||||
|
const resources_dir = global.resources_dir orelse {
|
||||||
|
log.warn("resource dir not found; not localizing", .{});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
const share_dir = std.fs.path.dirname(resources_dir) orelse {
|
||||||
|
log.warn("resource dir not placed in a share/ directory; not localizing", .{});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
const locale_dir = try std.fs.path.joinZ(alloc, &.{ share_dir, "locale" });
|
||||||
|
defer alloc.free(locale_dir);
|
||||||
|
|
||||||
|
// The only way these calls can fail is if we're out of memory
|
||||||
|
_ = bindtextdomain(build_config.bundle_id, locale_dir.ptr) orelse return error.OutOfMemory;
|
||||||
|
_ = textdomain(build_config.bundle_id) orelse return error.OutOfMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manually include function definitions for the gettext functions
|
||||||
|
// as libintl.h isn't always easily available (e.g. in musl)
|
||||||
|
extern fn bindtextdomain(domainname: [*:0]const u8, dirname: [*:0]const u8) ?[*:0]const u8;
|
||||||
|
extern fn textdomain(domainname: [*:0]const u8) ?[*:0]const u8;
|
86
src/build/GhosttyI18n.zig
Normal file
86
src/build/GhosttyI18n.zig
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
const GhosttyI18n = @This();
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const Config = @import("Config.zig");
|
||||||
|
const gresource = @import("../apprt/gtk/gresource.zig");
|
||||||
|
|
||||||
|
const domain = "com.mitchellh.ghostty";
|
||||||
|
|
||||||
|
const locales = [_][]const u8{};
|
||||||
|
|
||||||
|
owner: *std.Build,
|
||||||
|
steps: []*std.Build.Step,
|
||||||
|
|
||||||
|
pub fn init(b: *std.Build, cfg: *const Config) !GhosttyI18n {
|
||||||
|
var steps = std.ArrayList(*std.Build.Step).init(b.allocator);
|
||||||
|
errdefer steps.deinit();
|
||||||
|
|
||||||
|
addUpdateStep(b);
|
||||||
|
|
||||||
|
if (cfg.app_runtime == .gtk) {
|
||||||
|
// Output the .mo files used by the GTK apprt
|
||||||
|
inline for (locales) |locale| {
|
||||||
|
const msgfmt = b.addSystemCommand(&.{ "msgfmt", "-o", "-" });
|
||||||
|
|
||||||
|
msgfmt.addFileArg(b.path("po/" ++ locale ++ ".po"));
|
||||||
|
|
||||||
|
try steps.append(&b.addInstallFile(
|
||||||
|
msgfmt.captureStdOut(),
|
||||||
|
std.fmt.comptimePrint("share/locale/{s}/LC_MESSAGES/{s}.mo", .{ locale, domain }),
|
||||||
|
).step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.owner = b,
|
||||||
|
.steps = try steps.toOwnedSlice(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn install(self: *const GhosttyI18n) void {
|
||||||
|
for (self.steps) |step| self.owner.getInstallStep().dependOn(step);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn addUpdateStep(b: *std.Build) void {
|
||||||
|
const pot_step = b.step("update-translations", "Update translation files");
|
||||||
|
|
||||||
|
const gettext = b.addSystemCommand(&.{
|
||||||
|
"xgettext",
|
||||||
|
"--language=C", // Silence the "unknown extension" errors
|
||||||
|
"--from-code=UTF-8",
|
||||||
|
"--add-comments=Translators",
|
||||||
|
"--keyword=_",
|
||||||
|
"--keyword=C_:1c,2",
|
||||||
|
"--package-name=" ++ domain,
|
||||||
|
"--msgid-bugs-address=m@mitchellh.com",
|
||||||
|
"--copyright-holder=Mitchell Hashimoto",
|
||||||
|
"-o",
|
||||||
|
"-",
|
||||||
|
});
|
||||||
|
|
||||||
|
inline for (gresource.blueprint_files) |blp| {
|
||||||
|
// We avoid using addFileArg here since the full, absolute file path
|
||||||
|
// would be added to the file as its location, which differs for
|
||||||
|
// everyone's checkout of the repository.
|
||||||
|
// This comes at a cost of losing per-file caching, of course.
|
||||||
|
gettext.addArg(std.fmt.comptimePrint("src/apprt/gtk/ui/{[major]}.{[minor]}/{[name]s}.blp", blp));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't make Zig cache it
|
||||||
|
gettext.has_side_effects = true;
|
||||||
|
|
||||||
|
const new_pot = gettext.captureStdOut();
|
||||||
|
|
||||||
|
const wf = b.addWriteFiles();
|
||||||
|
wf.addCopyFileToSource(new_pot, "po/" ++ domain ++ ".pot");
|
||||||
|
|
||||||
|
inline for (locales) |locale| {
|
||||||
|
const msgmerge = b.addSystemCommand(&.{ "msgmerge", "-q" });
|
||||||
|
msgmerge.addFileArg(b.path("po/" ++ locale ++ ".po"));
|
||||||
|
msgmerge.addFileArg(gettext.captureStdOut());
|
||||||
|
|
||||||
|
wf.addCopyFileToSource(msgmerge.captureStdOut(), "po/" ++ locale ++ ".po");
|
||||||
|
}
|
||||||
|
|
||||||
|
pot_step.dependOn(&wf.step);
|
||||||
|
}
|
@ -38,6 +38,7 @@ COPY ./dist/linux /src/dist/linux
|
|||||||
COPY ./images /src/images
|
COPY ./images /src/images
|
||||||
COPY ./include /src/include
|
COPY ./include /src/include
|
||||||
COPY ./pkg /src/pkg
|
COPY ./pkg /src/pkg
|
||||||
|
COPY ./po /src/po
|
||||||
COPY ./nix /src/nix
|
COPY ./nix /src/nix
|
||||||
COPY ./vendor /src/vendor
|
COPY ./vendor /src/vendor
|
||||||
COPY ./build.zig /src/build.zig
|
COPY ./build.zig /src/build.zig
|
||||||
|
@ -13,6 +13,7 @@ pub const GhosttyExe = @import("GhosttyExe.zig");
|
|||||||
pub const GhosttyFrameData = @import("GhosttyFrameData.zig");
|
pub const GhosttyFrameData = @import("GhosttyFrameData.zig");
|
||||||
pub const GhosttyLib = @import("GhosttyLib.zig");
|
pub const GhosttyLib = @import("GhosttyLib.zig");
|
||||||
pub const GhosttyResources = @import("GhosttyResources.zig");
|
pub const GhosttyResources = @import("GhosttyResources.zig");
|
||||||
|
pub const GhosttyI18n = @import("GhosttyI18n.zig");
|
||||||
pub const GhosttyXCFramework = @import("GhosttyXCFramework.zig");
|
pub const GhosttyXCFramework = @import("GhosttyXCFramework.zig");
|
||||||
pub const GhosttyWebdata = @import("GhosttyWebdata.zig");
|
pub const GhosttyWebdata = @import("GhosttyWebdata.zig");
|
||||||
pub const HelpStrings = @import("HelpStrings.zig");
|
pub const HelpStrings = @import("HelpStrings.zig");
|
||||||
|
Reference in New Issue
Block a user