diff --git a/docs/examples.rst b/docs/examples.rst
index 27617c3..d3b4480 100644
--- a/docs/examples.rst
+++ b/docs/examples.rst
@@ -91,7 +91,7 @@ Translations
~~~~~~~~~~~~
Use ``_("...")`` to mark strings as translatable. You can put a comment for
-translators on the line above.
+translators on the line above if needed.
.. code-block::
@@ -100,6 +100,18 @@ translators on the line above.
label: _("Hello, world!");
}
+Use ``C_("context", "...")`` to add a *message context* to a string to
+disambiguate it, in case the same string appears in different places. Remember,
+two strings might be the same in one language but different in another depending
+on context.
+
+.. code-block::
+
+ Gtk.Label label {
+ /* Translators: This is a section in the preferences window */
+ label: C_("preferences window", "Hello, world!");
+ }
+
Referencing objects by ID
~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/gtkblueprinttool/ast.py b/gtkblueprinttool/ast.py
index 886369d..9b755bc 100644
--- a/gtkblueprinttool/ast.py
+++ b/gtkblueprinttool/ast.py
@@ -275,7 +275,6 @@ class Property(AstNode):
def emit_xml(self, xml: XmlEmitter):
values = self.children[Value]
value = values[0] if len(values) == 1 else None
- translatable = isinstance(value, TranslatedStringValue)
bind_flags = []
if self.tokens["sync_create"]:
@@ -286,12 +285,14 @@ class Property(AstNode):
props = {
"name": self.tokens["name"],
- "translatable": "true" if translatable else None,
"bind-source": self.tokens["bind_source"],
"bind-property": self.tokens["bind_property"],
"bind-flags": bind_flags_str,
}
+ if isinstance(value, TranslatedStringValue):
+ props = { **props, **value.attrs }
+
if len(self.children[Object]) == 1:
xml.start_tag("property", **props)
self.children[Object][0].emit_xml(xml)
@@ -300,10 +301,7 @@ class Property(AstNode):
xml.put_self_closing("property", **props)
else:
xml.start_tag("property", **props)
- if translatable:
- xml.put_text(value.string)
- else:
- value.emit_xml(xml)
+ value.emit_xml(xml)
xml.end_tag()
@@ -357,11 +355,14 @@ class Value(ast.AstNode):
class TranslatedStringValue(Value):
@property
- def string(self):
- return self.tokens["value"]
+ def attrs(self):
+ attrs = { "translatable": "true" }
+ if "context" in self.tokens:
+ attrs["context"] = self.tokens["context"]
+ return attrs
- def emit_xml(self, xml):
- raise CompilerBugError("TranslatedStringValues must be handled by the parent AST node")
+ def emit_xml(self, xml: XmlEmitter):
+ xml.put_text(self.tokens["value"])
class LiteralValue(Value):
@@ -470,16 +471,13 @@ class BaseAttribute(AstNode):
def emit_xml(self, xml: XmlEmitter):
value = self.children[Value][0]
- translatable = isinstance(value, TranslatedStringValue)
- attrs = {
- self.attr_name: self.tokens["name"],
- "translatable": "true" if translatable else None
- }
+ attrs = { self.attr_name: self.tokens["name"] }
+
+ if isinstance(value, TranslatedStringValue):
+ attrs = { **attrs, **value.attrs }
+
xml.start_tag(self.tag_name, **attrs)
- if translatable:
- xml.put_text(value.string)
- else:
- value.emit_xml(xml)
+ value.emit_xml(xml)
xml.end_tag()
diff --git a/gtkblueprinttool/extensions/gtk_string_list.py b/gtkblueprinttool/extensions/gtk_string_list.py
index db92331..1f6b68a 100644
--- a/gtkblueprinttool/extensions/gtk_string_list.py
+++ b/gtkblueprinttool/extensions/gtk_string_list.py
@@ -48,12 +48,9 @@ class Item(AstNode):
def emit_xml(self, xml: XmlEmitter):
value = self.children[Value][0]
- translatable = isinstance(value, TranslatedStringValue)
- xml.start_tag("item", translatable="true" if translatable else None)
- if translatable:
- xml.put_text(value.string)
- else:
- value.emit_xml(xml)
+ attrs = value.attrs if isinstance(value, TranslatedStringValue) else {}
+ xml.start_tag("item", **attrs)
+ value.emit_xml(xml)
xml.end_tag()
diff --git a/gtkblueprinttool/parser_utils.py b/gtkblueprinttool/parser_utils.py
index ce9687d..ec10ebd 100644
--- a/gtkblueprinttool/parser_utils.py
+++ b/gtkblueprinttool/parser_utils.py
@@ -60,11 +60,22 @@ flags_value = Group(
translated_string = Group(
ast.TranslatedStringValue,
- Sequence(
- Keyword("_"),
- OpenParen(),
- UseQuoted("value").expected("a quoted string"),
- CloseParen().expected("`)`"),
+ AnyOf(
+ Sequence(
+ Keyword("_"),
+ OpenParen(),
+ UseQuoted("value").expected("a quoted string"),
+ CloseParen().expected("`)`"),
+ ),
+ Sequence(
+ Keyword("C_"),
+ OpenParen(),
+ UseQuoted("context").expected("a quoted string"),
+ Comma(),
+ UseQuoted("value").expected("a quoted string"),
+ Optional(Comma()),
+ CloseParen().expected("`)`"),
+ ),
),
)
diff --git a/tests/samples/translated.blp b/tests/samples/translated.blp
index c1cde19..d926754 100644
--- a/tests/samples/translated.blp
+++ b/tests/samples/translated.blp
@@ -3,3 +3,6 @@ using Gtk 4.0;
Label {
label: _("Hello, world!");
}
+Label {
+ label: C_("translation context", "Hello");
+}
diff --git a/tests/samples/translated.ui b/tests/samples/translated.ui
index 19cf381..6f84d40 100644
--- a/tests/samples/translated.ui
+++ b/tests/samples/translated.ui
@@ -4,4 +4,7 @@
+