3 May

How We’re Improving Localization and Internationalization in ACF

As you may have heard, Advanced Custom Fields 5.7 is now available in beta. This new version is packed with long-awaited features that will both improve your workflow and ACF’s performance. Today, we wanted to introduce you to an improvement that aims to do a little bit of both.

First, some background information. Version 5.7 sports a completely-rewritten JavaScript architecture that brings with it vastly improved ways to interact with fields. One of the biggest advantages with this new framework is a major expansion of ACF’s conditional logic capabilities.

These JavaScript improvements have opened up new opportunities for ACF, but also some rather unique challenges. One of the most important was how to best provide translations (handled in PHP) in our JavaScript environment.

The good news is that we have found a solution that will bring both efficiency and ease to the entire process – and we want to share it with you!

About l10n and i18n

Before we get into the nitty-gritty of how ACF 5.7 improves localization and internationalization, let’s take a brief look at what these processes are:


The process of internationalization (abbreviated as i18n) prepares a WordPress plugin or theme to be translated into different languages. WP makes this super easy with PHP functions such as __() used to both return translated text and generate .pot files.


Localization (abbreviated as l10n) is the process of actually translating a theme or plugin into a different language. When working with Javascript, this involves printing PHP translations into a Javascript Object via inline scripts.

These two processes work in tandem, yet they each serve a specific function. Localization (l10n) passes PHP data to JavaScript, while internationalization (i18n) uses l10n to pass PHP translate strings to JavaScript.

Our Challenge

The problem we faced in ACF 5.7 was that our existing l10n processes did not encourage a fast and efficient working environment.

Previously, translations were passed from PHP to JS through an inline script object that looked something like this:

Previous l10n object

acf.l10n = {
    uploadedTo: "Uploaded to this post"
    // ...

From there, we would use this data like so:

Previous l10n usage

var html = '<span class="acf-uploadedTo">' + acf.l10n.uploadedTo + '</span>';

This is all pretty basic stuff, and while this approach worked, there were some serious drawbacks:

  1. Timing. This inline script required that the ACF JS object already exist. This meant that translations didn’t exist during the execution of the ACF script. Instead, they were printed afterwards in the admin_footer.
  2. Transparency. The actual string was unknown when working in the JS file. That would require searching through the plugin files to find what the string (‘uploadedTo’, in this example) was.
  3. Reliability. If acf.l10n.uploadedTo didn’t exist, there was no fallback or default string.
  4. Efficiency. This l10n data would always be printed, even if the language is English and no actual translation exists. It was a terribly inefficient way to do things.

As we further developed the new conditional logic goodness in ACF 5.7, it became clear that the issues above needed to change. So, we went to work 💪😀

A New Approach

Starting in ACF v5.7, we have implemented a fully-revamped way to localize our plugin. Let’s take a look under the hood to see how it all works. We’ll take it issue-by-issue:


Solving the timing issue was quite simple. Instead of printing the acf.l10n data down in the footer, we made use of the WP recommended wp_localize_script() function to pass translation data from PHP to JS.

This function also creates an inline script. However, it is printed right before the main ACF JS file is included and passes the PHP data to a global JS variable called acfL10n.


Not knowing the exact string caused a lot of frustration when writing in Javascript. We’re happy to say that this process has been greatly improved!

Along with the new variable name, we also changed the way this variable looks. Which is something like this:

New l10n object

var acfL10n = {
    "Uploaded to this post": "Uploaded to this post"
    // ...

😳 The avid viewer may notice that the above object looks quite redundant as both the key and value are the same. Don’t fear, we have this covered later on!

And, to help make it easy to register strings for translation, we created the function acf_localize_text( $strings ). It can be used like so:

New localization function

    "Uploaded to this post" => __("Uploaded to this post", 'acf'),
    // ...


Adding reliability to our translations is a great by-product from the above changes. With our new system in place to register localized text, we can now interact with the data through a function like so:

New l10n usage

var html = '<span class="acf-uploadedTo">' + acf.__("Uploaded to this post") + '</span>';

This allows the JS to read just like PHP, while also providing a fallback if the translation doesn’t exist.


Remember how our acfL10n object had the same key and value? Super wasteful, right!

The solution to this is simple. All we do is loop over the registered strings and compare the array key to the array value. If they are the same, then we can safely assume that no translation exists for this string and it therefore doesn’t need to be printed to ‘acfL10n’.

Only translated strings (ones that have changed via the PHP __()function) will be registered. This means that when using ACF in an English language, the ‘acfL10n’ is completely empty. This a major efficiency win 🔥

Speaking in context

Providing context for translations is extremely important. We had a look at how WP’s _x() function worked and implemented a similar approach. Simply put, we now also have a JS acf._x() function too!

Data gets some love, too

We also added another function called, acf_localize_data( $data ), to do a similar job. This helps to separate the data away from translations.

Continuous Enhancements for a Better World

Both i18n and l10n are areas where we want to continually improve. It’s an important part of making ACF more user-friendly on a worldwide basis.

We’re also very excited to share all of the new features that have been developed for ACF 5.7. If you’re interested in beta testing, we’d love your feedback!