Problem/Motivation
Well this is obviously some low-hanging fruit. (Kidding, this is going to be very complicated.)
Currently recipes are not translatable. They may have hard-coded strings that are presented to users (e.g., during input collection), or saved in config in some way. These strings need to be translatable. But there's no way to do it, or at least no clear way. We have no mechanism to extract translatable strings from recipe.yml files, and even if we did, we don't necessarily have a mechanism for shipping recipe translations, or displaying/using those translations when they're needed.
This needs to be figured out.
Proposed resolution
Honestly, I have no idea. @alexpott said he had some thoughts. But I expect the approach(es) we take to be interesting, and to evolve over time.
Remaining tasks
TBD.
User interface changes
TBD.
Introduced terminology
TBD.
API changes
TBD.
Data model changes
Probably none, but who knows.
Release notes snippet
TBD.
Comments
Comment #2
phenaproximaComment #3
gábor hojtsyAt least YAML 1.2 allows for tags that allow us to define custom data types https://yaml.org/spec/1.2.2/#tags, then we would need to respond to the
!translatetype with wrapping it in translation at runtime and extracting it statically. Not sure how wide support for this is in tools.Comment #4
gábor hojtsyThe default (failsafe) schema of YAML does not include
!translate, howeverAll nodes with the “!” non-specific tag are resolved, by the standard convention, to “tag:yaml.org,2002:seq”, “tag:yaml.org,2002:map” or “tag:yaml.org,2002:str”, according to their kind.per https://yaml.org/spec/1.2.2/#failsafe-schema -- which I understand that tools are expected to resolve custom tags as sequence, map or string based on which syntax was used above them. So
!translatefollowed by a string would/should be understood as string when applied to a string by a YAML tool following the failsafe schema.Comment #5
thejimbirch commentedAdding related issues.
Comment #6
penyaskitoWe'll need to figure out too:
* Potx support
* l.d.org support: we need to run potx on general projects that include a recipe.yml.
Comment #7
joachim namysloAny news here?
Comment #8
phenaproximaLooks like the Symfony YAML parser supports custom tags: https://symfony.com/doc/current/components/yaml.html#parsing-and-dumping...
It's also supported by PHP's YAML extension ($callbacks param): https://www.php.net/manual/en/function.yaml-parse.php
Maybe something we could take advantage of here?
Comment #9
jose reyero commentedAbout having "recipes" as a thing - meaning the recipe's yaml strings - being translatable, can't we just use the same approach used for module's yml files? - I mean something like adding the files here.. (potx module) https://git.drupalcode.org/project/potx/-/blob/8.x-1.x/yaml_translation_...
Then the other big issue is having the content included in the recipes - Default content? - translated which may be a whole different story - and issue.
Comment #10
gábor hojtsyI think the pieces of the recipe where translatable pieces may appear are not predictable like that? Which is why I suggested
!translatein #3 above. Or do you see a way we can predictably say which parts will be translatable? I think they would depend on whether the config they reference for example are translatable, which I'm not sure we can resolve?Comment #11
phenaproximaThe content is already translatable. Default Content exports translations and core imports them, and I'm pretty sure we have test coverage of that.
Comment #12
jose reyero commented@gábor hojtsy
Not sure about which parts need to be translated, just trying to identify the pieces of this issue and noting this should be somehow part of how Drupal handles translation for generic yml files, though these ones may be more complex..
@phenaproxima,
You're right, default content is translatable. What we are missing though is some workflow to be able to translate that default content and include those translations with the CMS distribution. Same happens for Drupal's Umami profile, which includes English and Spanish in the download.
So we need to either keep this default content editable / translatable somewhere online, and then include it in the downloadable package, for every language, which I don't think is easy/doable with current infrastructure, or..
... maybe find some way for translatable strings in content to be extracted and available on LDO for translation, then downloaded on the fly, kind of like configuration translations... or...
... extend LDO to translate texts in default content, make the translated content downloadable... or....
well, I don't know, just pointing out the issue and that we need some new and fresh approach for this.
Comment #13
gábor hojtsyBeing able to easily edit the translations is a separate problem from being able to translate them I think. Let's try to focus on translating recipes here?
Comment #14
joachim namysloMaybe symphonies approach can help here?
https://symfony.com/doc/current/translation.html
They have a way to handle transitions in yml files according to their docs.
Drupal is based on symphony, isn't it?
So maybe someone can take a closer look on how they do it. It'll help, eventually.
To make it a bit more clear for anyone else to stumble up on this issue:
We need a way to make configurations like:
translatable when such things come from a recipe. In the end, everything that could be transferred from a recipe to the UI must be made identifiable and translatable. The question in this issue is: How do we achieve this?
But since a recipe can provide all kinds of configurations, fields, and even more, identifying all those bits and pieces that we need to provide in additional languages besides English isn't an easy task.
On the other hand by now we have mixed languages when recipes are enabled. This is not an option because whenever you want to sell software like Drupal to someone whose native language is not English, they have a reasonable expectation that the software will be offered in their native language. This applies equally to software such as computer games and DVD players as well as Microsoft office. Drupal is unfortunately no exception. It is therefore important that we find a way to solve this problem as holistically as possible.
Comment #15
jose reyero commentedI have to agree with @gàbor #13 about focusing on the recipes stuff first, not default content for now.
About extending yaml format / parsers, though it may open some interesting options, I don't think that is really what we need.
I've been playing around with strings, recipes, configurations and translations and as I see the problem, besides translating the strings in recipe.yml which looks like a trivial one, see #3516466: Extract strings from Recipes translating the configuration shipped with recipes looks pretty much like the issue with module's configuration, only the yml files - and the schemas - are organized differently.
The main feature for having all these - modules, themes, recipes? - translated is being able to extract the translatable strings, for which we need basically to locate the configuration files and the configuration schema and that is the potx module, right? However, I've been running some tests and it looks like for individual modules, it also fails when extracting the strings for configuration whose schema is not in the module itself - still not sure whether having the module including the schema around will help.
When editing / installing the Recipe the modules providing the configuration schema are supposed to be available / installed so configuration translation will work just fine provided we have the translations available. So the only piece that is left is being able to parse the recipe package by itself and extract the translatable strings and that must be done by the potx or similar module.
I'm thinking two options right now that could also help with modules' strings: one is having the schema files located and loaded dynamically which may get really complex, and the other is having the Recipe / module providing some po file with these extra strings - that could be also generated by potx extended module but with the module and all the dependencies installed.
Comment #16
gábor hojtsy1. I think #3516466: Extract strings from Recipes is a good one and simple one to make the labels of the recipes itself translatable.
2. Also config shipped with the recipes should be translatable (is it already?).
3. But finally what's not yet being solved and where I suggested the in-place YAML format support is config action values, eg. if you have a config action that updates the title of the a menu item or a views display etc that will save as-is and not translated.
Comment #17
jose reyero commentedYes, it should. AFAIK the missing piece here is the strings not getting extracted by potx.
Ok, this is the part I was missing. Yes, I think these YAML tags look like a good option.
Comment #18
penyaskito>> 2. Also config shipped with the recipes should be translatable (is it already?).
> Yes, it should. AFAIK the missing piece here is the strings not getting extracted by potx.
I wouldn't expect changes needed at potx for shipped config, but we probably need l.d.o to parse general projects and check if they are recipes looking at composer.json?
Comment #19
phenaproximaToday, I met with the multilingual track leads for Drupal CMS, and we determined that this issue -- with all its extensive implications -- is the main technical blocker to making Drupal CMS truly, gloriously multilingual.
I'm new to multilingual stuff (I live in 'Murica and therefore am only fluent in one language), but I'm eager to learn. Please forgive any ignorance on my part. :)
From my cursory research, I see four distinct phases emerging here:
I'd like to propose we turn this issue into a meta, organized around these four phases, with specific lists of issues that need to be fixed for each phase to be complete.
Comment #20
jose reyero commentedI think the plan in #19 looks great, adding the 'META' tag to this one for now.
And some patch about the approach discussed here using symfony tags, on this one https://www.drupal.org/project/drupal/issues/3313863#comment-16087199
Comment #21
catchI understand the need to make the strings in config actions something that can be translated, but the translation itself needs to apply to the config on the Drupal site itself- since it will be the config translation module that eventually does the actual translation in Drupal.
E.g. if you created that config by hand and translated it by hand, it should end up in the same format as if it was translated from shipped config no?
Which makes me wonder if there could be a build step on l.d.o, or for recipes themselves, that installs the recipe and exports the resultant config, so it can then be parsed for translation.
Comment #22
jose reyero commentedHi @catch,
> ... the translation itself needs to apply to the config on the Drupal site
Right, but since the config translation goes through the localization system, or main issue is being able to track and export these locale strings - and for that these yaml tags are a handy solution.
Once these strings are into the locale system, they just need to be applied - on the fly, with that small patch - to config-actions strings. For regular configuration objects, the existing API should just work - though we need to make sure Recipes use it (?).
> ...a build step on l.d.o, or for recipes themselves, that installs the recipe and exports the resultant config...
Well, that could be an interesting alternate approach, though it looks to me way more complex and also it may not allow for runtime translation of config actions strings before applying them, I mean something like:
1. With tagged strings:
Existing config + Config action (translate string on the fly) -> Resulting config (translated). OK
2. With building step, we'll have the strings and their translations, but still cannot tell about the strings in the config actions.
Existing config + Config action (untranslated string) -> Resulting config (untranslated) -> Can we translate that?? (Not sure)
Putting it another way, our current config translation relies on being able to tell which strings are translatable within the shipped configuration and then translate those through the locale system. But with configuration actions we are adding some more strings on top of already existing - but not shipped with modules - site config, for which current translation/localization system may not work.
(And on top of that I'm afraid at some point we will need to think about applying recipes/actions to multiple language versions of the configuration...)
Comment #23
gábor hojtsyFor l.d.o: I think this would result in a much bigger set of translatable strings. I'm not sure how we can tell apart from the resulting config which parts were from config actions and which ones were not? So far for all other string extractors we do get the specific strings, not a superset that includes a bunch of unrelated stuff :)
For a specific website: As Jose wrote, when recipes get applied on an actual site, they may get applied to a multilingual site where the default is not English anymore or the edited config value is not as expected. Let's consider this theoretic situation for the sake of demonstration:
In this situation, the config translation's shipped by Drupal for the username are for "Anonymous". Those are not useful as the site changed the setting.
The new value shipped with the config action is not the same as the Drupal default, but it is in English.
So when it is applied, the default config on the site needs the Spanish translation of "Unknown name" set. This requires recipe application to be aware that the value is translatable and needs to
t("Unknown name")as part of the recipe application, which will possibly make "Nombre desconocido" the anonymous name.If there was an English language config translation, THAT would get "Unkown name" applied from the config action at the same time and other translations would get the
t("Unknown name")too.Lifecycle after recipe applied: After the recipe is applied, there will be no trace that the recipe applied the use of "Unknown name" and that is somehow a source string that needs translations applied. We discussed that this is by design, so the config system cannot deal with translating "Unknown name" after the fact that the recipe got applied, because "Unknown name" does not even necessarily appear in config anymore and also because config does not know that "Unknown name" came from some shipped source code that could be t()-ed.
---
All in all I think the above point to needing to explicitly mark translatable strings in recipes, parse them out on l.d.o explicitly and t() them when applied, because afterwards there is no chance to do it.
Comment #24
catchI might be missing something, don't think I've actually worked on a site using config translation yet.
If a site has English default language + French and Spanish config translations, how do both of the French and Spanish translations get into the language configuration override collection if we t() when the recipe is applied?
Comment #25
gábor hojtsyThe config update and override creation/update needs to happen as part of the recipe application, before the recipe application is completed. After that the system has no idea if the "customized" string came from a source code (and can be
t()-ed) or was manually entered (and cannot bet()-ed).We would
t()the string from the config action to create or update the French and Spanish config overrides as part of the recipe application. Since the English is default, we'll nott()the config action string for the default language in this case.If the site has a Spanish default with English and French translations, then we would need to apply the untranslated config action string to the English translation on the site and
t()for Spanish active config andt()for the French override.In each case we need to save the result of
t()to the active config / override (as applicable) and can't update it anymore later unlike every othert()-ed thing because that is the philosophy of recipes.---
When the recipe is applied, I guess one backward way of figuring out which config actions may be translatable is to backreference from the config schema as part of applying the recipe, but such backreferencing needs to be built into the recipe application then. However that still does not solve the l.d.o extraction problem. L.d.o does not have a Drupal runtime for extracting strings where the code would actually be run, it always statically inspects the code. (From a Drupal 7 system even at this time). It does not run the code it handles.
Comment #26
catchThanks for #25, that sounds like it should work in terms of the recipe application side of things.
Comment #27
gábor hojtsyI'll assume that was in reference to the explanation above the "---", because under that I intended to outline a solution that is IMHO not really useful :)
Comment #28
catchYes sorry the last paragraph gave me a headache so I skipped over it ;) but everything up to that answered my question and sounded sensible.
Comment #29
thejimbirch commentedComment #30
jose reyero commentedComment #32
gábor hojtsy