Skip to content

Conversation

@timothyqiu
Copy link
Member

@timothyqiu timothyqiu commented Nov 13, 2025

This PR is separated into 3 commits for easier review.

1. Add hints for Translation

There are two problems.

First, whether a message in Translation should be translated using get_message() or get_plural_message() depends on whether it was added using add_message() or add_plural_message(). But there is currently no way to know that information in code.

Second, even if we record the source of the message when adding it, we still cannot properly call get_plural_message(): The plural form of the source text remains unknown. This also means that we can't reliably restore a Translation loaded from PO files back to a PO file. For example, #106573 attempts to convert PO files to MO files on export, but it has to write ? for the msgid_plural field, which is technically wrong.

Considering godotengine/godot-proposals#7334, it'll also be better if metadata like comments and locations can be stored along with the message. The long-term goal is to make PO, CSV, and Translation interchangeable.

This PR added {get,set}_hint() for storing hints like untranslated plural, comments, and locations (references to the program's source code).

How a Translation is serialized to a dictionary (_{get,set}_messages()) is also updated:

For messages, keys can be either:

  • &"msgid"
  • [&"&msgctxt", &"msgid"]

Their values can be either:

  • &"msgstr[0]"
  • [&"msgstr[0], &"msgstr[1]", ...]

The optional hints are stored using an integer key. The value is another dictionary, mapping message keys to a hints dictionary which maps HINT_* to the hint value. Empty values are not stored for hints.

This makes the format compact. And it's still compatible with older versions as long as new features (context, plural forms, and hints) are not used.

2. Use plural hint when importing/exporting Translations

When importing CSV files and loading PO files, HINT_PLURAL is now set. For exisitng projects, .csv files have to be reimported to get this hint.

When generating the translation template, the parse result is now a Translation instead of a vector of custom structs. This lays the foundation to adding template generation related APIs in the future (for problems like #111074).

When implementing, I also fixed some problems about the Translation APIs:

  • Added missing has_message()
  • Changed internal version of get_message_list() to yield MessageKeys instead of StringNames. The scripting API version concats msgid and msgctxt for compatibility.

Usages of other hints (comments and locations) are not added in this PR. I think that should be done in separate ones.

  • They are information for the translators, so not as important as the plural hint.
  • The PO parsing code is a complex state machine, hard to change.
  • The CSV importer does not have the concept of comments and locations yet. CSV template generation produces these two metadata as ignored columns.

3. Add translation viewer

Currently, the inspector only shows Locale and Plural Rules Override for Translations. This commit adds a View Messages button for inspecting contents in the resource.

This makes it easier to debug translation related problems and dogfoods the new API.

Peek.2025-11-13.17-05.mp4

*The screencast is using the Classic style theme. Trees don't have frames when using the modern style theme, which makes many editor UI look broken :(

When implementing, I also added get_plural_forms() to Translation for getting all possible plural forms for a given message.

Test project: translation-hints.zip

@timothyqiu timothyqiu requested review from a team as code owners November 13, 2025 10:24
Added `{get,set}_hint()` for storing hints like untranslated plural, comments,
and references to the program's source code.

These hints does not affect how translation works, but is useful for tools.

How a `Translation` is serialized to a dictionary is also updated:

For messages, keys can be either:

- `&"msgid"`
- `[&"&msgctxt", &"msgid"]`

Their values can be either:

- `&"msgstr[0]"`
- `[&"msgstr[0], &"msgstr[1]", ...]`

The optional hints are storaged using an integer key. The value is another
dictionary, mapping message keys to a hints dictionary which maps `TYPE_*` to
the hint value. Empty values are not stored for hints.

This makes the format compact. And it's still compatible with older versions as
long as new features (context, plural forms, and hints) are not used.
`HINT_PLURAL` is set when importing CSV files and loading PO files.

When generating the translation template, the parse result is now a
`Translation` instead of a vector of custom struct. This makes it easier to
add template generation related APIs.

Generating a template without any messages is now allowed.

Changes to `Translation`:

- Added `has_message()`
- Changed internal version of `get_message_list()` to yield `MessageKey`s
instead of `StringName`s
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants