Conversation
Co-authored-by: José Iván López González <jlopez@suse.com>
By adding a new query for the system endpoint and consuming data from there. Previous "system" query has been moved to "hostname", which most probably will dissapear once hostname is ported to the new API and, most probably, served as part of the system.
Forgotten in commit dbf9e73
Read the FIXME in the code to know more.
Apart from fixing some wrong keys after a backend update.
- Some code is commented in the supervisor until l10n is able to dispatch the required actions (e.g., apply config).
|
Note The real-time language change shown in the screencast is intended for demo purposes only. Ideally, we would retain this behavior, as it improves usability, especially on the mentioned welcome screen (#1941, #2503), by allowing users to view options and buttons in their selected language. However, the final implementation should meet the following requirements:
|
But, In fact, I think we shouldn't use |
lslezak
left a comment
There was a problem hiding this comment.
In general it looks good, just some minor comments.
| // replaced with the product name and the text in the square brackets [] is | ||
| // used for the link to show the license, please keep the brackets. | ||
| _("I have read and accept the [license] for %s"), | ||
| t("I have read and accept the [license] for {{product.name}}", { product: nextProduct }), |
There was a problem hiding this comment.
I'd keep the old way and avoid using the i18next interpolations. There are several advantages for the old approach:
- Gettext adds the
c-formatflag for such messages and then it checks that all translations also contain the%splaceholder. And that check is also done by Weblate, the translators cannot even save a translation without the proper number of%splaceholders. That avoids bugs in translations from the very beginning. - The
%sis much simpler format and avoids typos or copy&paste errors. What happens if a translator by mistake writes{{product,name}}or{{produtc.name}}in a translation? With simple%syou cannot do much mistakes and if you do then gettext will complain.
| import LanguageDetector from "i18next-browser-languagedetector"; | ||
| import HTTPApi from "i18next-http-backend"; | ||
|
|
||
| i18next |
There was a problem hiding this comment.
I do not like when some initialization code is executed directly at import. You cannot write a test for such functionality.
I'd wrap it into an init() function or something like that and call that function explicitly.
There was a problem hiding this comment.
I have not a clear idea nor strong opinion here. It was just copy/pasted form the upstream documentation for doing a quick PoC
There was a problem hiding this comment.
OK, np as it is still a PoC...
|
Regarding translations, I'd keep the current The same with plural forms, let's keep using the |
If I'm not wrong, that is not possible: "t" is a hook. And that is why it works and immediately change the texts as soon a new translation file is loaded.
I'm not sure we should stick with xgettext if it goes against a more React-friendly way of handling translations. For better or worse, React is the framework we're using for the web UI, so it makes sense to align with its ecosystem as much as possible. As mentioned in the PR, there are plugins available that can handle translation extraction. That said, you're the expert in i18n here. Let me just kindly ask you take a moment to think twice about how we might combine the best of both worlds, xgettext and i18next, even if that means reworking or even sacrifice some of what’s already been done. The goal is to enable more dynamic, reactive translations, and we expect changes along the way. |
Um, that should be possible, t() is just a wrapper around the translate() function which should just return a string in the end... (Hopefully I haven't overlooked something.)
I'm fine with changing the tooling if it better matches the used framework. I just do not want to loose the features provided by the current translation stack. I found out that Weblate has native support for the i18next JSON files so that should not be a problem. However, I can still see benefits of using the GNU gettext and the PO files:
Another reason for gettext and the PO files is that it is unified for all Agama components, it is also used by the Ruby backend. Using the same approach and the same rules everywhere reduces confusion and mistakes on the translators side. |
|
First of all, thanks for the explanation.
Maybe it was me who overlooked something. BtW, I meant
Don't get me wrong. Me neither. Just betting for find the best of two world but avoiding to fight against the framework.
Cool.
I see. Let's try put all the pieces together and see what we can do. We can talk about it tomorrow or whenever this task can be moved ahead. Thanks. |
|
Yes, probably using i18next + GNU gettext and PO files is the best solution... |
a2dce67 to
7540489
Compare
Agama: switch to react-i18next
As you already know, Agama translations are a bit complex, mainly because they come from multiple sources: the web interface, backend services, and even external libraries such as libstorage-ng (which returns actions in the language set on the backend side).
Throughout development, we’ve found several i18n related issues which have been handled by increasing/polishing our custom, Cockpit-based translation code. However, this solution still doesn’t offer an easy way to address an old challenge related to the not yet implemented welcome screen: the ability to load and apply translations for the selected language without requiring a full app reload.
To tackle this, I’ve started exploring react-i18next as an alternative. It’s a mature, actively maintained library that could help us to reduce time invested on maintaining a custom i18n implementation, streamline translation handling, and enable dynamic language switching.
The good news is that it almost works out of the box. That said, to fully integrate it with the Agama translation stack, I’ll need help from someone with deeper knowledge of how translations are currently wired throughout the system.
Screencast.From.2025-09-15.11-32-45.mp4
For reference, please see below the relevant notes from my experiment:
react-i18next is based on i18next, which has a broad ecosystem of plugins and utilities available such as backends, language detectors, extractors, processors, and more. See: https://www.i18next.com/overview/plugins-and-utils
In the experiment, I used the settings you can check in the diff.
For testing, I generated locale JSON files using
i18next-gettext-converter:i18next-conv -l es -s es.po -t web/public/po-es.jsonInitially, I generated an English JSON file from
agama.potusingmsginit, before realizing that settingfallbackLng: falsecauses i18next to use keys as fallback:From what I understand, plural handling in i18next differs slightly from (n)gettext. The
t()function doesn’t require a plural key by default—just acountvariable. As a result, if a plural form is missing from the translation file, it will silently fall back to the singular form.That said, when using the i18next-parser:
It correctly extracts and generates plural keys. For example:
Produces the following keys in the output JSON:
{ "{{count}} languages supported_one": "", "{{count}} languages supported_other": "" }The caveat: developer-introduced texts alone won’t be enough to fully render the app in the default language (English), because plural forms are no longer present in the source code. Instead, they only would exist in the translation files.
Is this a limitation, or could it be seen as a benefit? It might help ensure that even the default language is reviewed and localized properly, rather than assuming source strings are final. Check Missing plural keys in translation i18next/i18next#1690 and ngettext replacement ? i18next/i18next#1096
Still, if this behavior becomes problematic, perhaps it could be improved or automated with a custom plugin: https://www.i18next.com/misc/creating-own-plugins)](https://www.i18next.com/misc/creating-own-plugins
For translations comming from backend, we can implement a good sync mechanism now that API is being rewritten. Something to manage them via query-cache or so, able to react to an onBackendLanguageChanged or event.