File indexing completed on 2024-04-21 03:54:21

0001 /*
0002     SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
0003     SPDX-License-Identifier: LGPL-2.0-or-later
0004 */
0005 
0006 #ifndef KLAZYLOCALIZEDSTRING_H
0007 #define KLAZYLOCALIZEDSTRING_H
0008 
0009 #include "klocalizedstring.h"
0010 
0011 #include <cstddef>
0012 
0013 /**
0014  *  @class KLazyLocalizedString klazylocalizedstring.h <KLazyLocalizedString>
0015  *
0016  *  Lazy-initialized variant of KLocalizedString.
0017  *
0018  *  This is a safer replacement for the I18N_NOOP set of macros and allows
0019  *  marking strings for extraction without runtime-initializing KLocalizedString instances,
0020  *  for storing in static data tables.
0021  *
0022  *  Instances of KLazyLocalizedString are not created directly, unless they should be empty.
0023  *  Instead they have to be obtained via the kli18n* functions (similar to KLocalizedString
0024  *  and the ki18n* functions).
0025  *
0026  *  Example usage in a static message table:
0027  *  @code
0028  *  struct {
0029  *      MyClass::VehicleType type;
0030  *      KLazyLocalizedString msg;
0031  *  } static constexpr const vehicle_msg_table[] = {
0032  *      { MyClass::Train, kli18np("%1 train", "%1 trains") },
0033  *      { MyClass::Bus, kli18ncp("the vehicle, not the USB one", "%1 bus", "%1 buses") },
0034  *      ...
0035  *  };
0036  *
0037  *  ...
0038  *
0039  *  const auto it = std::find_if(std::begin(vehicle_msg_table), std::end(vehicle_msg_table), [vehicleType](const auto &m) { return m.type == vehicleType; });
0040  *  QString translatedMessage = (*it).msg.subs(vehicleCount).toString();
0041  *  @endcode
0042  *
0043  *  @note KLazyLocalizedString is primarily meant for storage in e.g. message tables,
0044  *  not for use in API, especially not across translation domains. This is due to
0045  *  it not carrying the translation domain in which it was created, but using the
0046  *  active translation domain at the point of converting to a KLocalizedString.
0047  *
0048  *  @since 5.89
0049  */
0050 class KLazyLocalizedString
0051 {
0052 public:
0053     /**
0054      * Construct an empty message.
0055      *
0056      * Direct construction is used when e.g. a KLazyLocalizedString field in
0057      * a data table needs to be set, but there is not message to be used.
0058      * Directly constructed instances are not valid for
0059      * finalization by \c toString methods.
0060      *
0061      * \see isEmpty
0062      */
0063     constexpr inline KLazyLocalizedString() = default;
0064 
0065     /** Convert to a KLocalizedString to actually perform the translation and obtain a concrete
0066      *  localized string.
0067      *
0068      *  The translation domain active at this point will be used. This means this has to be
0069      *  called in the same translation domain the corresponding @c kli18n call is in.
0070      */
0071     Q_REQUIRED_RESULT inline operator KLocalizedString() const
0072     {
0073         if (!m_text) {
0074             return KLocalizedString();
0075         }
0076 #ifdef TRANSLATION_DOMAIN
0077         return KLocalizedString(TRANSLATION_DOMAIN, m_context, m_text, m_plural, m_markupAware);
0078 #else
0079         return KLocalizedString(nullptr, m_context, m_text, m_plural, m_markupAware);
0080 #endif
0081     }
0082 
0083     /**
0084      * Check whether the message is empty.
0085      *
0086      * The message is considered empty if the object was constructed
0087      * via the default constructor.
0088      *
0089      * Empty messages are not valid for finalization.
0090      * The behavior of calling toString() on them is undefined.
0091      * In debug mode, an error mark may appear in the returned string.
0092      *
0093      * \return \c true if the message is empty, \c false otherwise
0094      */
0095     Q_REQUIRED_RESULT constexpr inline bool isEmpty() const
0096     {
0097         return (m_text == nullptr) || (m_text[0] == '\0');
0098     }
0099 
0100     /** Returns the raw untranslated text as passed to @p kli18n*. */
0101     Q_REQUIRED_RESULT constexpr inline const char *untranslatedText() const
0102     {
0103         return m_text;
0104     }
0105 
0106     /**
0107      * Finalize the translation.
0108      *
0109      * Creates translated QString. If the string has placeholders,
0110      * make sure to call instead \c KLazyLocalizedString::subs and
0111      * further \c KLocalizedString::subs methods before finalizing.
0112      * Translated text is searched for based on the global locale.
0113      *
0114      * \return finalized translation
0115      */
0116     Q_REQUIRED_RESULT inline QString toString() const
0117     {
0118         return this->operator KLocalizedString().toString();
0119     }
0120 
0121     /**
0122      * Like toString(), but look for translation only in given languages.
0123      *
0124      * Given languages override languages defined by the global locale.
0125      * If \p languages is empty, original message is returned.
0126      *
0127      * \param languages list of language codes (by decreasing priority)
0128      * \return finalized translation
0129      */
0130     Q_REQUIRED_RESULT inline QString toString(const QStringList &languages) const
0131     {
0132         return this->operator KLocalizedString().toString(languages);
0133     }
0134 
0135     /**
0136      * Like toString(), but look for translation in the given domain.
0137      *
0138      * \param domain the translation domain
0139      * \return finalized translation
0140      */
0141     Q_REQUIRED_RESULT inline QString toString(const char *domain) const
0142     {
0143         return this->operator KLocalizedString().toString(domain);
0144     }
0145 
0146     /**
0147      * Like toString(), but resolve KUIT markup into given visual format.
0148      *
0149      * Given visual format overrides that implied by the context UI marker.
0150      * If the message is not markup-aware, this is same as toString().
0151      *
0152      * \param format the target visual format
0153      * \return finalized translation
0154      */
0155     Q_REQUIRED_RESULT inline QString toString(Kuit::VisualFormat format) const
0156     {
0157         return this->operator KLocalizedString().toString(format);
0158     }
0159 
0160     /**
0161      * Indicate to look for translation only in given languages.
0162      *
0163      * \param languages list of language codes (by decreasing priority)
0164      * \return updated KLocalizedString
0165      */
0166     Q_REQUIRED_RESULT inline KLocalizedString withLanguages(const QStringList &languages) const
0167     {
0168         return this->operator KLocalizedString().withLanguages(languages);
0169     }
0170 
0171     /**
0172      * Indicate to look for translation in the given domain.
0173      *
0174      * \param domain the translation domain
0175      * \return updated KLocalizedString
0176      */
0177     Q_REQUIRED_RESULT inline KLocalizedString withDomain(const char *domain) const
0178     {
0179         return this->operator KLocalizedString().withDomain(domain);
0180     }
0181 
0182     /**
0183      * Indicate to resolve KUIT markup into given visual format.
0184      *
0185      * If the message is not markup-aware, this has no effect.
0186      *
0187      * \param format the target visual format
0188      * \return updated KLocalizedString
0189      */
0190     Q_REQUIRED_RESULT inline KLocalizedString withFormat(Kuit::VisualFormat format) const
0191     {
0192         return this->operator KLocalizedString().withFormat(format);
0193     }
0194 
0195     /**
0196      * Substitute an int argument into the message.
0197      *
0198      * \param a the argument
0199      * \param fieldWidth width of the formatted field, padded by spaces.
0200      *                   Positive value aligns right, negative aligns left
0201      * \param base the radix used to represent the number as a string.
0202      *             Valid values range from 2 to 36
0203      * \param fillChar the character used to fill up the empty places when
0204      *                 field width is greater than argument width
0205      * \return updated KLocalizedString
0206      */
0207     Q_REQUIRED_RESULT inline KLocalizedString subs(int a, int fieldWidth = 0, int base = 10, QChar fillChar = QLatin1Char(' ')) const
0208     {
0209         return this->operator KLocalizedString().subs(a, fieldWidth, base, fillChar);
0210     }
0211 
0212     /**
0213      * Substitute an unsigned int argument into the message.
0214      *
0215      * \param a the argument
0216      * \param fieldWidth width of the formatted field, padded by spaces.
0217      *                   Positive value aligns right, negative aligns left
0218      * \param base the radix used to represent the number as a string.
0219      *             Valid values range from 2 to 36
0220      * \param fillChar the character used to fill up the empty places when
0221      *                 field width is greater than argument width
0222      * \return updated KLocalizedString
0223      */
0224     Q_REQUIRED_RESULT inline KLocalizedString subs(uint a, int fieldWidth = 0, int base = 10, QChar fillChar = QLatin1Char(' ')) const
0225     {
0226         return this->operator KLocalizedString().subs(a, fieldWidth, base, fillChar);
0227     }
0228 
0229     /**
0230      * Substitute a long argument into the message.
0231      *
0232      * \param a the argument
0233      * \param fieldWidth width of the formatted field, padded by spaces.
0234      *                   Positive value aligns right, negative aligns left
0235      * \param base the radix used to represent the number as a string.
0236      *             Valid values range from 2 to 36
0237      * \param fillChar the character used to fill up the empty places when
0238      *                 field width is greater than argument width
0239      * \return updated KLocalizedString
0240      */
0241     Q_REQUIRED_RESULT inline KLocalizedString subs(long a, int fieldWidth = 0, int base = 10, QChar fillChar = QLatin1Char(' ')) const
0242     {
0243         return this->operator KLocalizedString().subs(a, fieldWidth, base, fillChar);
0244     }
0245 
0246     /**
0247      * Substitute an unsigned long argument into the message.
0248      *
0249      * \param a the argument
0250      * \param fieldWidth width of the formatted field, padded by spaces.
0251      *                   Positive value aligns right, negative aligns left
0252      * \param base the radix used to represent the number as a string.
0253      *             Valid values range from 2 to 36
0254      * \param fillChar the character used to fill up the empty places when
0255      *                 field width is greater than argument width
0256      * \return updated KLocalizedString
0257      */
0258     Q_REQUIRED_RESULT inline KLocalizedString subs(ulong a, int fieldWidth = 0, int base = 10, QChar fillChar = QLatin1Char(' ')) const
0259     {
0260         return this->operator KLocalizedString().subs(a, fieldWidth, base, fillChar);
0261     }
0262 
0263     /**
0264      * Substitute a long long argument into the message.
0265      *
0266      * \param a the argument
0267      * \param fieldWidth width of the formatted field, padded by spaces.
0268      *                   Positive value aligns right, negative aligns left
0269      * \param base the radix used to represent the number as a string.
0270      *             Valid values range from 2 to 36
0271      * \param fillChar the character used to fill up the empty places when
0272      *                 field width is greater than argument width
0273      * \return updated KLocalizedString
0274      */
0275     Q_REQUIRED_RESULT inline KLocalizedString subs(qlonglong a, int fieldWidth = 0, int base = 10, QChar fillChar = QLatin1Char(' ')) const
0276     {
0277         return this->operator KLocalizedString().subs(a, fieldWidth, base, fillChar);
0278     }
0279 
0280     /**
0281      * Substitute an unsigned long long argument into the message.
0282      *
0283      * \param a the argument
0284      * \param fieldWidth width of the formatted field, padded by spaces.
0285      *                   Positive value aligns right, negative aligns left
0286      * \param base the radix used to represent the number as a string.
0287      *             Valid values range from 2 to 36
0288      * \param fillChar the character used to fill up the empty places when
0289      *                 field width is greater than argument width
0290      * \return updated KLocalizedString
0291      */
0292     Q_REQUIRED_RESULT inline KLocalizedString subs(qulonglong a, int fieldWidth = 0, int base = 10, QChar fillChar = QLatin1Char(' ')) const
0293     {
0294         return this->operator KLocalizedString().subs(a, fieldWidth, base, fillChar);
0295     }
0296     /**
0297      * Substitute a double argument into the message.
0298      *
0299      * \param a the argument
0300      * \param fieldWidth width of the formatted field, padded by spaces.
0301      *                   Positive value aligns right, negative aligns left
0302      * \param format type of floating point formatting, like in QString::arg
0303      * \param precision number of digits after the decimal separator
0304      * \param fillChar the character used to fill up the empty places when
0305      *                 field width is greater than argument width
0306      * \return updated KLocalizedString
0307      */
0308     Q_REQUIRED_RESULT inline KLocalizedString subs(double a, int fieldWidth = 0, char format = 'g', int precision = -1, QChar fillChar = QLatin1Char(' ')) const
0309     {
0310         return this->operator KLocalizedString().subs(a, fieldWidth, format, precision, fillChar);
0311     }
0312 
0313     /**
0314      * Substitute a \c QChar argument into the message.
0315      *
0316      * \param a the argument
0317      * \param fieldWidth width of the formatted field, padded by spaces.
0318      *                   Positive value aligns right, negative aligns left
0319      * \param fillChar the character used to fill up the empty places when
0320      *                 field width is greater than argument width
0321      * \return updated KLocalizedString
0322      */
0323     Q_REQUIRED_RESULT inline KLocalizedString subs(QChar a, int fieldWidth = 0, QChar fillChar = QLatin1Char(' ')) const
0324     {
0325         return this->operator KLocalizedString().subs(a, fieldWidth, fillChar);
0326     }
0327 
0328     /**
0329      * Substitute a \c QString argument into the message.
0330      *
0331      * \param a the argument
0332      * \param fieldWidth width of the formatted field, padded by spaces.
0333      *                   Positive value aligns right, negative aligns left
0334      * \param fillChar the character used to fill up the empty places when
0335      *                 field width is greater than argument width
0336      * \return updated KLocalizedString
0337      */
0338     Q_REQUIRED_RESULT inline KLocalizedString subs(const QString &a, int fieldWidth = 0, QChar fillChar = QLatin1Char(' ')) const
0339     {
0340         return this->operator KLocalizedString().subs(a, fieldWidth, fillChar);
0341     }
0342 
0343     /**
0344      * Substitute another KLocalizedString into the message.
0345      *
0346      * \param a the argument
0347      * \param fieldWidth width of the formatted field, padded by spaces.
0348      *                   Positive value aligns right, negative aligns left
0349      * \param fillChar the character used to fill up the empty places when
0350      *                 field width is greater than argument width
0351      * \return updated KLocalizedString
0352      */
0353     Q_REQUIRED_RESULT inline KLocalizedString subs(const KLocalizedString &a, int fieldWidth = 0, QChar fillChar = QLatin1Char(' ')) const
0354     {
0355         return this->operator KLocalizedString().subs(a, fieldWidth, fillChar);
0356     }
0357 
0358     /**
0359      * Add dynamic context to the message.
0360      *
0361      * See \ref dyn_ctxt for use cases.
0362      *
0363      * \param key context key
0364      * \param value context value
0365      * \return updated KLocalizedString
0366      */
0367     Q_REQUIRED_RESULT inline KLocalizedString inContext(const QString &key, const QString &value) const
0368     {
0369         return this->operator KLocalizedString().inContext(key, value);
0370     }
0371 
0372     /**
0373      * Relax matching between placeholders and arguments.
0374      *
0375      * Normally the placeholders should start from %1 and have no gaps,
0376      * and on finalization there must be exactly as many arguments
0377      * supplied through \c subs methods as there are unique plaecholders.
0378      * If this is not satisfied, in debug mode warnings are printed
0379      * and the finalized string may contain error marks.
0380      *
0381      * This method relaxes the placeholder-argument matching,
0382      * such that there must only be an argument available for
0383      * every present unique placeholder (taking placeholder numbers
0384      * to be 1-based indices into the argument list).
0385      * This can come useful in some situations.
0386      *
0387      * \return updated KLocalizedString
0388      */
0389     Q_REQUIRED_RESULT inline KLocalizedString relaxSubs() const
0390     {
0391         return this->operator KLocalizedString().relaxSubs();
0392     }
0393 
0394     /**
0395      * Do not resolve KUIT markup.
0396      *
0397      * If the message is markup-aware
0398      * (constructed by one of \c \*xi18n\* calls),
0399      * this function can be used to make it non-markup-aware.
0400      * This may be useful for debugging markup.
0401      *
0402      * \return updated KLocalizedString
0403      */
0404     Q_REQUIRED_RESULT inline KLocalizedString ignoreMarkup() const
0405     {
0406         return this->operator KLocalizedString().ignoreMarkup();
0407     }
0408 
0409 private:
0410     template<std::size_t TextSize>
0411     friend inline constexpr KLazyLocalizedString kli18n(const char (&text)[TextSize]);
0412     template<std::size_t ContextSize, std::size_t TextSize>
0413     friend constexpr inline KLazyLocalizedString kli18nc(const char (&context)[ContextSize], const char (&text)[TextSize]);
0414     template<std::size_t SingularSize, std::size_t PluralSize>
0415     friend constexpr inline KLazyLocalizedString kli18np(const char (&singular)[SingularSize], const char (&plural)[PluralSize]);
0416     template<std::size_t ContextSize, std::size_t SingularSize, std::size_t PluralSize>
0417     friend constexpr inline KLazyLocalizedString
0418     kli18ncp(const char (&context)[ContextSize], const char (&singular)[SingularSize], const char (&plural)[PluralSize]);
0419     template<std::size_t TextSize>
0420     friend constexpr inline KLazyLocalizedString klxi18n(const char (&text)[TextSize]);
0421     template<std::size_t ContextSize, std::size_t TextSize>
0422     friend constexpr inline KLazyLocalizedString klxi18nc(const char (&context)[ContextSize], const char (&text)[TextSize]);
0423     template<std::size_t SingularSize, std::size_t PluralSize>
0424     friend constexpr inline KLazyLocalizedString klxi18np(const char (&singular)[SingularSize], const char (&plural)[PluralSize]);
0425     template<std::size_t ContextSize, std::size_t SingularSize, std::size_t PluralSize>
0426     friend constexpr inline KLazyLocalizedString
0427     klxi18ncp(const char (&context)[ContextSize], const char (&singular)[SingularSize], const char (&plural)[PluralSize]);
0428 
0429     constexpr inline KLazyLocalizedString(const char *context, const char *text, const char *plural, bool markupAware)
0430         : m_context(context)
0431         , m_text(text)
0432         , m_plural(plural)
0433         , m_markupAware(markupAware)
0434     {
0435     }
0436 
0437     const char *m_context = nullptr;
0438     const char *m_text = nullptr;
0439     const char *m_plural = nullptr;
0440     bool m_markupAware = false;
0441 };
0442 
0443 /**
0444  * Mark the string @p text for extraction.
0445  *
0446  * \param text string to translate
0447  * \return KLazyLocalizedString for deferred translation.
0448  * \since 5.89
0449  */
0450 template<std::size_t TextSize>
0451 constexpr inline KLazyLocalizedString kli18n(const char (&text)[TextSize])
0452 {
0453     return KLazyLocalizedString(nullptr, text, nullptr, false);
0454 }
0455 
0456 /**
0457  * Mark the string @p text with @p context for extraction.
0458  *
0459  * \param context context of the string
0460  * \param text string to translate
0461  * \return KLazyLocalizedString for deferred translation.
0462  * \since 5.89
0463  */
0464 template<std::size_t ContextSize, std::size_t TextSize>
0465 constexpr inline KLazyLocalizedString kli18nc(const char (&context)[ContextSize], const char (&text)[TextSize])
0466 {
0467     return KLazyLocalizedString(context, text, nullptr, false);
0468 }
0469 
0470 /**
0471  * Mark the string @p singular and @p plural for extraction.
0472  *
0473  * \param singular singular form of the string to translate
0474  * \param plural plural form of the string to translate
0475  * \return KLazyLocalizedString for deferred translation.
0476  * \since 5.89
0477  */
0478 template<std::size_t SingularSize, std::size_t PluralSize>
0479 constexpr inline KLazyLocalizedString kli18np(const char (&singular)[SingularSize], const char (&plural)[PluralSize])
0480 {
0481     return KLazyLocalizedString(nullptr, singular, plural, false);
0482 }
0483 
0484 /**
0485  * Mark the string @p singular and @p plural with @p context for extraction.
0486  *
0487  * \param context context of the string
0488  * \param singular singular form of the string to translate
0489  * \param plural plural form of the string to translate
0490  * \return KLazyLocalizedString for deferred translation.
0491  * \since 5.89
0492  */
0493 template<std::size_t ContextSize, std::size_t SingularSize, std::size_t PluralSize>
0494 constexpr inline KLazyLocalizedString kli18ncp(const char (&context)[ContextSize], const char (&singular)[SingularSize], const char (&plural)[PluralSize])
0495 {
0496     return KLazyLocalizedString(context, singular, plural, false);
0497 }
0498 
0499 /**
0500  * Mark the markup-aware string @p text for extraction.
0501  *
0502  * \param text string to translate
0503  * \return KLazyLocalizedString for deferred translation.
0504  * \since 5.89
0505  */
0506 template<std::size_t TextSize>
0507 constexpr inline KLazyLocalizedString klxi18n(const char (&text)[TextSize])
0508 {
0509     return KLazyLocalizedString(nullptr, text, nullptr, true);
0510 }
0511 
0512 /**
0513  * Mark the markup-aware string @p text with @p context for extraction.
0514  *
0515  * \param context context of the string
0516  * \param text string to translate
0517  * \return KLazyLocalizedString for deferred translation.
0518  * \since 5.89
0519  */
0520 template<std::size_t ContextSize, std::size_t TextSize>
0521 constexpr inline KLazyLocalizedString klxi18nc(const char (&context)[ContextSize], const char (&text)[TextSize])
0522 {
0523     return KLazyLocalizedString(context, text, nullptr, true);
0524 }
0525 
0526 /**
0527  * Mark the markup-aware string @p singular and @p plural for extraction.
0528  *
0529  * \param singular singular form of the string to translate
0530  * \param plural plural form of the string to translate
0531  * \return KLazyLocalizedString for deferred translation.
0532  * \since 5.89
0533  */
0534 template<std::size_t SingularSize, std::size_t PluralSize>
0535 constexpr inline KLazyLocalizedString klxi18np(const char (&singular)[SingularSize], const char (&plural)[PluralSize])
0536 {
0537     return KLazyLocalizedString(nullptr, singular, plural, true);
0538 }
0539 
0540 /**
0541  * Mark the markup-aware string @p singular and @p plural with @p context for extraction.
0542  *
0543  * \param context context of the string
0544  * \param singular singular form of the string to translate
0545  * \param plural plural form of the string to translate
0546  * \return KLazyLocalizedString for deferred translation.
0547  * \since 5.89
0548  */
0549 template<std::size_t ContextSize, std::size_t SingularSize, std::size_t PluralSize>
0550 constexpr inline KLazyLocalizedString klxi18ncp(const char (&context)[ContextSize], const char (&singular)[SingularSize], const char (&plural)[PluralSize])
0551 {
0552     return KLazyLocalizedString(context, singular, plural, true);
0553 }
0554 
0555 #endif // KLAZYLOCALIZEDSTRING_H