Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the acf domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /home/evomark/public_html/wp-includes/functions.php on line 6121

Warning: Cannot modify header information - headers already sent by (output started at /home/evomark/public_html/wp-includes/functions.php:6121) in /home/evomark/public_html/wp-includes/rest-api/class-wp-rest-server.php on line 1896

Warning: Cannot modify header information - headers already sent by (output started at /home/evomark/public_html/wp-includes/functions.php:6121) in /home/evomark/public_html/wp-includes/rest-api/class-wp-rest-server.php on line 1896

Warning: Cannot modify header information - headers already sent by (output started at /home/evomark/public_html/wp-includes/functions.php:6121) in /home/evomark/public_html/wp-includes/rest-api/class-wp-rest-server.php on line 1896

Warning: Cannot modify header information - headers already sent by (output started at /home/evomark/public_html/wp-includes/functions.php:6121) in /home/evomark/public_html/wp-includes/rest-api/class-wp-rest-server.php on line 1896

Warning: Cannot modify header information - headers already sent by (output started at /home/evomark/public_html/wp-includes/functions.php:6121) in /home/evomark/public_html/wp-includes/rest-api/class-wp-rest-server.php on line 1896

Warning: Cannot modify header information - headers already sent by (output started at /home/evomark/public_html/wp-includes/functions.php:6121) in /home/evomark/public_html/wp-includes/rest-api/class-wp-rest-server.php on line 1896

Warning: Cannot modify header information - headers already sent by (output started at /home/evomark/public_html/wp-includes/functions.php:6121) in /home/evomark/public_html/wp-includes/rest-api/class-wp-rest-server.php on line 1896

Warning: Cannot modify header information - headers already sent by (output started at /home/evomark/public_html/wp-includes/functions.php:6121) in /home/evomark/public_html/wp-includes/rest-api/class-wp-rest-server.php on line 1896
{"id":746,"date":"2024-10-28T08:23:00","date_gmt":"2024-10-28T08:23:00","guid":{"rendered":"https:\/\/evomark.co.uk\/?page_id=746"},"modified":"2024-10-26T01:24:23","modified_gmt":"2024-10-26T00:24:23","slug":"inertia-i18n-internationalisation-for-vue-inertia-applications","status":"publish","type":"page","link":"https:\/\/evomark.co.uk\/open-source-software\/inertia-i18n-internationalisation-for-vue-inertia-applications\/","title":{"rendered":"Inertia-I18N: Internationalisation for Vue \/ Inertia Applications"},"content":{"rendered":"\n

There are over 7,000 languages in active use today. Even if you only deal in English, is that British English, American English, Caribbean English, Sinaporean English, Hong Kong English or any of the many other variations?<\/p>\n\n\n\n

Supporting multiple languages and dialects in your applications is often essential in our global world, and is something that generally presents a challenge to developers.<\/p>\n\n\n\n

Language is everywhere in software. It tells you what to do, when to do it, what just happened, what’s going to happen and explains the context around what you’re looking at. It’s inextricable from the software.<\/p>\n\n\n\n

So naturally we have to extricate it.<\/p>\n\n\n\n

Structuring for internationalisation<\/h2>\n\n\n\n

Take, for example, a simple text input. The text input will have a label, it may have a placeholder, it may have help text and it will have descriptive error messages. Something like this would not be unusual:<\/p>\n\n\n\n

<<\/span>div<\/span>><\/span><\/span><\/div>
\t<\/span><<\/span>label<\/span> <\/span>for<\/span>=<\/span>\"<\/span>first_name<\/span>\"<\/span>><\/span>First name<\/span><\/<\/span>label<\/span>><\/span><\/span><\/div>
\t<\/span><<\/span>input<\/span> <\/span>type<\/span>=<\/span>\"<\/span>text<\/span>\"<\/span> <\/span>id<\/span>=<\/span>\"<\/span>first_name<\/span>\"<\/span> <\/span>placeholder<\/span>=<\/span>\"<\/span>e.g. John<\/span>\"<\/span> <\/span>required<\/span> <\/span>\/><\/span><\/span><\/div>
\t<\/span><<\/span>p<\/span> <\/span>class<\/span>=<\/span>\"<\/span>error-message<\/span>\"<\/span>><\/span><\/<\/span>p<\/span>><\/span><\/span><\/div>
<\/span><\/<\/span>div<\/span>><\/span><\/div><\/code><\/pre><\/div>\n\n\n\n

This HTML template could be located anywhere in your codebase. The error message is probably generated on the server via a validation service that is buried away something else.<\/p>\n\n\n\n

Now imagine having dozens of different translations of all that text in those far reaches of your application. Completely unmanageable for all but the smallest of applications.<\/p>\n\n\n\n

The Laravel answer and the Inertia problem<\/h2>\n\n\n\n

Laravel has internationalisation (i18n) support built in via a collection of PHP files that are accessed through a __('some_file.some_field')<\/code> function. This works fine for the server and would work fine for Laravel Blade components too. Only problem is that if you’re using Inertia, you don’t have access to either Blade components or PHP functions in your application’s frontend.<\/p>\n\n\n\n

Sure, you could create a separate i18n setup for your frontend, but now you have the same problem of fragmenting from the intro. Every translation would likely become the subject of a hunt as you wonder if flash messages generated on the server but shown on the frontend should be part of which package. Then also, the duplications would be many.<\/p>\n\n\n\n

Ideally then, we need a way for our translations to be stored in one single place and then made available to both the server and the frontend, which is where our open-source package Inertia-I18n<\/em> comes in!<\/p>\n\n\n\n

Getting started<\/h2>\n\n\n\n

Because this package deals with both server setup and frontend features, it comes as a hybrid package (one containing both PHP and JavaScript in this case) and as such, we distribute it via Composer rather than NPM since the latter doesn’t allow any integration with PHP autoloading. So step one is to install it into your Laravel application:<\/p>\n\n\n\n

composer<\/span> require evo-mark\/inertia-i18n<\/span><\/div><\/code><\/pre><\/div>\n\n\n\n

We also need our bundlers to be able to use the JavaScript parts of the package, so we can install it as a “local” package like so:<\/p>\n\n\n\n

npm<\/span> <\/span>install<\/span> .\/vendor\/evo-mark\/inertia-i18n<\/span><\/div>
<\/span># OR<\/span><\/span><\/div>
<\/span>pnpm<\/span> <\/span>add<\/span> .\/vendor\/evo-mark\/inertia-i18n<\/span><\/div><\/code><\/pre><\/div>\n\n\n\n

The next step is setting up Vite to convert our PHP language files into a format that JavaScript can read. The handly Vite plugin included in our package automatically handles this.<\/p>\n\n\n\n

1<\/span>\/\/ vite.config.js<\/span><\/span><\/span><\/div>
2<\/span><\/span>import<\/span> <\/span>InertiaI18n<\/span> <\/span>from<\/span> <\/span>\"inertia-i18n\/vite\"<\/span>;<\/span><\/span><\/span><\/div>
3<\/span>\n<\/span><\/span><\/div>
4<\/span><\/span>export<\/span> <\/span>default<\/span> <\/span>{<\/span><\/span><\/span><\/div>
5<\/span> <\/span>plugins<\/span>:<\/span> <\/span>[<\/span>InertiaI18n<\/span>(<\/span>)<\/span>]<\/span>,<\/span><\/span><\/span><\/div>
6<\/span><\/span>}<\/span>;<\/span><\/span><\/div><\/code><\/pre><\/div>\n\n\n\n

Then finally we need to make our language files available to our Inertia application with a Vue plugin (also included)<\/p>\n\n\n\n

1<\/span>\/\/ resources\/js\/app.js<\/span><\/span><\/span><\/div>
2<\/span><\/span>import<\/span> <\/span>useInertiaI18nVue<\/span> <\/span>from<\/span> <\/span>\"inertia-i18n\/vue\"<\/span>;<\/span><\/span><\/span><\/div>
3<\/span>\n<\/span><\/span><\/div>
4<\/span><\/span>createInertiaApp<\/span>(<\/span>{<\/span><\/span><\/span><\/div>
5<\/span> <\/span>async<\/span> <\/span>setup<\/span>(<\/span>{<\/span> el<\/span>,<\/span> <\/span>App<\/span>,<\/span> props<\/span>,<\/span> plugin <\/span>}<\/span>)<\/span> <\/span>{<\/span><\/span><\/span><\/div>
6<\/span> <\/span>const<\/span> inertiaI18nPlugin <\/span>=<\/span> <\/span>useInertiaI18nVue<\/span>(<\/span>props<\/span>)<\/span>;<\/span><\/span><\/span><\/div>
7<\/span> <\/span>\/\/ Optional, but you may get warnings without this<\/span><\/span><\/span><\/div>
8<\/span> <\/span>await<\/span> inertiaI18nPlugin<\/span>.<\/span>load<\/span>(<\/span>)<\/span>;<\/span><\/span><\/span><\/div>
9<\/span>\n<\/span><\/span><\/div>
10<\/span> <\/span>createSSRApp<\/span>(<\/span>{<\/span> <\/span>render<\/span>:<\/span> <\/span>(<\/span>)<\/span> <\/span>=><\/span> <\/span>h<\/span>(<\/span>App<\/span>,<\/span> props<\/span>)<\/span> <\/span>}<\/span>)<\/span><\/span><\/span><\/div>
11<\/span> <\/span>.<\/span>use<\/span>(<\/span>plugin<\/span>)<\/span><\/span><\/span><\/div>
12<\/span> <\/span>.<\/span>use<\/span>(<\/span>inertiaI18nPlugin<\/span>)<\/span><\/span><\/span><\/div>
13<\/span> <\/span>.<\/span>mount<\/span>(<\/span>el<\/span>)<\/span>;<\/span><\/span><\/span><\/div>
14<\/span> <\/span>}<\/span>,<\/span><\/span><\/span><\/div>
15<\/span><\/span>}<\/span>)<\/span>;<\/span><\/span><\/div><\/code><\/pre><\/div>\n\n\n\n

Usage<\/h2>\n\n\n\n

From this point, your Laravel application tells Inertia which language files to load (using App::currentLocale()<\/code> and the config('app.fallback_locale')<\/code> setting for your fallback language) and then we hand the rest off to the Vue-I18N library<\/a> to handle the business of showing your text to users. So all of the standard methods are available to you<\/p>\n\n\n\n

<<\/span>template<\/span>><\/span><\/span><\/div>
<\/span><<\/span>div<\/span> <\/span>v-text<\/span>=<\/span>\"<\/span>$t(<\/span>'<\/span>messages.hello_world<\/span>'<\/span>)<\/span>\"<\/span>><\/span><\/<\/span>div<\/span>><\/span><\/span><\/div>
<\/span><\/<\/span>template<\/span>><\/span><\/div><\/code><\/pre><\/div>\n\n\n\n

or<\/p>\n\n\n\n

1<\/span><<\/span>script setup<\/span>><\/span><\/span><\/span><\/div>
2<\/span><\/span>import<\/span> <\/span>{<\/span> useI18n <\/span>}<\/span> <\/span>from<\/span> <\/span>'vue-i18n'<\/span>;<\/span><\/span><\/span><\/div>
3<\/span>\n<\/span><\/span><\/div>
4<\/span><\/span>const<\/span> <\/span>{<\/span> t <\/span>}<\/span> <\/span>=<\/span> <\/span>useI18n<\/span>(<\/span>)<\/span>;<\/span><\/span><\/span><\/div>
5<\/span><\/span>const<\/span> translated <\/span>=<\/span> <\/span>computed<\/span>(<\/span>(<\/span>)<\/span> <\/span>=><\/span> <\/span>t<\/span>(<\/span>'message<\/span>.<\/span>hello_world<\/span>)<\/span>)<\/span>;<\/span><\/span><\/span><\/div>
6<\/span><\/span><<\/span>\/<\/span>script<\/span>><\/span><\/span><\/div><\/code><\/pre><\/div>\n\n\n\n

Changing the language directory location<\/h2>\n\n\n\n

By default, the package assumes your language files are inside the lang<\/code> directory at the root of your application. If you have them elsewhere though, you can pass an alternate location to your vite plugin:<\/p>\n\n\n\n

1<\/span>\/\/ vite.config.js<\/span><\/span><\/span><\/div>
2<\/span><\/span>import<\/span> <\/span>InertiaI18n<\/span> <\/span>from<\/span> <\/span>\"inertia-i18n\/vite\"<\/span>;<\/span><\/span><\/span><\/div>
3<\/span>\n<\/span><\/span><\/div>
4<\/span><\/span>export<\/span> <\/span>default<\/span> <\/span>{<\/span><\/span><\/span><\/div>
5<\/span>\t<\/span>plugins<\/span>:<\/span> <\/span>[<\/span>InertiaI18n<\/span>(<\/span>{<\/span><\/span><\/span><\/div>
6<\/span>\t\t<\/span>langDirectory<\/span>:<\/span> <\/span>`<\/span>${<\/span>process<\/span>.<\/span>cwd<\/span>(<\/span>)<\/span>}<\/span>\/languages<\/span>`<\/span><\/span><\/span><\/div>
7<\/span>\t<\/span>}<\/span>)<\/span>]<\/span>,<\/span><\/span><\/span><\/div>
8<\/span><\/span>}<\/span>;<\/span><\/span><\/span><\/div>
9<\/span>\n<\/span><\/span><\/div><\/code><\/pre><\/div>\n\n\n\n

Conclusion<\/h2>\n\n\n\n

Now thanks to the power of Vite, any changes to your language PHP files will automatically build into JS compatible files and be loaded into your application (with support for HMR in dev mode).<\/p>\n\n\n\n

As always, if you have any problems or feedback using our open-source software, please post in the issues or discussions part of the GitHub repository.<\/p>\n\n\n\n

Bon voyage.<\/p>\n","protected":false},"excerpt":{"rendered":"

There are over 7,000 languages in active use today. Even if you only deal in English, is that British English, American English, Caribbean English, Sinaporean English, Hong Kong English or any of the many other variations? Supporting multiple languages and dialects in your applications is often essential in our global world, and is something that…<\/p>\n","protected":false},"author":2,"featured_media":747,"parent":218,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"templates\/open-source-software.php","meta":{"_acf_changed":false,"footnotes":""},"class_list":["post-746","page","type-page","status-publish","has-post-thumbnail","hentry"],"acf":[],"_links":{"self":[{"href":"https:\/\/evomark.co.uk\/wp-json\/wp\/v2\/pages\/746","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/evomark.co.uk\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/evomark.co.uk\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/evomark.co.uk\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/evomark.co.uk\/wp-json\/wp\/v2\/comments?post=746"}],"version-history":[{"count":12,"href":"https:\/\/evomark.co.uk\/wp-json\/wp\/v2\/pages\/746\/revisions"}],"predecessor-version":[{"id":759,"href":"https:\/\/evomark.co.uk\/wp-json\/wp\/v2\/pages\/746\/revisions\/759"}],"up":[{"embeddable":true,"href":"https:\/\/evomark.co.uk\/wp-json\/wp\/v2\/pages\/218"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/evomark.co.uk\/wp-json\/wp\/v2\/media\/747"}],"wp:attachment":[{"href":"https:\/\/evomark.co.uk\/wp-json\/wp\/v2\/media?parent=746"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}