 And anyone that wants a copy of the slides, they should be on a blog post live on my website right now. So you can go download them, follow along, look at them later. I've also, all the resources I have at the end of the presentation are also linked from my blog post and in the PDF. So you can go get all the resources that I've used to learn most of what I know and other resources I've found useful as I continue learning how to properly internationalize code. So the biggest question that I think a lot of people run into is what is it? So internationalization is the process of writing code so that it can be translated into other languages at a future time. And so with that, there's the important distinction between internationalization and localization. So internationalization is getting everything ready to translate. Localization is the process of actually translating your code. The abbreviations used in the WordPress community, internationalization is abbreviated I-18-N and localization is abbreviated L-10-N. The numbers represent how many letters are between the first and last letter in the word. So it's incredibly complicated how they came up with that. So but then probably the biggest question is why should you care? And yes, I did not update my memes since three years ago. So over 48% of WordPress installations are in a language other than English. With that said, less than half are in American English. So we have to figure in there's British English, South Africa, New Zealand, Australia, Canada. All of those have some variation where there is some level of translation. So with American English being the most popular at 47%, does anyone have any idea what the second most popular language is in the WordPress community? So actually you might be right with Spanish, but I did not take the time to total up all of the different Spanish translations, like all the different variations of Spanish. So it probably is Spanish, but by single language category it is Japanese, which accounts for 5.9% of WordPress installs. And all of that came from wordpress.org on their statistics page. So how do I internationalize my code? The first step is if you're writing a theme or a plug-in is in the plug-in or themes header. You just add the text domain and the domain path lines and that kind of starts the process. Especially if your code, if you've wrote a plug-in and you've released it on wordpress.org, having the text domain defined is one of the most important steps. It's not the only step, but it's one of the important ones. And in that particular case too, your text domain should match the slug of your plug-in or theme. So if your plug-in name is My Awesome Plug-In and you can view it on wordpress.org slash plugins slash My Awesome Plug-In, My Awesome Plug-In would be your text domain. If you do not do that on things released on wordpress.org, it does make the automatic translation through translate.wordpress.org a little bit more difficult to be done automatically behind the scenes. And as a plug-in developer or a theme developer, you get that essentially for free. The community can go translate into whatever language they have a desire for it to be in, and it all happens behind the scenes. And as long as your code is set up right, you don't have to touch it again to keep it all working, get it all translated. The second important thing to do is to actually load your translations. So it's a little different in both plugins and themes. In plugins, you'd use the load plugin text domain function on the plugins loaded action hook. Excuse me. And for themes, similarly, you just use load theme text domain. The arguments those take will vary based on exactly how your code's organized and what you're writing. Yes? Can you translate the translation of a page? So that's a good question. The code is not linking necessarily directly to files that have all the translations. In the case of a plugin that you released on WordPress.org, you would actually only need to specify the first argument on the load plugin text domain function, and that would allow it to automatically load any translations from translate.wordpress.org. It would know from that. But if you're ever packaging translations with your code, then you need to specify the path where those translation files live so that they can be loaded. I'll cover a little bit more of that towards the end of the talk. Just kind of what it actually looks like to translate, go from the code to actually translate it into another language. But getting the load plugin text domain or load theme text domain functions called and set up correctly, that is what actually will load your translations. And so without that, it's really difficult to actually see any changes. So let me move on to the functions that you would use as a developer in your code to actually have everything be able to be translated. So for basic string translation, you'd use the double underscore function. It's literally two underscores. It also has a variation where you specify underscore e, and it is equivalent to just echoing the double underscore function. Both of those functions take two arguments. The first is your text, and the second is your text domain. And also it's very useful for everybody involved if you avoid using HTML in your strings. There are ways to use HTML in your strings, but it's not terribly useful to have it always hard-coded into your string that then goes to a translator. Translators don't necessarily need the ability to manipulate your markup. They should be translating text, not editing code. So, but then you should also help the translators. So be wary of words that have more than one meaning in English. And if you use them, clarify what their use is. A good example is the word post. It could be an actual fence post. It could be a blog post. Or you could be posting something to a website in the process of actually submitting the information. And so there is the underscore X function, which takes a third argument. So text domain gets moved to be the third argument, and the second argument is the context. And so you can specify it's a noun, it's a verb. You could specify that it's menu text. You could specify that it is the single version of a post type name. And all that does is help the translators know what the intent is in the word, what it means. And more importantly, in the case here, you have post, it could be a noun or a verb. If it's differentiated, the translators actually see that as two separate strings to translate. Even though the text is identical in English, translators get to translate it two different ways. So that when it's in their language and they're reading it, it makes sense. Yes. There's some sort of glossary context. There is not really a glossary for what you pop into context. My understanding is it's just an open industry. My example may not be the best to reflect that. No, because you set a menu and that's a location, but it's contextual, so. The intent of it is to provide context. And so as long as what you put in there provides that context, then I don't think it really matters what you put in there. And there is also a version of the underscore x function that will echo as well. And then, yeah, the word post could be translated as article or submit depending on context. But seriously, how about the translators? So other things that you can do that are cool is if you do a comment right before the line, that comment will actually appear in the files for the translators to see when they're translating. So especially the second use case, the text that was totally wicked. Not everyone, especially if English was not their first language, would understand what wicked would mean in that use case. They might think, that was evil, why was it evil? But no, it was something cool, it was excellent, it was wonderful. And yes, I did steal that from the Incredibles. So you know what your text means, make sure your translators know what it means as well. Then you get into escaping. And escaping actually applies to far more than just translated strings, but you should never trust user content. If a user has entered in text onto a site, some way, shape, or form, it should be validated to make sure it's safe. WordPress by default does this by running content through the WP cases post function, which will do things like make sure it doesn't have script tags in it. Make sure it doesn't have iframes. Things that could commonly be used potentially to carry out cross-site scripting attacks or other attacks on a website. By disallowing those for general editors, you avoid a lot of security holes in your application. So if your string should never have HTML in it, you can use the escape HTML. If it's ever going inside of an HTML attribute, using escape attribute will ensure that it cannot break your markup by breaking the attribute. So an example of that would be a link tag. You could have the title. You use the escape attribute underscore e. It will still translate the click me text, but it will ensure that that text does not break your HTML. If somehow the translator accidentally put in a double quote in their text, it's not going to end your link tag early and break your markup. Now we get to the fun part, the things that people do wrong when internationalizing code. So when WordPress goes through all of your code and goes through all of its translation functions, when you're actually putting together files that can then be translated, it does not execute the code. The code is only executed when the website is actually loaded, when it's running. It does not execute when you're building a list of strings that need to be translated. So if you do something like this, where your text that needs to be translated is in a variable, WordPress will look at that, the translator, what they will see is they will actually just literally see dollar sign string as what needs to be translated. And they'll have no context, no idea what needs to be translated. And likewise, when it actually runs, it's not going to be dollar sign string that gets sent as the actual text. So no translation will ever actually occur in that use case. Similarly, if you have the text domain as a variable, you're not going to have the correct text domain in your file. So while that would work when it runs, when you generate your pop file of all of your strings, it will break. For constants, it's the same issue. You have the possibly worst example of using a variable for both values. Similarly, trying to use a PHP variable in a string does not work either, because when the translator gets it, they will see dollar sign name as the text to translate, and they'll have no context. Also, you do not want to break up a string that has a dynamic value in the middle of it, because then in languages where the sentence structure is different, your content's not going to make sense. So instead, we have printf. Or if you need to return it as a string, you have sprintf. And so with this, you're able to use the same comments that you've used before for translation comments. And this is actually where it's probably most important to use those comments, is because you can specify what the variable in your string is. So you're able to use the printf placeholders. So in this example, I've got three different items, and I actually have flipped two and three as an example of it doesn't matter which order they end up in. One thing I would recommend is especially if you have more than one dynamic value that needs to be replaced in a single string, that you use the %1, %2, %3 format instead of just using %s or %d for a string or a digit. Because that way, if when it's translated, they need to flip which order they're in, the translators can do that, and it will still make sense in the end, and all the values go to the correct place. So in this example of the output, hello, George. You'll be 21 on June 2, 2019. And then if you only have one variable, as I said, you can just use the %s or %d for strings or numbers, respectively. So printf and sprintf are very, very handy functions in PHP. If you don't know a whole lot about them, I highly encourage you to go look them up on the php.net documentation site. So then we get into numbers. So you can have zero books, one book, or two books. In English, at least, you cannot have one books. That doesn't make sense. Sounds wrong. So once again, use printf. We're also able to use the underscore in function, which will allow you to pass a singular version of the string, a plural version of the string, the actual number, and then the text domain. And then what it will actually do is it will pick either the singular or plural based on which number is provided. So you can get your proper pluralization. And then also there is a number format i18n function in WordPress that will allow you to format a number consistently for different locations. I actually found a bug with that last night. I'll have to dig into some more. But if you put in the number 1,000 in English, we'd have a comma as the 1,000 separator. Other locations in the world may use a period instead of a comma for their 1,000 separator. So that function is supposed to take care of all that. But I'll have to dig in and see about why it didn't work with Slovak. So then we get into the newest portion of internationalization, which is internationalization in JavaScript. This was just added back in the fall with the introduction of Gutenberg. So now everything that you can do in PHP, you can do in your JavaScript, and you don't have to go through all of the more tedious localization procedures you would have had to have done in the past. So just like in PHP, you have a double underscore, you have an underscore X, underscore N, and you have access to S print F. These are all part of WordPress's I18N JavaScript package. They're available as long as the script is loaded. And in most cases, if you are telling WordPress that you have a script that uses internationalization, it will actually ensure that all the proper dependencies are loaded. But I still recommend making sure you have all the dependencies there. So you can translate your text. You can have your pluralizations happen correctly. And be sure to add the wp-i18n script to the dependency list for your JavaScript files. So then actually getting the JavaScript translations to have them load where they're available, you use the wp-set script translations, which takes the script handle and your text domain, and will load all the appropriate files. So now we're going to briefly cover localization, which is the process of actually translating the script to which is the process of actually translating code. So there are two ways to localize. The first is to use translate.wordpress.org, which at this time is only for WordPress.org themes and plugins, and for WordPress core. And then there's the hard way. So the easy way is have your plug-in or theme approved in the WordPress.org repository. And then you can ensure that the plug-in theme has the appropriate text domain line in the plug-in or theme header and that it matches the slug for your plug-in or theme on WordPress.org. And then you need to make sure you're actually telling WordPress to load the language files for your text domain. And as I mentioned earlier in the talk, when you're dealing with things that are translated on WordPress.org, you can specify just the text domain if you're not including translations with your actual release. And then you sit back and let other people translate your code for you, which to me sounds amazing because it saves time. And as a bonus, your plug-in or theme read-me file can be translated, which means when someone from another country is looking at WordPress.org in their language, your plug-in description is in their language as well. Then there's the hard way. So WordPress has a variety of internalization tools. If you ever use WPCLI, it has a lot of things that it can do. They've added some IHTNN options to WPCLI. So you can generate a POT file. And then a translator can translate the POT file into a POT file, which would have all of the appropriate strings translated. And then you can ensure that the POT file is named using the correct locale. And then you can use a man-line tool to generate a MOT file from the POT file. And then now you can also generate JSON files from the POT files. And then once that's all done, the MOT files and the JSON files can be loaded on your site and it's translated. So if that sounds confusing, it's because it is. There's just a lot of steps to that. The resources section of the slides have good resources on where to actually find tools to do all that, steps to do all of that. It's a little more in-depth than have time to cover in this talk today. But that is the overview of the process. And then, most importantly, test it. So I like that. And while we're on the topic of translation, the WordPress translation day is this upcoming Saturday, May 11th. It's 24 hours of translation. So if you know more than one language, you are more than welcome to go to WPTranslationDay.org and find more details on how you can get involved and help. Yes? Is this a thing that we did on Contributor Day? I was translating words to Hungarian at the last dot US. Is this the kind of same thing? Or do I need to do the coding part? So I don't know that this is the exact same as Contributor Day. But my understanding is this is much more of going to translate.wordpress.org and actually spending time translating strings from a language to another. I was translating words. Yeah. Translating the actual text of things from English into other languages. So yes. So my understanding is that is more what this is. So if you know more than one language, by all means, go help ensure WordPress core, your favorite plug-in, your favorite theme are translated into whichever other language you know. And in doing that, the whole community has access to more things in their language. And then here's a list of resources and references that I used. The first handful at the top are ones that on actually from wordpress.org in either the plug-in theme or the Gutenberg handbook. So and as I said at the beginning, these slides can all be downloaded from my website, davidwood.ninja. And then you can just click on those links and go look at everything and not have to try to type from the slides. So yes, your question? I was just going to ask a clarifying question. It's all kind of new to me. So for the functions that you have been going through so far, those are all just things that we're putting in our functions at PHP for our general site. Now, does that get translated, or do we have to prepare certain strings for more loud type of content? If you're running a site that does kind of generate new content consistently, how do you update for those for that? OK, so if I understand you're asking if all the stuff that I listed helps with translating actual content on the site, or if it's just for things that are in your code. Is that correct? Yeah. So all of the functions I listed are all WordPress functions you use anywhere in your plug-in or theme where you have text that is part of it. So if you think of a plug-in, it has an admin interface in a lot of cases that help the user know how to interact with your plug-in, change settings. And so all of your text on those pages, all the different setting names, options, those would all be run through one of those functions. So that someone could translate your plug-in into another language, and the admin interface would be updated to reflect that. As far as translating actual content on a site, like blog entry content, if the site needs to be multilingual, there are plug-ins out there that can help with that. WPML is one plug-in. I know there's a handful of other ones out there as well. I've had the most experience with WPML, and it does have some caveats and some places where it doesn't always work the greatest. But it is still a good option. I don't think it's free, but I do know there are some free ones out there. But if you're just writing a site in another language, then this would cover if you needed to use a plug-in. As long as the plug-in is set up correctly, most of what I've covered you won't even need to do. But if you build themes, these are steps that you should take to make sure that the community as a whole can help translate your plug-in or theme into their language. I was talking with a guy just a few minutes before, and he said he actually has a plug-in that he released. And it was in the tens of thousands of downloads. But then he got it set up where it could be translated, and someone translated it into Indian, and now he's in the hundreds of thousands of downloads. So just translating your plug-in, having it available to be translated, is a great way to open up your audience. I mean, there's a lot of people in the world that don't speak English as much and as great as English is, and as nice as it is to know it when you're traveling, because there are a lot of places that it's an important language to know. The rest of the world does not solely operate on English. So that's a good question. Anyone else have a question? Yes. You said this could support your reading files, so mark down. Yeah, so if your plug-in or theme is on WordPress.org, to the description that lists out on the pages, like on the plug-in page on WordPress.org, all of that content comes from the plug-ins readme file. And so that readme file can be translated on translate.wordpress.org. And then it's translated on WordPress.org. So. Because my thought was, I know Gutenberg walks, this in the future might be able to apply to content used in Gutenberg. Yeah, this was not necessarily intended to go with content as much as your code. So for the translation of strings in Gutenberg, you would use the I18N JavaScript package from WordPress. And that would allow you to have the exact same functionality in JavaScript that you have in PHP when it comes to translation. And I do actually have on my website, as part of the blog post, everything from this talk, I did actually yesterday publish a small plug-in to GitHub that just has an example of how to translate from English to another language. Davidwood.ninja. Yes? So is this a recommended standard for plug-in developers to just integrate this into their normal workflow, trying to have the goal of making everything translate? Yes, I would definitely say this is a very strong recommendation for anyone that's writing code in WordPress. Even if you're writing custom code for a website that you don't think will ever be translated, just last year we actually had a very large client come to company I work for. And all they wanted was their website to be translated. It was already in WordPress. It was a multi-site setup. They just wanted to be able to create another site, use the same theme as one of the other sites I already had, and have all of their content be in a different language. The problem was almost all of their code that was there. Because I also wanted the interface to be in that language as well for the admins. But almost all of the custom code, none of it was able to be translated without going and rewriting all of it. Because the previous developer did not use any of the internationalization functions. So we ended up rebuilding their entire site. In the process, we moved them to Gutenberg. And so far they've been happy. They've got one site launched in French, one in Korean, and then two other versions that are in English, UK and Canadian English. And potentially pending Japanese version. But I think they're running into some issues with that right now. I don't think there's anything that really covers audiophile translation in any of the WordPress items. I think for that, you would probably just need to have a second copy of it that's been translated. I don't know if anyone else has any suggestions on translating audio. So yeah. Any other questions?