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 render/i18n.hpp
6/// @brief The renderer-side translation-catalog seam over morph's derived
7/// message keys (`morph::forms::i18n`, `forms/i18n.hpp`).
8///
9/// `morph::render` is client-side only and never appears on the wire — it is
10/// the C++ side of the client-side rendering seam. morph ships this seam and the resolution algorithm below; it
11/// defines **no** translation storage format. A host adapts whatever catalog it already owns (Qt `QTranslator`/`.qm`,
12/// a JSON bundle, a database) into the one `TranslationProvider` signature.
13
14#include <functional>
15#include <optional>
16#include <string>
17#include <string_view>
18
19namespace morph::render {
20
21/// @brief A host-supplied catalog lookup: `key`/`bcp47Locale` -> translated
22/// text, or `std::nullopt` on a miss.
23///
24/// A default-constructed (empty) `TranslationProvider` means "no catalog
25/// installed" — `resolveText` treats it the same as a provider that misses
26/// on every key.
28 std::function<std::optional<std::string>(std::string_view key, std::string_view bcp47Locale)>;
29
30/// @brief Resolves one display slot's text: explicit key, then derived key,
31/// then the schema literal.
32///
33/// Tried in order, most specific first:
34/// 1. @p explicitKey (a host-declared `x-i18nKey`-derived key), if present;
35/// 2. @p derivedKey (`morph::forms::i18n::fieldKey` or one of its siblings);
36/// 3. a miss at both falls back to @p schemaLiteral — the schema's
37/// authored `title` / `description` / `x-placeholder` / group or step
38/// title, unchanged.
39/// An empty/unset @p provider ("no catalog installed") skips straight to
40/// @p schemaLiteral, matching an unconfigured renderer's behavior exactly.
41/// @param provider The host's catalog lookup.
42/// @param bcp47Locale The locale to resolve against (e.g. `"fr-FR"`).
43/// @param explicitKey The field/group/rule/step's declared `x-i18nKey`-based
44/// key, or `std::nullopt` when none is declared.
45/// @param derivedKey The mechanically-derived key for this slot.
46/// @param schemaLiteral The schema's authored fallback text for this slot.
47/// @return The resolved display text.
48// NOLINTBEGIN(bugprone-easily-swappable-parameters)
49// `derivedKey` and `schemaLiteral` are adjacent `std::string_view`s, and the
50// check is right that swapping them would be silent and wrong: the derived key
51// would be rendered to the user as display text, and the schema's authored
52// title would be looked up in the catalog, miss, and fall back to the key. No
53// type or arity error would catch it.
54//
55// They stay in this order because the order *is* the contract. The three
56// parameters are the resolution chain in precedence order -- explicit key, then
57// derived key, then the schema literal as the fallback -- which is how the
58// @brief above states it, how the body below tries them, and how
59// docs/spec/forms/forms.md specifies it.
60//
61// Strong types would remove the hazard outright, but a `TranslationKey` wrapper
62// on this seam would have to be threaded through every caller in morph::forms,
63// which is a design change to the renderer boundary and not a lint fix. If that
64// is ever done, delete this suppression rather than widening it.
65[[nodiscard]] inline std::string resolveText(const TranslationProvider& provider, std::string_view bcp47Locale,
66 const std::optional<std::string>& explicitKey,
67 std::string_view derivedKey, std::string_view schemaLiteral) {
68 // NOLINTEND(bugprone-easily-swappable-parameters)
69 if (provider) {
70 if (explicitKey.has_value()) {
71 if (auto hit = provider(*explicitKey, bcp47Locale)) {
72 return *hit;
73 }
74 }
75 if (auto hit = provider(derivedKey, bcp47Locale)) {
76 return *hit;
77 }
78 }
79 return std::string{schemaLiteral};
80}
81
82} // namespace morph::render
std::function< std::optional< std::string >(std::string_view key, std::string_view bcp47Locale)> TranslationProvider
A host-supplied catalog lookup: key/bcp47Locale -> translated text, or std::nullopt on a miss.
Definition i18n.hpp:27
std::string resolveText(const TranslationProvider &provider, std::string_view bcp47Locale, const std::optional< std::string > &explicitKey, std::string_view derivedKey, std::string_view schemaLiteral)
Resolves one display slot's text: explicit key, then derived key, then the schema literal.
Definition i18n.hpp:65