 All right, welcome everyone. I'm very pleased to have everybody here today for the next in our webinar series. Today we're going to be talking about Drupal 8th Multilingual APIs and building multilingual sites for the, or building websites in general for the whole world. This is a joint presentation between the Drupal Association and Lingotech, one of our major supporting partners. And we're really happy to have them with us today on the call. The host today will be Gabor and Christian Lopez, and we'll be going ahead and getting started. So I'll let them introduce themselves, and we'll start talking about the Drupal 8th Multilingual. Hi, I'm Gabor Hoichi. I work for Acquia in the office of the CTO. And I also was the multilingual initiative lead for Drupal 8th for four and a half years. Hi, I'm Christian Lopez, and I work for Lingotech. And I've been working with Gabor and other awesome people in the Drupal 8th Multilingual initiative. Yeah, so although this is a joint webinar between the Drupal Association and Lingotech, what we are actually presenting is the work of more than 1,600 people who contributed to the multilingual initiative. So we'd like to start with a thanks to everyone who contributed and made all these great features possible throughout the several years that we worked on this initiative. And the reason we started working on this is because people have been building multilingual sites for quite a while on Drupal, but the experience was very far from perfect. So if you start off with the Drupal 7th site to make it multilingual, there's a lot of components to consider. First of all, you need to be able to set up languages on the site, which is managed by the locale module. And that allows you to have a list of languages and also do some interface translation on the site. However, the interface translation itself works with files uploaded from your computer. It's very tedious. If you have 100 modules and five languages, you need to manually locate 500 files, download to your computer, and upload them to your website. It's very inefficient. So we built the localization update module that allows you to automatically identify what to download. It downloads them and makes them available for you and imports them. It works quite well. But that's a contributed module that you need to add. And that doesn't really go anywhere else. It just automates your interface translations. So if you want to also translate content on the site, you need to enable the content translation module. And the content translation module works with nodes. It creates copies of your different nodes and as it translates them. So you can translate your content, but your menu items for your content and your categories for your content will still not be translatable. So when you edit a translation, you will not be able to translate the menu item. So you also download and enable the IETN module suite, which comes with several modules, including features for menu translation, taxonomy term translation, etc. So that sort of gives you a lot more flexibility, but still all the emails that are sent out to your users will be in the site's language. They are not possible to translate with either of these modules. So you also download the variable module suite, which is also several modules that deal with the configuration on Drupal. And then that lets you actually translate the emails that are sent out to users in the site name, slogan, etc. But then you also have a store on your website or you use the rules module or some other module that is not using nodes for maintaining its content. So then you need to enable the entity translation module, which also comes with several additional modules like title module that are required for it to work. And then you have views installed on the site. And now you need to have the IETN views glue module. And now you have web form installed on the site and you need to have the web form IETN and glue module installed on the site as well. So it's compatible. So each component that you want to have on the site may need to have its own glue module or mapping module to support multilingual features. And that very quickly becomes a maintenance burden. There's a lot of components to juggle with to figure out how to configure. So one of the very key things in the Drupal 7 lingo tech module is actually making you able to locate these components and making it easier to configure the modules on the site in a way that makes sense. Because all the settings that you have here will be very hard to navigate. Especially once you have these modules installed, you have two ways to translate nodes, both entity translation and content translation can translate nodes. And you also have two ways to translate taxonomy terms, both entity translation and IETN translates taxonomy terms. So there's a lot of possible problems that can arise from this scenario. So all we wanted to do in Drupal 8 instead is to make Drupal natively multilingual and support multilingual out of the box. So every other module that you add on top, whether it's commerce or rules or views or web forms will already work in a multilingual system. So we built that using four pillars and these are four modules that you can enable in Drupal core. So we have the language module that maintains the list of languages on the site. It's a base service for all modules to deal with language and it also supports everything to store language information. So your views would know their language, your menu items would know their language, your user profiles would know their specific language. Your email text will know their specific language. So when you need to translate them, you know, what's the source language? It's not just useful for multilingual sites. It's also useful if you just want to deal with data that's multilingual, but not necessarily have the site interface as multilingual. Also, we have the interface translation module as a separate module now, which has automated translation downloads and the built-in user interface is much nicer. So it's much easier to touch up on translations and deal with customized translation strings. We also have a content translation module, which only resembles the 777 version by its name because it now uses a field translation based system. So it's configurable on the field level and in some cases the subfield level, as we will see on the discussion later on. And it supports any kind of content, including user profiles, rules, e-commerce, whatever you have on the site. So it's very forward compatible in terms of content entities in Drupal 8. And then we have the configuration translation module, which provides a user interface on top of course, configuration language and translation management capabilities. So we have built-in functionality in Core to manage language on configuration and to translate any configuration in the system, email text, views, field labels, et cetera, et cetera, in the same way. This is also forward compatible for whatever you have in your contributed modules. So let's start with language management. The APIs that we have for these are what's going to come up in the sessions. So for language management, we have the language manager service in Drupal Core, which you can globally access with slash Drupal language manager. But you can also just get it from the service container. So with the language manager service by default is implemented by the language manager class, which is provided by the system. If you also enable the language module, then this is replaced with the configurable language manager. But basically, regardless of whether the site has a language module enabled or not, you have a language manager that can deal with language. And even if you don't have the language module enabled, Drupal comes with three languages. The UND not specified language that is used for cases where you could specify a language, but you have no idea what to set. The not applicable language, which should be used for cases where it does not make sense to assign a language. If you have a photo of some scenery, does not have any text on it, that would be not applicable in terms of language. And English is built in to core. If you enable the language module, then you can delete English. And it can also add any number of configurable languages. In this example, I've added Hungarian and Italian. And these show up in your configuration system. So when you export your configuration on the site, you will see all of these as language.entity.languagecode.yaml. And the only difference between them is the ones on the left will be locked, meaning you cannot edit or delete them. And the ones on the right will not be locked, so you can delete them, edit them, et cetera, however you want. You can also use the API to create these languages. So you can use the configurable language class. And there's handy helper methods on the configurable language to make it easy to create new languages. So you say you create from language code FR, they will pull some default data from our list of common languages. And we'll set up the French language for you, and you can save that. And later on, you can use the same class to load the French language and delete it if you want to make whatever changes you want to that language. So if you need a specific language, you can load that specific language and use it. But if you want to use the language that was used for the request, then you need to go to the language manager, which lets you get the current language that was negotiated for the request. So this runs through all the settings that you have on the site, whether it uses the browser settings for language detection, or whether it uses the domain or the path, whatever, and gets you the language that should be used for this request based on those settings. And you can use this in your code to display data based on whatever language was selected for the page. So that's the basics of how you deal with language. But of course, the language module does some other things that will not go into that are more complicated. Let's go on to the interface translation system. So now that we can create our languages and load our languages and make any operation on them, and we can also know the negotiated language for the current page, we may need to translate or strings for user interface. So for that, if you are familiar with Drupal 7, we have the t function that we can pass a string and it will get translated to the negotiated language. And in Drupal 8, we have the same, but we actually should avoid it in most cases. Internally, there are a lot of difference and we are going to see some of them. So before that, we need to introduce a concept called dependency injection. So if for example, we want to send an email to our users from our administration interface, our logic should load the users we want to email. We need to load the configuration and the preferred language of these users. And then we need to call the translation subsystem for translating these strings. And for that, we need to know a lot about the global context or code is running. So instead, in Drupal 8, we are reversing the arrows. And we will inject all the services that we need, all the classes that have the logic that we need to call, like the user subsystem configuration and translation services. And we will call the methods on them. This way we can swap them out as we saw before with the language manager. We can swap the default core language manager and the language module language manager. And this also makes our code more testable, which is a nice side effect. So instead of calling T, what we are going to call is this T function with the same arguments that you used to. And we don't really need to define the T function in every class we are creating, but still we can use the string translation trade. And this will include the string translation service in our class and the T function. So we will call this T instead. Yeah, most base classes like forum base and other interface classes already used the string translation trade. So a lot of base classes, if you extend from them, will already have this available and you don't need to care for it. In case you need to define your own class for something and you don't yet have the string translation trade, you can easily add the translation services this way. And as we said, internally is quite different. So calling T doesn't return a string anymore. We get a translatable markup object. And we can call methods on that, like getOption for knowing which language we call this function with. And these strings will be only translated when the rendering is actually happening. So if we are altering forms in our hook form alter and changing, removing strings that are there, they won't be actually translated if they are not really rendered. So it's like a nice performance improvement to have if they are not going to end in the final HTML or the final page. And for format plural, it's the same. But in this case, it's been removed from Dupal Core. So now we need to call this format plural instead. And this is included in the string translation trade too. So if we are standing for form base or we are using the string translation trade, we don't really need to define it again. So from JavaScript point of view, as in Dupal 7, we will have Dupal T and Dupal Format Plural, and they work in the same way. And for our templates, we have two different methods for translating. In Dupal 7, we have PHP template engine. So we were actually including PHP code in our templates. Now we don't do that anymore. We have the trick templating system, and we cannot call any PHP code anymore. So we have two different methods for translating our strings. The first one on the top is a filter. So we can use the dance filter after a string, and this will call the defunction. If we want to have placeholders in our strings or we are using context, it's not really readable. So we have a better option, which is the dance tag in the bottom. And this way we include our placeholders and we can include any context that will be passed to the defunction. Yeah, these will be parsed by unlocalized at Drupal.org and by the POTX module, and these will be replaced by the placeholder items that you are probably or may be used to from Drupal 7. So they will end up in a very similar way as they were in Drupal 7, but the two-week side API is different. Another difference between Drupal 7 and Drupal 8 is hook menu is gone. So now for defining our root and our menu links, we have YAML files. In hook menu, we have to define the title in English and Drupal 8 will translate them for you. And we shouldn't use t there because they won't be cached properly. In Drupal 8, we have the YAML files. We cannot include code there. So Drupal 8 will know in these YAML files which properties we need to translate with special keys. We will need to translate. So we have the title here and the description for our menu links. And Drupal 8 will call the PIN function for us. And in the same way, the POTX module will take care of extracting those. And if we are downloading our translations from localizeDrupal.org, it uses the same method for extracting these strings for making them available to the community to translate for translation. So we said, we saw in how we can translate our strings in our modules. We can make our modules available for the interface translation module for translating. And as we saw, we have to define our strings in English still. And we can translate with the interface translation from English to any other language. And the next pillar is the content translation module. As we saw, it's not based on having copies of our notes for each language, but instead we will have a field-based translation approach. So any translation will be in the same entity. If we are creating our own entities, we will be probably extending from content entity base. And I think we need to define an annotation where we define the metadata of our entity. We can say the ID of this entity, we provide a label, and we can define if we want that our site builders can define this entity as translatable. For that, we have the translatable key on the annotation. And this will make that administrators can make these entities translatable. They are not translatable by default, but this is an option that our site builders can use. And for making entities translatable and tracking the language of these entities, we need a langcode entity key. So we need to define in the entity keys array, we need to define the langcode entity with a field that will host the language. If we define our base fields in the base field definitions method, and if we use this langcode and we ensure that we call the base field definition from content entity base, this field will be created for us. So we don't really need to do anything else. And as an example in our PHP base field definitions called the parent base field definitions. So this will create the language for our notes. And for any field that we have there, we can call that set translatable method, setting them to true. And this will make a title of a translatable field by default. So when we enable translation for notes in our content translation settings page, title will be defined as translatable by default, but our site builders can still change that if they want to. So for fields, if we are coding around fields, there are good news. So they will be translatable by default. So we don't really need to do anything for integrating our fields with the translation mechanism in core. Yeah, this is for a configurable field. So whatever additional fields that you put on entities would support translatability automatically. And also the other side of the previous slide where we've seen the title field, the title field itself does not need to do anything special for it to support translation, only when it's defined as a base field, we can define its default translatability. So if you have a single-value field, you don't need to. You define a new type of field that is single-valued in your module, then you don't need to think about the translatability consequences at all. That's all up to the site builder. However, if you have multiple values in your field, you may define your column groups. So you can have, like in this example, we have an image field where we have different columns, the target ID, which identifies the file that we have as an image, the width and the height of this image and the alternative text and the title. So here we are grouping our target ID or width and our height and we have a file group. And then we have the alternative text as another group. So if we want to make our alternative text translatable by default, we have to set the translatable key to true. And this way, when we enabled our field or image fields for translation or content translation settings page, the alternative text and the title will be enabled by default and not the files, but still the site builders can configure that. Yeah, so in this case, this is the subfield translatability that I was talking about earlier. In this case, the content translation module exposes these groups as settings for a field translation. So under the field, you can configure the file as a thing in itself to be translatable or not and the alt text as a thing in itself to be translatable or not, even though this is under a field. And then the content translation module maintains the right values for different translations based on the configuration of the groups. So there's nothing else need to be done for the groups to be able to operate just defining them in an annotation. So now we can use entity API for loading our content or content entities and we have also operations for managing translations here. So we can call node load for loading a node object and then we can call on this object, we can call different methods like get translation and passing a langcode will get this translation from for Hungarian or if we don't need a concrete language but we want to load the negotiated language for the page we can use the entity repository, get translation from context method and it will use the negotiated language for loading this node. In the same way, we have several other methods on our content entities like get and translated for getting the source node, the language method for getting the language object that this node is in or get translation languages for getting the list of available translations that we have or has translation for checking if a given translation exists and we can add translation, remove translation, any other code operation on translations themselves. And when you call get and translated or you call get translation with the language code you get an object that could be identically used like you have the node before so it has the same methods and everything so you can navigate around different translations and treat them as the same entity. We are using node as an example here but any other entity works, users, menu items, etc. So we saw the entity API is quite powerful but we may have not needed but we have views in core in Drupal 8 and language is also integrated into views. So for views has two main operations which is requesting data from the system and then rendering that data. So we have language integrated in both sides. So on the left we can define which data we are creating into, we are filtering in the system and we can use the translation language so we can filter by having a given translation language and then for the rendering we can specify the language we want to render our content in so we can filter all the content that has a Hungarian translation and then display it in German as an example. So we may not really need to use APIs, we may use views for building or pages here. So as we saw we can, with the content translation model, we can translate from any language to any other language and what we are using here are intelligent objects that we can call or methods and operations for getting all the translations. Alright so next up is the configuration language and translation support and as I've said before this is also all built into Drupal Core much like all the things that we talked about earlier and the way this works is we maintain language information on every piece of configuration that we have on the system. So when you look at the system maintenance configuration file for example this is the file in its entirety so you have system maintenance.yaml it has a message key that contains the text for what message should be printed when the site is set to maintenance mode and it also has a length code key that says what language is the file in all the stuff that is in the file. So this way we know that this setting is in English and then different configuration files could be in different languages so maybe the email text that you send out to users may be configured in Spanish and a view may be configured in French whatever you have set up on your site so then Drupal knows what source language it deals with and translates from there and the translation is maintained in a way that we are replacing textual keys in the configuration files with their translations so we needed to have a way to define which parts of the configuration files are translatable because there's a lot of other data in the files that are not translatable and we need to avoid messing with them when we are doing the translation so we have the configuration schema system to define the structure of configuration so we can look up what's translatable and the configuration schema system uses data types that are dependent on each other so at the bottom here for example we have the text type that is based on the string type which is a built-in type and is also translatable so we use the text type for whatever is translatable and we have the config object type which is an associative mapping and has one key that's defined as length code which is of type string so we are using these type pieces, these typing information to define the structure of our configuration files and we are going to use the config object type and the text type to define the structure of system maintenance so when we define the system maintenance file structure we say this is a config object type so it's an associative mapping that has keys and values and one of the keys that are defined in config object is the length code so that's already defined for us and we add additional mapping keys to this mapping and that is the message key that we want to add which is of type text and we also provide labels for the translation user interface for this structure information but basically the type text carries forward the translatability characteristic of the string and the config object carries forward the language code key that we define in the associative array so when the configuration translation system looks at this file it knows that the system maintenance is an associative mapping it knows where to take the language code from and it knows which keys are translatable in this case the message key so we can store the translations for them the way we store these translations is we have the base file in the configuration system as system.maintenance.yaml and then we have languages slash language code slash system.maintenance.yaml which is storing the translation for the keys that are translatable and have translations for themselves so in this case the message key is translated as whatever the Hungarian equivalent of the message in English and then the same applies for Italian as well we store it under directory languages slash id slash etc etc so when you export your configuration you get all of your base configuration and also all of the translations all at once the translations will be in subdirectories that you can identify and the configuration translation module is basically built to provide forms and pages to translate the main configuration files based on the schema information that we have of them it uses the labels from the schema and then it generates these files in the configuration system and then we use these files as overrides on top of the configuration so when a Hungarian version of system maintenance is needed we load the original configuration and load the Hungarian file on top and merge them together and then we have the Hungarian translation of system maintenance and what's special about the configuration system is language is not just one way that overrides may happen it's also possible that there are other types of overrides on the system there may be global settings PHP overrides there may be domain based overrides there may be time of day based overrides or organic groups based overrides or whatever other system you have on the site that could use configuration overrides so when you load the configuration from Drupal using either the config factory or the Drupal config method then all of the overrides will apply as appropriate for that point in time and when you get the message from that configuration object it will be the message of the override that win the race of being applied in priority order if you want to have a specific language version of your configuration that's maintained by the language manager so this is unfortunately much harder to do than with the content entity API because the language overrides are maintained on the language manager so you ask the language manager for the override language to store for later and then you set your own override language in this case Hungarian and then you do something with the configuration like send emails as we've seen before and then you set the config override language back to what it was before to restore the state it's not very nice to have global states like this but because arbitrary overrides are possible in configuration there's no way the configuration system would know of all the possible variants of overrides that may exist on the system but this is basically rooted from Drupal's history of allowing all kinds of configuration changes based on groups or rules or domains or other things so if you want to specifically deal with these overrides or certain kind of overrides then as I've said when you just call up the configuration object these will apply all the overrides and the strongest will win if you want to have the configuration object in its original version without overrides then the config factory service has a get editable method that loads you the raw original configuration and that has no overrides whatsoever it's also possible to save back to that configuration object because it does not have randomly applied overrides from across the system if you want to deal with the language override specifically once again you reach back to the language manager and the language manager has a get language config override method that you give the language code and the config key and then you can do whatever you want with that language override in this case set a different value for the message and then you can save it so this is very similar to deal with as a configuration object it's just stored a different place in the configuration system so the configuration API is not as nice as the content API because configuration is mostly just dump arrays they don't really know where they came from how were they merged etc because the whole override process is not transparent to the configuration object however this also allows you to translate from whatever language to whatever other language and it's a lot more flexible than content because you can have variants based on whatever else you want one more thing to note is that we have all of these features in core so you can have your languages managed your interface translated your content and your configuration translated but these use separate user interfaces and there's no integration built in with translation memories or no integration with translation service providers so that's a feature that contributed modules like Lingotek provide very well where you don't need to care for whether something comes from content or from your interface they make it much easier to deal with the transitability of your site so once again all the features that we've presented thanks to these 1,600 people who worked on these features for more than four and a half years and made all of these possible to work in Drupal core so thanks to them for their contributions I'd also like to thank Gabor and Christian for presenting today and we'll do a little bit of time for Q&A as well if anyone has any questions they'd like to ask about Drupal multilingual APIs about translatability please feel free to use the Q&A button in the webinar client or use the chat we'll take a look there in the meantime Christian would you like to talk about the ebook that's available while we're waiting for questions yeah so we have an ebook covering all of what we talk in this webinar it's free to download at the lingotech.com site we will be also sending the link to every attendee with their recording URL so yeah if there's something we didn't cover please feel to reach out or use the Q&A section awesome okay it looks like we don't have questions at this time so I just want to go ahead and say thank you one more time to all of our attendees who joined us on this next webinar in the Drupal Association series I want to thank Christian and Gabor again for presenting with us and I would love to thank Lingotech for supporting the Drupal Association and for being just one of the most important supporters in our program their leaders in the translation system and in cloud-based language management so I encourage any of the viewers who have translation needs to check them out and we actually do have a question or two coming in so let's go ahead and run through a few of those the first one from Rogi question about interface is there a way to set up different language for interface let's see that's most of the question here let me take a quick look different than language for I think it was meant for different than language for content so yes so Drupal Core has a built-in way similar to what NGD translation had to have separate language negotiation for interface translation and content translation so you can configure them separately and you can display some content that's in a different language from the interface if you have that requirement the next question is it possible to have, this is from Patrice Diocardo is it possible to have contextual translations so I think we would need some clarification on that but if it means for a short text that needs context like if you say view what word does that mean or that kind of stuff then that is possible the same way as it was in Drupal 7 so it's possible to provide in the format plural and the TAPI context information for the string we also built in context information so it's also in the JavaScript API and we also built in that possibility to the routing YAML files and the plugin YAML files etc and I believe it's also possible to have that kind of integration schema so I think all the ways where interface translation is involved we try to make sure that the contextual the context is possible to define so I think the answer is yes let's see next question, are entities saved as a new entity when translated? No, so each translation is host under the same entity so if you have content like a node in English and you translate it to French you end up having one node which hosts both translations so fields are translated but we still have only one entity with all the shared values between them the next question is are there any issues with translation and translation comments so since it's the same entity the comments are stored on the entity and they are filtered for language when you display them in a language but it's either configurable on the comment field display or it could be configured if you swap out the fields displayed to something else so I think I'm not aware that there are problems it is also possible to translate comments themselves by the way because there are also entities so if somebody has that requirement we've heard that requirement from a photo site where people were posting comments this is a multilingual photo portfolio site somebody had a requirement to translate the comments because they wanted to show activity in all the languages that they've had on the site so they wanted to have the genuine original comments trusted to other languages that was interesting so some people need that too okay let's see one more question here I believe this is our last question but I encourage our remaining viewers to please go ahead and ask their questions if they have them but this question is what is the situation with multilingual and paragraphs module in the past there have been some issues so paragraphs are internally safe as entities so you can translate your paragraphs they are referencing the principal hosting content entity so you can translate your paragraphs but you cannot translate the reference to this paragraph you have a note which has three paragraphs and you can translate each of them but you cannot reference like three different paragraphs I think there's an issue on Drupal.org for that there's people working on that but it has not been committed yet okay alright I'll give it one more minute because I don't want to cut anyone off it's clear but if we have any final questions please let us know alright it sounds like we're good to go so again I want to thank our viewers I want to thank our presenters Christian and Gabor I want to thank Lingotech for supporting the association and supporting the community and if you have further questions again you can learn about the multilingual initiative you can reach out to Lingotech to learn about their cloud-based language solutions or if you have questions about particular issues I'm sure the issue queues for example the paragraphs issue queues we'll have some information about that paragraph issue and at that time I think we're going to go ahead and wrap up thank you again everyone thank you goodbye