morph 0.1.0
Asynchronous bridge between GUI thread and business models
Loading...
Searching...
No Matches
i18n.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2
3#pragma once
4
5/// @file forms/i18n.hpp
6/// @brief Stable, schema-derived message-key vocabulary for renderer i18n.
7///
8/// A renderer that wants to translate a piece of GUI display text (a field's
9/// label/help/placeholder, a layout group's title, a cross-field rule's
10/// violation message, a wizard's or app-shell's title) needs a **stable
11/// catalog key** to look that text up under. This header is the single
12/// source of truth for how such a key is *derived* from identifiers the
13/// schema (or the `actionType` label a renderer already has, per
14/// `docs/spec/forms/forms.md`'s renderer contract) already carries:
15/// `morph::model::ActionTraits<A>::typeId()`, a reflected wire field name, or
16/// a 0-based index into a schema array (`x-layout.groups`, `x-rules`, a
17/// wizard's steps, an app's menu).
18///
19/// No key derived here is written into the schema — a renderer computes it
20/// itself from data it already has, so the common (no-override) case adds
21/// zero bytes to any schema. See `morph::render::resolveText`
22/// (`render/i18n.hpp`) for how a derived key is looked up against a
23/// host-supplied catalog, with the schema's authored literal text as the
24/// fallback on a miss.
25
26#include <cstddef>
27#include <cstdint>
28#include <optional>
29#include <string>
30#include <string_view>
31
32namespace morph::forms::i18n {
33
34/// @brief Which of a field's three translatable texts a message key names.
35enum class FieldSlot : std::uint8_t {
36 Label, ///< The field's display label (`<stem>.label`).
37 Help, ///< The field's help/description text (`<stem>.help`).
38 Placeholder ///< The field's empty-state placeholder text (`<stem>.placeholder`).
39};
40
41/// @brief The catalog-key suffix for @p slot.
42/// @param slot The field text slot.
43/// @return `"label"`, `"help"`, or `"placeholder"`.
44[[nodiscard]] constexpr std::string_view fieldSlotName(FieldSlot slot) noexcept {
45 switch (slot) {
47 return "label";
48 case FieldSlot::Help:
49 return "help";
51 return "placeholder";
52 default:
53 // Unreachable through any real code path: FieldSlot is a closed
54 // enum and every enumerator is already handled explicitly above.
55 // This arm only exists to satisfy the compiler that the function
56 // returns on every enum value, including one manufactured by an
57 // out-of-range `static_cast` -- mirrors ruleKindName's identical
58 // default: arm in forms.hpp.
59 return "label";
60 }
61}
62
63/// @brief Appends `.<slotName>` to a message-key stem.
64/// @param stem A message-key stem: a derived `<actionTypeId>.<wireField>` or
65/// a host-declared override (a field's `x-i18nKey`).
66/// @param slot Which field text this key names.
67/// @return `"<stem>.<slotName>"`.
68[[nodiscard]] inline std::string withSlot(std::string_view stem, FieldSlot slot) {
69 std::string key{stem};
70 key += '.';
71 key += fieldSlotName(slot);
72 return key;
73}
74
75/// @brief The derived message-key stem for a field: `<actionTypeId>.<wireField>`.
76/// @param actionTypeId `ActionTraits<A>::typeId()` of the action the field belongs to.
77/// @param wireField The field's wire key (its reflected member name).
78/// @return The stem, before the `.label` / `.help` / `.placeholder` suffix.
79[[nodiscard]] inline std::string fieldKeyStem(std::string_view actionTypeId, std::string_view wireField) {
80 std::string stem{actionTypeId};
81 stem += '.';
82 stem += wireField;
83 return stem;
84}
85
86/// @brief The fully-derived message key for one field text slot.
87/// @param actionTypeId `ActionTraits<A>::typeId()` of the action the field belongs to.
88/// @param wireField The field's wire key.
89/// @param slot Which of the field's texts this key names.
90/// @return `"<actionTypeId>.<wireField>.<slot>"`.
91[[nodiscard]] inline std::string fieldKey(std::string_view actionTypeId, std::string_view wireField, FieldSlot slot) {
92 return withSlot(fieldKeyStem(actionTypeId, wireField), slot);
93}
94
95/// @brief The explicit override key for one field text slot, when the field
96/// declares an `x-i18nKey` stem override.
97///
98/// A single `FieldMeta::i18nKey` override (see `gui_field_metadata.md`)
99/// replaces the derived `<actionTypeId>.<wireField>` stem for *all three* of
100/// a field's text slots; the `.label` / `.help` / `.placeholder` suffix is
101/// still appended per slot, exactly as it is for the derived key.
102/// @param i18nKeyOverride The field's declared `FieldMeta::i18nKey` (empty = no override).
103/// @param slot Which of the field's texts this key names.
104/// @return `std::nullopt` when @p i18nKeyOverride is empty; otherwise `"<i18nKeyOverride>.<slot>"`.
105[[nodiscard]] inline std::optional<std::string> explicitFieldKey(std::string_view i18nKeyOverride, FieldSlot slot) {
106 if (i18nKeyOverride.empty()) {
107 return std::nullopt;
108 }
109 return withSlot(i18nKeyOverride, slot);
110}
111
112/// @brief The derived message key for a layout group's title.
113/// @param actionTypeId `ActionTraits<A>::typeId()` of the action the group belongs to.
114/// @param groupIndex The group's 0-based index into `x-layout.groups`.
115/// @return `"<actionTypeId>.group.<groupIndex>"`.
116[[nodiscard]] inline std::string groupKey(std::string_view actionTypeId, std::size_t groupIndex) {
117 std::string key{actionTypeId};
118 key += ".group.";
119 key += std::to_string(groupIndex);
120 return key;
121}
122
123/// @brief The derived message key for a cross-field rule's violation message.
124/// @param actionTypeId `ActionTraits<A>::typeId()` of the action the rule belongs to.
125/// @param ruleIndex The rule's 0-based index into `x-rules`.
126/// @return `"<actionTypeId>.rule.<ruleIndex>"`.
127[[nodiscard]] inline std::string ruleKey(std::string_view actionTypeId, std::size_t ruleIndex) {
128 std::string key{actionTypeId};
129 key += ".rule.";
130 key += std::to_string(ruleIndex);
131 return key;
132}
133
134/// @brief The derived message key for a wizard's own title.
135/// @param wizardId The registered wizard id.
136/// @return `"<wizardId>.title"`.
137[[nodiscard]] inline std::string wizardTitleKey(std::string_view wizardId) {
138 std::string key{wizardId};
139 key += ".title";
140 return key;
141}
142
143/// @brief The derived message key for one wizard step's title.
144/// @param wizardId The registered wizard id.
145/// @param stepIndex The step's 0-based index.
146/// @return `"<wizardId>.step.<stepIndex>.title"`.
147[[nodiscard]] inline std::string wizardStepTitleKey(std::string_view wizardId, std::size_t stepIndex) {
148 std::string key{wizardId};
149 key += ".step.";
150 key += std::to_string(stepIndex);
151 key += ".title";
152 return key;
153}
154
155/// @brief The derived message key for an app shell's own title.
156/// @param appId The registered app id.
157/// @return `"<appId>.title"`.
158[[nodiscard]] inline std::string appTitleKey(std::string_view appId) {
159 std::string key{appId};
160 key += ".title";
161 return key;
162}
163
164/// @brief The derived message key for one app-menu entry's label.
165/// @param appId The registered app id.
166/// @param menuIndex The menu entry's 0-based index.
167/// @return `"<appId>.menu.<menuIndex>.label"`.
168[[nodiscard]] inline std::string appMenuLabelKey(std::string_view appId, std::size_t menuIndex) {
169 std::string key{appId};
170 key += ".menu.";
171 key += std::to_string(menuIndex);
172 key += ".label";
173 return key;
174}
175
176} // namespace morph::forms::i18n
std::string appMenuLabelKey(std::string_view appId, std::size_t menuIndex)
The derived message key for one app-menu entry's label.
Definition i18n.hpp:168
std::string appTitleKey(std::string_view appId)
The derived message key for an app shell's own title.
Definition i18n.hpp:158
std::string fieldKey(std::string_view actionTypeId, std::string_view wireField, FieldSlot slot)
The fully-derived message key for one field text slot.
Definition i18n.hpp:91
std::string groupKey(std::string_view actionTypeId, std::size_t groupIndex)
The derived message key for a layout group's title.
Definition i18n.hpp:116
std::string ruleKey(std::string_view actionTypeId, std::size_t ruleIndex)
The derived message key for a cross-field rule's violation message.
Definition i18n.hpp:127
constexpr std::string_view fieldSlotName(FieldSlot slot) noexcept
The catalog-key suffix for slot.
Definition i18n.hpp:44
std::string withSlot(std::string_view stem, FieldSlot slot)
Appends .<slotName> to a message-key stem.
Definition i18n.hpp:68
std::string fieldKeyStem(std::string_view actionTypeId, std::string_view wireField)
The derived message-key stem for a field: <actionTypeId>.<wireField>.
Definition i18n.hpp:79
FieldSlot
Which of a field's three translatable texts a message key names.
Definition i18n.hpp:35
@ Placeholder
The field's empty-state placeholder text (<stem>.placeholder).
Definition i18n.hpp:38
@ Help
The field's help/description text (<stem>.help).
Definition i18n.hpp:37
@ Label
The field's display label (<stem>.label).
Definition i18n.hpp:36
std::optional< std::string > explicitFieldKey(std::string_view i18nKeyOverride, FieldSlot slot)
The explicit override key for one field text slot, when the field declares an x-i18nKey stem override...
Definition i18n.hpp:105
std::string wizardTitleKey(std::string_view wizardId)
The derived message key for a wizard's own title.
Definition i18n.hpp:137
std::string wizardStepTitleKey(std::string_view wizardId, std::size_t stepIndex)
The derived message key for one wizard step's title.
Definition i18n.hpp:147