 Hello, everyone. Welcome to the Moodle Academy webinar on translating user-generated content. My name is Rajanith Kotharam, and I'm a developer educator at Moodle Academy. And I'm based in Fiji. I'm joined by my colleague Anna Kerasa, who is also from the Moodle Academy team, and Anna is based in Greece. Anna will help me facilitate today's session. So I will be presenting today in the session today, together with Andrew Hancox. So again, my name is Rajanith Kotharam. I'm a developer educator at Moodle Academy. And my role at Moodle Academy involves creating developer courses and looking after the Moodle Academy Moodle site, which includes maintaining the site, creating plugins, fixing bugs, and all those things. And we have Andrew Hancox, who is the director of open source learning. Andrew is based in the UK. And we're excited to have Andrew here because he has developed two plugins that we will be discussing in today's session. And these two plugins are quite important to the work that we do at Moodle Academy. So we're very happy to have Andrew here as well. In this session today, we will cover the following things. We will discuss why translating content is important. We will talk about how you can generate, you can translate user-generated content. And we'll give an overview of the content translation plugin set. So this is a plugin that has been developed, especially with the purpose of translating user-generated content. We'll discuss some of the design and the challenges of developing this plugin and making it work to allow content translation to happen. And we'll also discuss some of the future plans that we have in terms of weather enhancing this plugin to make content translation much more easier. So why translate content? That's an important question for us at Moodle Academy based on the work that we do. And for many people, if you are saving people from different countries who speak different languages. And earlier in the session, I mentioned that I am based in Fiji. My colleague Anna is from Greece. Andrew is based in the UK. And then if you look at the chat, then we see many of you are joining from different countries. We all speak different languages. And for many of us, English would be a second language or third language. And you would agree that when you, it's easier to understand content which is in your own language. And that is the reason why it's important to have content translated. And the things for the work that we do at Moodle Academy, we try to ensure or at least strive towards having our courses and the webinars that we offer translated into different languages so that we can enable access to as many people as possible to be inclusive. And like I said, when you have content translated in your own content, then it's much more easier for you to understand. So basically the whole purpose of enabling content translation for us is to empower people to learn. I will now hand over to Andrew. Andrew is going to cover the rest of the slides and then I will join in later. OK, so yeah, good to meet you all. So I'm Andrew Hancox. I'm based on the south coast of England, director of open source learning, which is a small micro agency. I'm not affiliated with Moodle HQ outside of this project. And we specialize in outward facing learning management systems. So do a lot of work where organizations are training people that sit outside of their organization. So this work sort of came out of a project I did working with the Peruvian government in the part of a knowledge sharing exercise as part of the rebuild in the wake of the El Nino disaster. And we built some tools to allow translation to happen that then got noticed by Moodle HQ and got turned into this new suite of tools. So I'm going to run through sort of translation in Moodle and what we ended up building. So OK, elements of language. There's a few places where text comes from within Moodle. Core PHP and the server operating system. So when you localize dates, when you localizing dates is the obvious example, it uses the language pack that's installed at operating system level. You've then got language packs within Moodle. So obviously your users will select a language out of the languages that are installed and those are maintained via Amos. Hang on, sorry, I'm cutting ahead. So we'll get into the detail of, no way, OK. So yeah, language packs within Moodle. These are curated by Amos. They're provided by Moodle HQ. They're installed on your server within the Moodle instance itself. You've then got curated content. So you've got the stuff that's written by your teachers when they're creating page activities, they're writing quizzes, all that kind of thing. You've got the curated content from the educators using your platform. And then the fourth category of content you've got is user-generated content. So this is, it might be forum-closed. It might be entries in a database activity. So these are actually four very different sources of text where you've got less and less control and less and less sort of predictability about what there's going to be. So you can have a very structured approach to the core PHP and all the stuff that's handled by the underlying operating system. Moodle HQ have done heavy lifting with the language packs for you curated content. You can tell your teachers approximately what to do and they'll normally do it. User-generated content, if you want to start having that available in multiple languages, it's going to start getting quite complicated. So looking at how Moodle does and manages languages internally, obviously, the cyber administrator will make sure that the server has to write languages installed at an OS level. They'll install the right language packs into Moodle. The author of the content can actually write the courses of whatever language they want. And they can use the various filters to control what elements get visible. So you might use the multi-lang filter to embed the different translations in one block of text, or you might use the language accessibility availability restriction so that users see the page based on their chosen language. And then the user themselves viewing the site controls the language that they see. Moodle will respect the user agent, the information that comes across in the HTTP request tellers that tells the Moodle application what language their browser is expecting things to come in. And Moodle is quite clever in how it can use that. The user can set a language preference. The site has a default. And the Moodle app, when I wrote this, has one global setting. It's less flexible. So there's different ways Moodle will decide how to serve what language people are wanting to see. So OK, so translations within Core PHP and the server operating system. So this is where you install locales using like Audubon 2 app to get. You can manage the locales that are available. You can make sure that you've got the right database and coding set up so that the unicode characters used in non-Latin alphabets will get displayed correctly, diacritics and things like that. Again, that's the first level you need to worry about. Then you've got the language packs. So obviously any good Moodle developer knows you don't write any text within the body of your code. You create these libraries of strings. And then everything gets passed through the get underscore string calls. Or when it's embedded in a moustache template, you use the relevant moustache tags. Everyone should do this. 99% of people do. I think it's pretty hard to get a plug-in into the Moodle plugins database if you're not doing this bit correctly. But if you're installing plug-ins from GitHub, sometimes it can get slightly sketchy. If you're working with code that's by less experienced developers who haven't been working with Moodle, this is a step that sometimes gets skipped, which can cause a lot of problems when you're relying on that for functionality of the language packs. So the language packs are managed by Amos. They're installed by the admins into the Moodle instance. You can customize them through UI. And there's brilliant availability. So all the major Western languages are covered. Outside of that, Chinese, 94%. Next biggest outside of Western languages is Hindi. That is not all. Yeah, OK. These stats are quite old, but most languages, most major languages are covered by a language pack. When I looked, there were 2.4 million strings translated across 100 languages. So outside of the things that you're the person administering the platform can manage, you've got the curated content. So this is the course content created by instructional designers. So there are different approaches you might want to take depending on how you want to report on the user activity, how you want to enroll users, factors like that. So again, you're slightly at the mercy of the plugins. So all plugins should run text that gets output through format text, format string, external format string. And this runs the filters. So I'm just going to show you how to do that. The filters. So I'm assuming everyone is aware of this already, but Moodle has the concept of filters that user entered content will get run through. And it does things like check there's no hostile HTML in there, automatically links course names, embeds video players where it sees Bimeo links. But there's also several translation tools that hang off this. So again, all plugins should do this properly. Most plugins do, particularly for Moodle plugins database, but again, there are issues around this. So that's how curated content, but those are the hooks, those are the tools that you can use to deliver multi-lingual content. So the actual approach is when you've got curated content, the simplest option is always going to be you just have a course per language. It's so much easier. You don't end up having... It just removes so many levels of complication. The issue is you need to manage any content changes across all of those courses. You need to somehow enroll people on the course that's relevant for their language. You instantly can't report on course completions in a single place because you've got the same course present in multiple spaces. So there are some pretty big limitations with this approach, but it is the simplest option. The next simplest option is availability conditions. So where you have an element of a course that needs to be translated, you just have multiple instances of it in different languages. So you'll have one course and you'll have, for example, three different labels with the same content, English, Spanish, and German, and you'll use Renat de Blers' excellent restriction by language plugin to make sure that the user sees the one relevant for their language. Again, it gets in the way of reporting because you can't report on activity completion in the same way and it can introduce complications there, but again, it's pretty easy to manage and work with and it's more sustainable because at least you can get all the content in one screen. It's all in one place. It's all in one course. So the next step is multi-language filters. So this was the only way of doing it for quite a long time if you had to have a single activity supporting multiple languages. So you would embed these span tags within your content and a core filter would see these and only show the ones relevant to the user's current language. So then multi-lang 2 came out. It was slightly meter syntax, easy to work with, a bit more concise, not using standard span tags, using curly braces, but the same idea. So the text that you enter, you wrap it in these tags and the filter system will make sure that the end user gets the right text displayed to them. So it can be fiddly to edit. The M-lang tags from multi-lang 2 are easier to work with because it doesn't confuse the Atto editor as much as the span tags. But it's a hassle. You end up with, particularly with labels, a lot of the labels are restricted to 255 characters. So if you've got a 25-character label that you're going to display in six different languages, by the time you've got the tags around it as well, you probably won't fit it in the database field. So it doesn't work. It's relying on all the tags being filtered correctly through the filter stack, using those functions that we talked about earlier. There's a couple of spots in Moodle Core where that doesn't happen in quite the way you'd expect. There's quite a few third-party plugins that don't do it as consistently as one would hope. So that worked, but those were the options. So then Fahang Kamali released a content translation system which picked up and deployed it with the Peruvian government with some quite a few fixes to make it a bit more reliable, cover more use cases. And Moodle HQ, as part of the Moodle Academy Initiative, needed to deliver much more comprehensive translation of their sites. So again, it's a filter, but you don't need to put any tags in. It will take care of all the translation for you automatically. So you don't have the issues with having to put weird tags within all the content that you're entering. It's a lot easier for non-technical stakeholders to work with. It's a much more sustainable approach, but it does have its limitations that we will get onto. So essentially, when a piece of text goes through the filter stack, it gets hashed. So we create a sort of a 16-character signature for that text, which will be different for every block of text ever. And we can use that to see if we have an existing translation of it. If we don't have a translation of it, then we can either prompt the user to supply a translation or we can fire off a request to Google Translate to get a translation for it. Imagine you will show you the UI in the next bit, but it's all in line. It's a really neat user experience. It's a lot easier for the people producing the content to work with. There are some management tools, so you can see what proportion of your course is translated. If there's any languages where coverage is spotty, it's a much easier approach to work with. So I'm just going to skip ahead slightly. We've covered this already. So going on to some of the limitations, you can master through Google Translate. Google Translate is getting better all the time, but there are a couple of spaces where it has challenges. So one of the biggest problems that we've got in building this plugin is when the filter stack runs, when text gets run through the filter stack, the filter itself has no idea what the source language is. So if you end up with Spanish people and Portuguese people, South American people from Hispanic nations, you can end up with a bunch of different languages that superficially look the same and to Google Translate will look the same and potentially to your translators will look the same, but you don't actually know which one it was. Working in Peru, that was particularly problematic because we had a lot of dialects that Google Translate couldn't reliably tell apart. We also have Spanish people working on the project as well just to make it even more complex. I think there's other languages that overlap enough to confuse Google Translate like Austrian and German, things like that. And the issue around not knowing the context that the translation is being performed in comes up a lot. Individual names, that's a big problem with Google Translate. A lot of names mean something. So John Tyler, when Google Translate sees John Tyler, it will translate Tyler as in the guy that sticks ceramics to the wall of your house, which is uncomfortable. It's not great. There are names that have slightly more unfortunate translations into different languages, and that's been quite an uncomfortable and embarrassing problem a few times. So there are these challenges. Getting the filters to run in the right order is absolutely critical. So if the content translation filter runs after or before some filters, it can break the filter codes. It can translate things you don't want translated like. You can also get a lot of false positives in terms of identifying any content. So your Google Translate bill can go absolutely through the ceiling. So you have to be very, very careful. It's not a perfect solution, but it's the best we've got. I mean, it was great fun working with Google ASQ to build it. What's next? So Rajneel will go through this, improving the UI, the UX. But a lot of it comes down to what I would love to see from Google Core. So a couple of places that don't implement filters, making the editors more aware of the content that they are displaying, that would be a major step forward in the API. More flexible completion logic would make it easier to use availability restrictions and things like that to avoid this translation. It's a slightly technical thing, but classic British arrogance. English is the universal fallback. In the absence of anything else, Moodle will end up displaying English. And there are a couple of places where that makes it quite tricky to manage the translations that get displayed. The way language packs get implemented in Moodle Workplace makes things a bit tricky. You might not have come across this, but there's the concept of child language packs. And the inheritance hierarchy within Moodle Workplace isn't quite what you'd expect. Anyway, I'm going to stop presenting now. I hope that's given you some background as to the challenges of translating and where this product, where this project came from. So Andrew has covered a lot of the things that I have, but let me just quickly show you. Once you have the filter enabled on your site and you are in that inline translation mode, so this is something that you will see. So for each piece of content that can be translated by this plugin, the content translation plugin, you will see these icons. So these green icons with the tick indicates that this content has been translated. These ones indicate that it is something that needs to be translated. And an orange icon indicates that this content has been translated, but the content there has been updated. So it's possible that the translation also needs to be updated. So these are the indications that a user gets when they turn on inline translation. And to edit your translation, you will simply click on any of these icons, inline translation icons, that will then open up a page similar to this, which will show you the original text that you have in whatever language that was there, the language in which you want to translate to, and you can add your translation. And again, you have the full Etoeditor, which allows you full formatting, embedding files and everything. And then you can save this translation into your system. And then once the next time I use the Cs, accesses that particular page, then it will touch up the correct translation. An important aspect that Andrew mentioned that, we've got two sets of plugins. So once we have the content translation filter, which is responsible for fetching the translations and displaying those inline translation icons. And then within the Etoeditor, we've got another plugin, which is responsible for adding this translation span text that you see at the bottom. So for a normal user, in normal editing mode, they will just see what you see, what you get editor in Eto. But if you switch to the code view, then you will see that the Etoed translation plugin has added this additional translation span text. And this span text that I use to match the content with its corresponding translation. Andrew has explained how things work and what were the challenges. Let's just see some of the things that happened in the backend. Not really the actual backend, but things that our user does not see. So as mentioned, any content that gets rendered on the screen would usually pass through format text or format string. And when that function is called, then model intent will call all the filters that apply to the particular page, to the particular content. And because the content transition plugin is also a filter, so the filter method for that plugin, for the content transition plugin in this case, will also be called. And basically a filter plugin, what it does is quite simple. All it does is gets an input text, does some processing, filtering, whatever, and then returns the process text back to the output API, which is then responsible for displaying it in the appropriate format. A very simplified algorithm of the things that happen to fetch the translation, display the translation to the user is when the filter function gets the text to be filtered, then the function looks for, is there a translation span tag in that particular content? If so, it will extract that translation span tag and then remove the additional span tag that was added by the Ato filter, so that we get the raw text. And then it will generate a hash, MD5 hash of that content. So we have a hash of the text to be translated. And then of course, because we are dealing with translation, so we need to know which language to show the translation in. So we get the current language. And then the filter will look, is there a matching translation for that particular hash, for that hash that we get in the first step, for that particular language in our cached version? If so, then of course, you just return it, right? There's nothing else to do unless you are in inline translation mode. If there is nothing found in the cache, then the plugin, then the function goes and checks, you know, is there a translation that I can use from the database? And then it tries to get the best translation from the database. And then I'll discuss, you know, why we call it the best translation. So because there's a number of checks that it does to find a translation or a matching translation that it can use. And if we are in inline translation mode, if the user has permission to translate content on that particular page, then it will also inject those inline translation icon. That makes it easier to translate content. And for adding translations, saving translations into your system, again, a very simplified algorithm, everything starts from that filter plugin. So the filter gets that text, tries to find a matching span tag. If there's a span tag, then it extracts the hash. And that hash we call the MD5 key. And then generates a hash of the content to be translated. And that is referred to as the last generated hash. And I'll explain this again later in the presentation. And then for the particular text, what is the substitute text to use? So basically what is the translation that we want to display? And then it's a simple form thread and then save the data into the database. And then the next time when a user accesses it, the filter will fetch that content, cache it if it is not cached, and then display it to the user. So that's basically the algorithm that we use. Of course, there are other fields such as the ID, the user who translated the content, and the time-modified time-traded. But these are the specific fields that relate to the translation data for that particular content. We have the MD5 key, which is the identifier, the translation hash identifier used to identify that particular translation and match to its corresponding content. We have the last generated hash. And this is the hash of the text to be translated. And then, of course, we've got the language that is going to be used for that particular translation. The context ID just to match where that content or the text belongs to. This context ID is the modal context ID that every piece of content gets. And then we have the raw text, the text to be translated and the substitute, which is the translated data. So these are the details recorded for every piece of translation data. Now, let's see how all these things work with some examples. So let's say in your Ato editor, you have this particular text. This is my text in paragraph, in terms of paragraph text. So in Ato, this is what you will see. And then if you switch to the code view in HTML view, then you will see the Ato plugin will also insert this additional translation hash span tags. Now, when this piece of content gets passed to the filter, what the filter does is that, OK, is there a translation hash span tag? In this case, yes. So it will extract out the hash value and store it in the MD5 key. So that's the value that we have here. It will strip out the span text altogether because we just need the raw text now. So the raw text is also stored here. So this is the, again, the text without the translation span tags. And then the function will then create a hash of this particular content. In this case, this is my text enclosed in paragraph text. So it will create a hash and that is the generated hash of that content. So this MD5 key that you have here is a random hash value. It could be anything, but this generated hash is the hash of this particular content. So once the filter has all these things, now it checks the translation data. Is there anything in the cache or in the database? So the translation data that is recorded would be something like this. The main identifier, the MD5 key would be used again that is extracted from this translation hash span tags. The last generated hash is the hash of this raw text that is being translated. And if you notice that is the generated hash of the content. So it's the same value. It's just recorded under a different field name. And then the raw text is the text without the translation span tags and the substitute text, which is the translated data or the translation that needs to be shown that needs to be shown instead of this raw text. So this is what the translation data writes. So when the filter, when a user comes right who needs the content to be translated, what the plugin, what the filter will do, for this particular text, this is the MD5 key that I have. Is there a matching record with this ID? And then in this case, yes, there is one. So it will then display this substitute text instead of this original text. So that's basically a very simple thing that the filter does. Just finds a matching record and then instead of the original text, display the substitute text. And in our case, that substitute text would be the translation data, the translation of that particular content. Now, then that content can also be updated. So later on, right? So the content has been added, has been translated, and then now let's say a teaching staff comes in, then it's on editing, and then updates that text. So the text that was, you know, this is my text is updated through this text has been updated. Again, using Ato editor. So because this pen tag already existed, was already added to the particular content, the Ato plugin will not do anything. And the only thing that the teaching staff will do is just update the content. And this hash will remain the same. So content gets updated. Now the raw text has been updated. This text has been updated. That is the raw text. The MD5 key that was extracted was the same as the previous value and the generated hash. The generated hash is the hash of the content. In this case, the hash of the updated raw text. So the generated hash would be a new value. So we have a new value for the raw text. The same value as the MD5 key, which is, which acts as the identifier. And then we have the generated hash, which is again an updated value because the raw text has been updated. So now when the plugin looks for a translation data, what it does is this is what it gets. So this is the updated record. This is the updated information for the content, raw text, MD5 key and the generated hash. And now it checks. Is there a matching record for this MD5 key in the translation data? Yes. So there is the matching record with this. So okay, we have found a translation that we can display. So for this text, this is the translation substitute text that will be displayed. But we also have this generated hash and the last generated hash. So a nice thing about this plugin is that with this generated hash and then the last generated hash, the reason why this is stored is that, you know, then this allows it, okay, this MD5 key is matching. So this is the translation that I'm going to use, but the generated hash for the current content that I am looking at and its translation data, the last generated hash are different. So this gives an indication to the plugin that the content here has been, the raw text here is different. So this indicates, gives an indication that, okay, the text, the raw text might have been updated. So the content has been updated. So this is when you see in the inline translation mode, when the translation is displayed, of course this transition will be displayed, but it will also show with an orange icon that, you know, this content, this translation is outdated, is stale. So, you know, that transition might need to be updated. So that's the reason that's the importance of, you know, generating the hash of the raw text and then storing that here in the last generated hash. So that's basically a check to easily identify, you know, is the translation that we are displaying on the page up to date or not. So, yes. And one other thing I said that, you know, if I just go back to the previous slide here is that, so in this case you have text and you have content that has this pain text added. Sometimes it's also possible that you get some text which does not have this pain tag, this translation spend tag. So you will have this raw text as this text has been updated. The MD5 key, because there is no translation spend tags, then this key value will be null, right? It will be empty because there's no spend tags and there's no value to fetch. So the function will again generate the hash of the content. So it will get this hash. And let me just go back to this one, right? So it will get this generated hash. And then because there's no MD5 key to use, it will then use the last generated, this generated hash, right? In this case, when it looks at the database, okay, there's nothing matching the MD5 key because I don't have a MD5 key value. Now I'm just going to use the hash of the content. And the hash of the content value is the same as the large generated hash of the content. So because this does not match, but there is a matching record under this hash. So in this case, again, you know, because the identifier is not there, the system is still able to pick. Okay, there is nothing matching the exact MD5 key, but there is a similar, there is a translation that I can use because the hash of the content matches. And then the plugin is able to display this particular translation data. And that, again, is one of the advantages of this particular plugin is that, you know, it allows you to easily pick out the best translation. So that's one example of, you know, the use of the best translation. Another thing Andrew also mentioned is that the concept of parent and child languages. So the design of the plugin allows, if our content is translated in a parent language, then its translation can also be used in the child languages. For example, if something is translated in French, then the Canadian French, any user who switches to Canadian French, French will also be able to see those translations. And then if, for some reason, if you know somebody comes and updates the translation in the French, Canadian French language for the particular content, then of course that one would be used. But if there's no translation that exists, then the system will look, is there a matching translation in the parent language that I can use. And Andrew also mentioned about automatic translations. So Google Translate is supported at the moment. And there are some issues that Andrew discussed that, you know, that you need to keep note of. What's next? Future plans? Again, some of the things Andrew mentioned. For us, because this plugin is important, Eto is going to be out of support soon. And we have started work on creating a TinyMCE compatible version for the Eto plugin. So that is in the works, but there's some tricky things because with TinyMCE, they are, it's a bit more strict in the way that, you know, it accepts additional takes. So at the moment, it's not able to recognize the translation hash takes, so we need to, you know, work around that. Another thing, like we said, you know, enabling access is, you know, providing access to translation data from one side to another. So we are looking at, you know, having backup and restore of this translation data, better management of translation history, some UI improvements that would be coming soon and support for more automated translations. At the moment, Google Translate is supported, but it's not highly accurate. And then they're cost involved in using that Google Translate. So we are looking at some other options that, you know, that allows more people to use it without cost some open translation APIs. And then one of the challenges that we have because this is not a typical filter plugin that, you know, that just takes content and displays it after some processing. There's some additional things that happens, right? This plugin stores data, this plugin and injects inline translation icons and then, you know, allows other things and stores data in different parts of Moodle, right? For course, for sections, for activity modules, for blocks, for questions, basically anywhere that AtoEditor can be shown, it stores the translation data for. And backing up all those data is difficult. One, the filter plugin does not hook into the backup and restore API. So that's basically impossible to use the translation, sorry, the backup and restore API for the filter plugin. So the current workaround or the option that we are exploring is using an admin tool. So we'll have another plugin, an admin tool, a translation manager basically to handle the backup and restore of translations. The advantage here is that the admin tools can hook into a lot of these, not all. So we are also looking at, you know, making some improvements to code to allow admin tool to at least allow to hook up into the backup and restore API of all these things, right? Everywhere that translation data is recorded. So that's some challenges and things that we are working on. And like I said, at least for Moodle Academy, for our Moodle Educator Certification, and also on Moodle.org, this plugin is used. So it's quite important for us to, you know, have these things working and, yeah, have these things working. So here are the links to the plugins, the content translations filter available on the Moodle plugins database. And we've got the link to the code repository as well. And the auto translations plugin, again, also available on the Moodle plugins directory. And we've got the link to the GitHub repository as well. Now, let's move on to questions. We've got a few minutes for questions. So if there are any questions, then we can answer those. I think you can come on the chat, and there's a few in there that I'll just run through really quickly. So Ralph, translate once, use everywhere makes sense. But in some situations, the same string or term has to be translated differently. Is this possible? No, not at the moment. The translations, whether they've come from Google Translate or have been manually created, keep track of the Moodle context that they've been created in, be it an activity, a course, or system. But at the moment, they're not filtered based on that. Olay messing, do you have plans to implement other translating platforms? So as Ralph replied, there is a commit for Deepul. I've not tested it. The filter plugins can't have sub-plugins. So the way you would do this would be to have a Google Translate sub-plugin, a Deepul sub-plugin. That's not an option. It would be good if there does end up being an admin tool as well. Then that would be a good place for those auto-translation sub-plugins to go. Cost for Google Translate. So Google Translate is priced at $20 per million characters, which you can choose through surprisingly quickly. So when we deployed this in Peru, we were having a lot of people communicate via the forums activity. And that needed to be translated. We were having to automatically translate these conversations. So obviously there was a lot of content being created to run through. The bigger issue and the one you really need to watch out for is when you're translating very, very dynamic content. So as an example, I worked with the UN to deploy this for a project to educate agricultural, but people working in the agricultural sector across Africa. And when Moodle renders a quiz question, the multi-choice question puts a different ID in each question option for each attempt, which meant that every time anyone viewed the quiz, it was having to re-translate everything, because the MD5 passions were different, because one ID on a query string on an image that was embedded within one of the options changed every time. So suddenly the Google Translate bill went through the roof. So there's things like that that you really need to watch out for, but correctly and carefully configured, the cost is minimal. It also caches all the translations locally, so you only have to pay to translate a piece of text once. Ato Editor versus TinyMCE, again, a devious design decision up front on my part to use span tags, which are valid HTML and looked later, now means that TinyMCE breaks, which is frustrating. We'll need to figure out something. Ralph Hilgenstock, you can... So, okay. So you're giving the example of the quiz results page, which can vary a lot. And for someone managing translations, that's quite challenging to do. So this is one of the consequences of the fact that the plugin is translating reactively. We're translating the content that gets omitted by Moodle rather than translating it at the point that the content is edited, which strengths and weaknesses either way. So, yeah, that is a hassle, but there are reports. So if retranslation that gets missed gets recorded, anytime Moodle would have liked to have found a translation to a different language for a piece of content, if it can't find anything, it does log that. So you can actually go through and see all the stuff that needed translating. There's a lot of noise in that report. It can be quite tricky to spot what you actually need to do, but yeah. Backups and restores, originally also covered. If I could just add a few things. So just going back to the TinyMCE plugin, so we already have something created for TinyMCE, but like we said, the TinyMCE is quite strict on the syntax. So it's just dealing with how we can make the existing translation spans compatible with TinyMCE, so that it allows the otherwise, all the other functionalities for the TinyMCE plugin is available. I think you would be able to find that on the Moodle Academy GitHub repository. Still not complete, but there's something work in progress that you can have a look at. In terms of Ralph's question regarding translate once and use everywhere. So we've also found that an issue with Moodle Academy and other sites as well where you have a similar text or content that is using the same translation. So our workaround or at least our solution was that every piece of content should have a unique translation hash. And for that, we've added this additional schedule task. So if you would get the latest version of the plugin, I think you would see there's an additional setting in the filter settings for the content translation, for the schedule task on the fields on the table that you want the schedule task to run against and to find if there is a duplicate MD5 key, then it will generate a new one. And then another schedule task would then see, okay, there's a new there's a new span tag. Let me see is a matching translation that I can take towards that. So basically what it does is that every content now gets a new span tag, sorry, hash. And then if there is a matching translation, then a copy of the translation is made for that particular content. Now to translations, I mean no translations are shared, it's just a copy over. So if somebody wants to update a particular translation for a particular text, it only affects that, even though the share translations, but that translation is a copy over of the original translation record. So that helps, but also your translation table grows massively as well. But I think that helps fix or at least address that issue of having different translations for different context. Yeah. So as a Moodle developer, building a filter, the one thing you should always remember is that filters should not make the database calls and they should not make web service calls because it's really important that filters run really, really quickly because they run potentially on huge a huge number of times on every page load. This plugin obviously has to violate both of those principles. So if you've got Google Translate enabled, then the page the first time you view a page if it's all completely new content there could be tens if not over 100 calls to Google Translate to render the page which has a performance implication even if you're even if the translations are crashed or you're using or you're just using manual translations potentially there's a lot of database calls. So you do also need to take care with this from a performance perspective. John Cutie translator is needing full editing rights. So, yeah that does seem important doesn't it? This is a limitation. Because of the way translations get shared across different courses because of the limited very limited information that the filter has while it's running it's almost impossible to meaningfully implement capability restrictions. For example, a course summary would be displayed on the dashboard of a user so that means anyone so it's probably a bad example it's very hard to manage and it's one of the architectural limitations of this approach. This is the least bad way to implement translation within Moodle. Moodle has been brilliant in the way it's supported multilingual content coming from the other direction with language packs, with filter and things like that. But architecturally translating user generated content the architecture is not there and this is the best approach that could be arrived at so there isn't a issue there. Just to mention that although the translator rule can access basically all the content that needs to be translated through the reports but they actually do not need to have editing roles I think that's something that we missed to mention so you don't need to have full editing roles but technically it's still possible through the reports to view content that you are not supposed to access so if you go into inline translation mode you do not need to have course editing right so you can't make any changes to the course content but you can update just the translations so yeah and there are other things that we still need to work out but you don't need editing role at the moment for the course content yeah maybe it would help to say a bit more about the permissions for the user of the translation plugin because there seem to be separate permissions that allow the translator to start their translation at the moment so the translation capabilities are applied at a different role which is given at the course at the site context level so course editing roles rights are given at individual course level but for the translator to translate something they need to be enabled at the site level at the site context level so that's the only difference that we have at the moment I hope that answers your question so there are two separate there are two separate roles one for editing rights that a normal teacher would have but for a translator they would not have any editing rights to the course yeah that was my impression I'm speaking from the experience of using it in Moodle Academy so it seems like you have permission to translate actually just the language that you've been given permission to be a translator so the access actually is not so wide or so dangerous but maybe that's special for Moodle Academy in general we are using the same plugin that is available on the plugins directory so yeah we've got at the moment just restrictions on who can translate so that is given in a separate role applied at the at the system level and there is a permission capability that allows people to be able to do inline translation or not and that is separate from course editing rights and roles so let's quickly wrap up the session so thank you very much for joining and if you've enjoyed this session consider getting involved further and help Moodle Academy grow you can suggest ideas for new topics for webinars or courses you can go into our get involved course and then suggest an idea or vote for an existing idea suggested by others you could contribute to webinars or courses a webinar such as this one you could help us co-present or co-create courses and you could also help us translate our courses in other languages you would be using the plugin that we discussed in this session today and you will help translate courses in other languages and you know the whole idea like I mentioned in this session today is that you know is to make Moodle Academy more inclusive and please also help spread the word about Moodle Academy you can tell your friends and your colleagues about Moodle Academy and about the MEC when you complete a course on Moodle Academy you get a package and if you are an educator you could consider taking the Moodle Educator Certification these are you ready for the MEC quiz which you could take and once you complete that you know one of our certified Moodle providers is going to support you through the entire certification process and in the end thank you very much for joining thank you Andrew for your time and sharing your initial experience in this plugin and we hope to see all of you in a future Moodle Academy webinar