mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-05-05 15:33:00 +00: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:
parent
4e5e4a7c2f
commit
9360afd29f
@ -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
|
||||
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!**
|
||||
|
||||
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.
|
||||
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);
|
||||
@ -39,6 +40,7 @@ pub fn build(b: *std.Build) !void {
|
||||
if (config.app_runtime != .none) {
|
||||
exe.install();
|
||||
resources.install();
|
||||
i18n.install();
|
||||
}
|
||||
|
||||
// Libghostty
|
||||
|
@ -34,6 +34,7 @@
|
||||
gobject-introspection,
|
||||
libadwaita,
|
||||
blueprint-compiler,
|
||||
gettext,
|
||||
adwaita-icon-theme,
|
||||
hicolor-icon-theme,
|
||||
harfbuzz,
|
||||
@ -127,6 +128,9 @@ in
|
||||
# wasm
|
||||
wabt
|
||||
wasmtime
|
||||
|
||||
# Localization
|
||||
gettext
|
||||
]
|
||||
++ lib.optionals stdenv.hostPlatform.isLinux [
|
||||
# My nix shell environment installs the non-interactive version
|
||||
|
@ -17,6 +17,7 @@
|
||||
libadwaita,
|
||||
blueprint-compiler,
|
||||
libxml2,
|
||||
gettext,
|
||||
wrapGAppsHook4,
|
||||
gsettings-desktop-schemas,
|
||||
git,
|
||||
@ -63,6 +64,7 @@ in
|
||||
../dist/linux
|
||||
../images
|
||||
../include
|
||||
../po
|
||||
../pkg
|
||||
../src
|
||||
../vendor
|
||||
@ -86,6 +88,7 @@ in
|
||||
wrapGAppsHook4
|
||||
blueprint-compiler
|
||||
libxml2 # for xmllint
|
||||
gettext
|
||||
]
|
||||
++ lib.optionals enableWayland [
|
||||
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
|
||||
- git
|
||||
- patchelf
|
||||
- gettext
|
||||
override-build: |
|
||||
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
|
||||
|
@ -33,6 +33,7 @@ const ConfigErrorsWindow = @import("ConfigErrorsWindow.zig");
|
||||
const ClipboardConfirmationWindow = @import("ClipboardConfirmationWindow.zig");
|
||||
const Split = @import("Split.zig");
|
||||
const c = @import("c.zig").c;
|
||||
const i18n = @import("i18n.zig");
|
||||
const version = @import("version.zig");
|
||||
const inspector = @import("inspector.zig");
|
||||
const key = @import("key.zig");
|
||||
@ -93,6 +94,11 @@ quit_timer: union(enum) {
|
||||
pub fn init(core_app: *CoreApp, opts: Options) !App {
|
||||
_ = 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.info("GTK version build={d}.{d}.{d} runtime={d}.{d}.{d}", .{
|
||||
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 ./include /src/include
|
||||
COPY ./pkg /src/pkg
|
||||
COPY ./po /src/po
|
||||
COPY ./nix /src/nix
|
||||
COPY ./vendor /src/vendor
|
||||
COPY ./build.zig /src/build.zig
|
||||
|
@ -13,6 +13,7 @@ pub const GhosttyExe = @import("GhosttyExe.zig");
|
||||
pub const GhosttyFrameData = @import("GhosttyFrameData.zig");
|
||||
pub const GhosttyLib = @import("GhosttyLib.zig");
|
||||
pub const GhosttyResources = @import("GhosttyResources.zig");
|
||||
pub const GhosttyI18n = @import("GhosttyI18n.zig");
|
||||
pub const GhosttyXCFramework = @import("GhosttyXCFramework.zig");
|
||||
pub const GhosttyWebdata = @import("GhosttyWebdata.zig");
|
||||
pub const HelpStrings = @import("HelpStrings.zig");
|
||||
|
Loading…
x
Reference in New Issue
Block a user