 All right. So thanks again, everyone for joining today. So today, we are going to discuss about best practices about building multilingual sites that we did for one of our project. And mainly, we will be reviewing in two aspects, the site builder perspective, and another from the developer perspective. So I'll be sharing a few relations. And apart from that, it would be fun. So yeah, let's get started, actually. I'm Mohit, and I'm working as a Drupal technical architect at Excel Run. And so this is my Twitter and Drupal ID so you can reach out to me anytime. So let's get started. So primarily, the multilingual ecosystem in Drupal 8 is built on four modules, basically. So the language module is something which provides base for entire multilingual ecosystem. So let's say we have language entity, apart from that language creation form, language feature blocks, language negotiation plugins, and all other things are provided by language module. So essentially, language module works as a foundation of an entire multilingual ecosystem that is present in Drupal Core. So on top of language module, we have content translation module. So content translation module basically takes care of translation related to content entities. Basically what we have like Node, Blob, all other media entities. So it takes care of managing all the translated entities and related things. After that, we have a configuration translation module. So configuration translation module is something that allows us to have the translation facility for all the configuration entities. So in Drupal, we all know we have a concept of content entity and configuration entity. So the configuration entities like views and view mode entity, form mode, image style, and all other configuration entities. So translation feature for all those configuration entities is provided by configuration translation module. And the third one is basically interface translation module or what we call it as local. So it mainly provides interface for translating strings, actually, the string that we see on the front end. Apart from that, it also takes care of dealing with localization server, paging translation, and all those things. The other aspects or the feature that local module provides is the translation import and export features. Like let's say, we can export translations in PO file and import it again so that it will import all the translation. So all other like scaffolding related to string translation and everything is provided by local modules. So as I mentioned earlier, we will be seeing from two perspective. One will be from site builder perspective. Second will be from developer perspective. I'll be going through a few things related to site builder perspective, but from the developer perspective, I'll be covering how we can use entity API and how we can use another multi-lingual API and related things in custom modules. So let's start with the site builder things. So primarily, the simple thing is making any content translatable in Drupal we haven't ordered. So we just need to go to content type edit form, click on enable translation, and we have various options for the default language. For example, it will be the default language for the content. So it can be any based on site's default language or it can be based on logged in users preferences or default language. So I mean, we can select our preference over here and just save it so the content type will be multi-lingual enabled. Same is the case for block content entities, actually. So go to admin structure block types, enable translations, and get them with this. So these are pretty straightforward cases. Same is the case for fields, actually. So we have a simple field like let's a body field, right? So we just click on the state box and it will translate it. So these are pretty straightforward fields. And I mean, the translation support for all these things is out of box, so we don't need to worry about it. Apart from this other straightforward flow, we have a centralized page where we can review what all things are enabled for translations. For example, if you can see here, we have all these fields that are enabled for translation. Apart from that, it also provides a couple of configuration where if you want to hide certain fields from, for example, non-translation field from a translation form, we can do that. So this is pretty straightforward. And this is a brief overview of what all content entities are there, what all fields that we want them to translate. So this is good. So now, as we all know, typical Drupal site consists of various complex fields, right? Entity reference revision field, paragraph revision fields, right? So in case of paragraph fields, which is like entity reference revision, so the translation is not straightforward like what we have for body field, right? So being the entity reference revision entities, paragraph basically supports symmetric translations. So what essentially happens is like, for example, in case of body field, we just make the host field translatable and get things done with this. Now, in case of paragraph, what we need to do is we need to make entire paragraph translatable and all the fields of the paragraph translatable, but we can't do anything with the settings of the host entity. So if we see in our case, actually, what we have right now here is the paragraph field in the advanced recipe content type. So here itself, we can see the ordering. Like it's not translatable. I mean, the way we won't do. So now if we see the configuration on the content language page, here we have like accordion item and all the paragraphs. Here we are enabling translations for all the fields. So this is the way, actually, we enable the translations for the paragraphs, actually. Now, there are modules like paragraph or symmetric translation and all those things that helps us to achieve the similar functionality. But I think it has some limitation. I think it works with experimental widget and all. So this is something which we need to be cautious about while enabling translations for paragraph. Apart from that, all the different content entities like node, block type, taxonomy, and all other, this becomes the same. So this is pretty easy. Now moving on to Vue, actually. So for Vue, typically, we usually use Vue to render the content. So Vue has two ways to display the translation. So first is go to display a plugin and select the language that we want to render. So for example, this is our recipe Vue. And here we have option like rendering language. So whatever the rendering language that we have selected, content will be displayed in that particular language. It also takes care of translation fallback as well. Now, sometimes what happens is we need additional features. Like we want to filter based on some content. Let's say we have four to five language in the site. And now what we want to do is we want to have some sort of translation fallback, like let's say the French translation is not present and then we should display a Spanish one. If Spanish is not present, we should display English one. So those kind of gradual fallback is not out of books possible in Rupa. So that we can use our contributed modules. So there is a module called select translation, which provides a Vue's filter plugin, actually. So what happens, actually, we need to give a language priorities over here, like let's say French, English, or depending on like various other options we have over here. So once the content is being varied and it is being rendered, it will automatically fetch the translations accordingly. We have one Vue over here, let me show you. Yeah. So we have a simple Vue, the option is as it is and we have a select translation plugin over here. Apart from that, like I have like just two language in demo side, so I have like kept it disabled, but here we can provide all the options and it will display translations accordingly. So this is about displaying a content translation in the Vue's. Now, what would happen if you want to translate the attributes in the Vue's itself? So typically what happens in Vue, like we have a various exposed form, we have titles, we have default messages, like default to no result text and all, right? So at some point of time, you want to translate those as well, right? So being a configuration entity, Vue has its own configuration entity translation, actually. So let's quickly see how we can actually translate Vue's as a configuration entity, right? So I mean, this is like edit Vue, click on translate Vue and it will take us to the page where we can actually see both the translation. Click on add link for this translation and here it displays like the nested list of all the attributes that are like translatable. For example, exposed form or submit button text and all those things. So this is how we can actually translate particular Vue. So the thing is that once we translate it, we can actually export it via configuration export as well. So once we like run address config export, Drupal will export it into like default config directory. Obviously like it will place into that specific language related folder, but this is how like the output will look like for one of Vue's translation. So this is like mainly about Vue's translation and moving on to next thing like solar search. So typically like almost all the sites uses like solar and the related artifact nowadays, right? So even like the multilingual solar search is like pretty much a part of search API. So we don't need to do anything from a site builder perspective actually. We just need to enable module do a couple of configuration and once we like, once we update the index, solar will start indexing the content. And after that it's it's also like just to use then use those in Vue and disable it. So I'll show you basically this is our search API. I'll modify the index. And here we have like what we want to index and all those things. So here we just need to provide the proper configuration like what all languages we want to index. And later we can use indexing. Sure, I'll show you, I have that view ready. So yeah, so yeah, once we have that ready. So yeah, so this is like the index will be done. And I have like both the views ready actually. So this is like one of the views. So this is how the Vue will look and I can show you, yes. So here, here if we see like it will handle translation for like, for example, I haven't added the translation for this recipe. So it's just present in English and we can see like all other recipes are like being proper like the properly translated. We just don't have translation for this. So it will handle this translations. Similarly, for the solar search view, let me open the view URL. So this is again, I have used index. So this is again the same, the content is coming from the solar index now. And the things are same actually. It's just, it's coming from index and I'm using the same view modes. So this is basically about like a search, implementing multilingual search. So yeah, and moving on to next one, a couple of other contributed module that I would like to mention over here. I like that we significantly used actually. So one of them is like translation management module. So this is something which we use like where we want to provide the support for like third party translations. And it basically has like various module ecosystem with like various other service providers that we can integrate it. So that like it's easy for third party too. It's easy for like site owners to get their site translated actually. Apart from that, it also provides, I think translation support for various, I think machine translation. I think there is a support for Microsoft Translator actually for work. So this is kind of, I just wanted to mention that like we extensively used it. So this is something. So this is pretty much about site building aspect. Any questions? Like I'm checking the chat. So feel free to share any questions. Cool. I think, yeah, we are good. So let's switch to Hayden. So you mean translation management tool? Yeah. There is a module for EMGMT like in Drupal 7 version. And there are like multiple plugins available. But yeah, so, and it is like stable as well. So you can use it. Like I'm not sure about other plugins in the ecosystem, but yeah, definitely you can use it. Cool. Thanks. All right. So I think let's switch to how we can actually leverage APIs. So the main thing about like entity API in Drupal, there were like significant changes around entity API and like everything was not completely, but yeah, the change was major, right? So there were like changes, like for example, in context of translation, what the major changes were there like translation where the instances of entity interface. So now being a translation is instance of entity interface, we can directly call all the method, like if you want to get the field value. So now like when we have the translation object, just call the like translation arrow gate and like fetch the value. So this is like really easy in the like, it really improves our developer experience. Like while, let's say if you're building any blocks or any controllers where we want to play with translation, we can really use those. So apart from that, there are various methods actually that we like extensively used, for example, entity gate translation. So this is something it will fetch the translation. Basically like we need to provide the language argument over here. So once we have the proper language, it will fetch the translation. Apart from that, there are like various other methods like for example, gate translation from context. So what this method does is like, it will get the proper translation based on language negotiation plugin. So it will allow language negotiation plugins to identify the active language and based on that, it will fetch the proper translation. So we don't need to like worry about any additional things like identify language based on certain parameters or whatsoever. So this is basically, apart from that, I mean there are several other methods like get languages or get translation language, get translation or there are multiple methods actually that we can leverage on day-to-day task and we have like various hooks. So we can actually use it like at certain point of time. Now moving on to like API. So typically like while developing custom modules, many times we create content entities, right? So to make any content translatable, we just need to provide like two attributes actually, make translatable true and we need to actually provide a link code over here in entity keys. So both of them together will like help like entity to be translatable. Apart from that, like there are things like other things like content entity storage base and content entity storage class, I think we have two classes that will take care of related stuff holding. So we don't need to worry about other things. Now like whenever we are creating, let's say content entity, we also create, we also add like few base field definitions, right? Like for example, main title or whatsoever. So we need to make those translatable as well, like depending on the requirement. So we just, we can just call it like said, translatable true and the translation system will take care of it and make it translatable. There are like entity field manager and that those will take care of like adding additional fields because additional field translatable. For example, there are, once we enable, once we like make this translatable, there are other fields also, and gets like translated along with that. So yeah, so this will be taken care of by I think entity field manager class. So this is basically about having like content entity translatable, translatable. Now moving on to configuration entity. Now as I mentioned earlier, like we have a module, right? For the configuration entity translation. So like all the configuration entities like views or image style or any other configuration entity, these are like out of works supported by Drupal Core. So we don't need to do anything actually. We can just go here and show you the page. Yeah, so we can go here, regional language, configuration translation. And yeah, so we have the list of all the content entities. Like click on whatever we want to translate and yeah, and translate it. So this is like pretty straightforward for all the content entities. So that now the question comes up like when we want to translate custom configuration. So let's say we are building a module and we have configurations like the default messages or other strings or probably email tags that we want to send it to editors or some other stakeholders, right? So those should be like, those should be given in their own language, right? So we want to have like configurable email templates and all those things. So in those cases, we want to make those translatable, right? So apart from like having configuration of what else we need to take care of is like we need to create a schema YAML and the configuration translation YAML. So let's review schema YAML first basically. So this is basically in our case, this is like simple configuration that we have provided like what we can say it's a config object, right? So that can be like config object or config entity. So in our case, it's a simple configuration. So it's a config object, this is label and the mapping, in mapping, we actually provide various attributes that we want to map actually. So the main purpose or the why schema YAML was introduced because we wanted to provide a translation support for the configurations like what we have spring, right? That was the main purpose of introducing a schema YAML. And after that, like we extended it and it also provides supports of like generating forms on the generating forms on the configuration translation page. So that like it's easy for us to like figure out the translation. So mainly like we have actual configuration. So on our config form, actually I'll show you. Yeah. So on our config form, let me just open it. So this is our simple configuration form, okay? Which we have like two configurations right now and we want to eventually translate it fine. So basically this is help text and default description which basically holds our configuration value. And we have like various types for that configuration. So in our case like type is text. So there can be like many attributes. Like for example, there is a type, there is a label. There can be like translatable attribute like translatable true, false. We have like nullable attribute as well. Like for example, then you should be null or not. You can also provide class. Like if there are some types which doesn't have any default type data class we can provide custom class as well. So this takes like all these values and we can just provide the mapping over here. Once we are done with like schema YAML, we just need to provide the mapping over here. And names will be like similar to what we have in schema YAML. And this is our route name basically. The route that provides the configuration form in English actually. Together this will basically provide the translation support. So basically if we go here and we can actually add the translations over here. So this is basically providing a translation support for configuration. Now what would happen actually? We have stored some email text in our configuration. Like let's say we want to send an email to media owner whenever like media is expiring or a certain thing is happening, right? So the preference set by the editor is like I should receive email in my own language. So fine, like we have a default email text like when the media is expiring we have stored it in configuration. We want to translate it as well, right? So that like media's owner should receive email as per their own preferred language, right? So the same case is like I have taken this example from Drupal Core actually user module. So what we are doing here is like we get the language from the user account. Actually what user has selected, okay? He store original language for a while like get config override language. Now we actually set config override language and we actually like set dollar language which is like the language coming from users preference. So that like for a while the configuration language will be what is like selected by the user. So this is a snippet from actually user.mail and user.module and hook mail actually. So we get like all sort of configuration and again like once we are done with our job again set the original language. So that way what happens like we can actually send like personalized email like the translated emails to all the stakeholders. So this is something which we extensively used to send an email on various events actually whether in like the media is updated, node is updated. So this was like really handy. Now moving on to like next one. So the thing is that like let's say whenever we write like slash en on the URL like slash en slash hello one, right? So the like what basically decides like the language should be en, right? So language negotiation plugin typically takes care of all those things like they will identify the language and they will tell the system to like return that particular language. So in our case like we had a there was like a similar use case there were like feed URL and application was consuming it, right? So what was the case that like feed URL and didn't had any like language specific prefix or any other way that we can identify it. So the issue was like we were not able to properly detect the language based on URL or any other negotiation plugins that we have specified it. So we have to write our new language negotiation plugin. So fortunately there was one flag in the URL basically the portion of the URL that was like depicting or that was suggesting that this feed URL should be for English content actually. So we actually built a language plugin we were leveraging that portion and identifying the language based on that. So again I mean I have like shared hypothetical example I mean so like I have explained like this is like typical annotation for writing a new plugin. We are writing language negotiation plugin so provide ID name description and all those things. This is where our business logic is there. Like here like what I'm doing is like I'm getting the query parameter from the test type and language data language. But in like here we can like use all sort of business logic that we want to do to identify the language so that like we can like return proper language. So this is how we can actually leverage language negotiation plugin for like various environments. I mean typically we don't have requirements. I mean we don't have that much requirements of language negotiation plugins because like Drupal we take care of all those things like having URL prefix and all those things. But the kind of the cases that I just mentioned right in those cases like it is like really useful to have a language negotiation plugins. So this was like really handy and we like used it a lot. Apart from that yeah for interface text translation we like it's like we used it. I mean now we customized like we like used the context parameter of the T function. Actually pretty much everyone are familiar with T function. So that's pretty standard but we actually leveraged the context parameter of T function. So the case was that like we had a like multiple blocks actually which is like same title but we wanted to display like different transaction for those blocks. So the thing was that like English translation is same but we want to have contextual translation for both the blocks. So in those cases T function was like it takes argument called context actually that was like really helpful to us. So in our case the string was similar actually like the English string is saying and we just provided a different context for both the strings. So here this is what it looks on translation interface page. So what happens like we can actually translate both the strings individually. I think we should be able to see it over here. Yeah so these are like this is the block I have created. Actually I'll share the repository URL as well. It's a I have created a custom module. So this is the block I have created and this is basically a Spanish language. And yeah I mean the string is same English string is same for both the string and the contextual translation is different. So yeah and we can actually see here as well just as a kind of configuration. Yeah so this is how it will look I mean on the translation interface page. So this is something which we frequently used just to provide a contextual translation like various places. Apart from that like what we have T function in PHP we have like similar T filter or we can say translation filter in doing and in JS like the same which is like pretty standard. So we can like almost like everyone frequently uses it. So another case was like like adding translations in custom module. So like typically what happens for contributed module let's say we have a contributed module we install it and it has translation. So it will fetch translations from Drupal's localizations or what like Drupalized to Drupal.org. Now when we are writing custom module the case is different right. We need to provide our own translations. So now the thing is that it becomes hectic like sometimes we need to go there we need to upload translations. It's like kind of tedious process. So what happens actually? So Drupal has like a few actually attributes. So we can actually add interface translation project and interface translation server pattern in modules info.yaml file. And so what those attributes basically does like we can actually provide a pattern for PO files. So let's say I have provided a pattern so that like my translation will be there in like translation folder and the language code.io. So in our case it will be es.io. So now what will happen like Drupal will identify the particular translation when the module is installing and actually import it. So this is again localized way of handling translation like what Drupal.org it does with contributed module. So the next thing is like we have lots of translation I mean coming from various spaces like themes and all and we want to I mean those translation are frequently generated like by the developers or like if you are back end developers who are working on the themes or contributed modules or custom modules. So the thing is that periodically you want to like generate a PO file, upload it and like import it again, right? So to reduce those manual thing we can actually use a Dress Language module actually. So what Dress Language does is like it provides a generic utility around the language module. It has like various Dress commands basically like that allows us to create language and all those but two commands are like they're like Dress Translation Import and Translation Export command. So we can actually use those and we can import translation via Dress command. So the thing is that it will actually simplify the things for the like translation import and overall like translation process. We can in fact include CI pipelines whenever deployment will happen all the translation will get referenced. So we again I mean we used it very well and it was like easy for everyone. Yeah and after like if you want to try it out this is the repository. So you can just pull it and yeah you can try it out like all the things. I have created a custom module with all those custom the developer related changes that I have done. So in case if you want to play with that and that's it and I'll review the questions now. Charles I'm not clear about the translated field name thing that you mentioned we wind up. We wind up making the translation and one thing that is fit me in the same as the content of the translation really best in translation I don't actually understand. So we want the UI to be engaged but output the web page to be in the target. You mean so does it does it mean like let's say the translation forms text are in like let's say English and like while we are adding the French translation. I mean I mean if it is the case I'm not sure kind of like we can definitely provide like customized translation text but as long as like let's say let's say we are adding a French translation the URL itself will be like a far slash not slash and it is less you did and the translation form will be presented and definitely like out of box like Drupal will translate all the interface so interface will have translation but somehow we can I'm not quite sure like I haven't reviewed this kind of scenario but somehow I think we can manage it. I mean I can check it and get back to you maybe. Yeah, yeah, thanks. Any other questions? Cool, I think quickly check in just a second. Yeah, I'll share the slides as well. I'll upload it in session and I'll quit it as well. So yeah, you will be able to get it in and I'll share the Github URL as well. So yeah, we should be, I'll get it. Yeah, all right, I think, yeah, that's pretty much it. Thanks everyone. Thanks for joining today. Thank you.