没有不值得去解决的问题,也没有不值得去学习的技术!

Why WordPress English Posts Miss New Tags: A Redis Cache Troubleshooting and Fix

[Figure 1: The English article edit page after the fix. The tag panel on the right fully displays 8 English tags, and the issue of missing tags no longer occurs.]

作者:

在

Recently, I continued organizing the development blog for the A Tour of Go multilingual translation project.

To make it easier for readers of different languages, I usually publish the Chinese article first and then use SlyTranslate to translate it into English.

Besides the title and body text, the article’s tags also need to be synchronized and translated.

My workflow is fairly fixed:

  1. Publish the Chinese article and set the corresponding tags.
  2. Run a batch script to create English versions for the Chinese tags that have not yet been translated.
  3. Review and refine the names and slugs of the English tags.
  4. Use SlyTranslate to translate the article.
  5. Review the English draft and publish it after confirming there are no issues.

Following this workflow, as long as the Chinese article has 8 tags and all 8 tags have established English translations, the English article should be associated with the corresponding 8 English tags.

But that is not what actually happened.

The issue I encountered this time is quite interesting: pre-existing English tags were inherited normally, but the newly created English tags were missing.

Stranger still, the tags and their translation relationships in the database were clearly correct.

1. First Inspection: The English Article Is Missing Two Tags

On October 20, 2026, while processing a new development blog post, I noticed the missing tag issue again.

The Chinese article ID was 27889, and it had 8 tags set in total.

Of these, 6 tags already had corresponding English versions, while the other 2 were newly added.

I ran the tag synchronization script following my usual workflow.

After synchronization completed, both new tags had their English versions successfully created:

Chinese TagEnglish TagCorresponding Relationship
本地内容覆盖Local Content Override46228 → 46232
站点架构Site Architecture46230 → 46234

Afterward, I manually modified the names and slugs of the new English tags and re-checked the corresponding relationships between the Chinese and English tags.

The inspection showed that all 8 Chinese tags had correct English translations.

At this point, I originally thought the tag preparation was complete and I could start translating the article.

However, after the English draft was generated, the problem reappeared.

The English draft ID was 27892, and it actually only had 6 tags.

It was exactly missing the two English tags that had just been created:

  • Local Content Override
  • Site Architecture

The other 6 pre-existing tags were all normal.

This time, I paid special attention to the timing of the inspection.

Before opening the English article editor, I had already checked through the database and API to confirm that the English draft only had 6 tags.

Therefore, we can rule out one possibility: the two tags were not lost because of opening the editor, saving the article, or a page display anomaly.

The omission occurred during the process of translating and generating the English draft, or in the tag association step directly related to this process.

2. The Database Is Clearly Correct, So Why Can’t the Program Read It?

Next, I started checking the tag translation relationships saved by Polylang.

Polylang is the plugin in WordPress responsible for associating multilingual content.

For example, the Chinese tag “站点架构” and the English tag “Site Architecture” are not matched simply by name; rather, Polylang saves the translation relationship between them.

The inspection results showed that the translation relationships for both new tags existed in the database.

Moreover, the relationships were written correctly.

This presented a contradiction that needed explaining:

The database knew which English tag corresponded to which Chinese tag, but when SlyTranslate migrated the tags, it seemed unable to retrieve these relationships.

During previous troubleshooting, I had also noticed another phenomenon.

The English tags had already been renamed to their English names, but when read through certain WordPress APIs, they still returned the old Chinese names.

Querying the database directly, however, returned the correct English names.

This led me to start suspecting the cache.

However, suspicion did not mean I had found the cause.

If I just cleared the entire Redis cache simply because the API data was inconsistent with the database, it might temporarily restore normalcy, but it could not explain where the problem actually lay.

Furthermore, clearing the cache only eliminates the current stale data; it does not guarantee that the same issue won’t happen again the next time a new tag is created.

Therefore, I decided to continue investigating the specific cache reading process.

3. The Real Breakthrough: The Command Line and the Website Read Different Caches

After further inspecting the actual code of WordPress, Polylang, and W3 Total Cache, I discovered a crucial detail.

The cache keys used by the server command line and the website requests are not exactly the same.

My batch tag synchronization script runs via the PHP command line.

Whereas the article translation request runs within the WordPress website environment.

The W3 Total Cache installed on the website incorporates the hostname used by the request into the object cache key.

This hostname can be simply understood as the website domain corresponding to the request.

When accessing the website normally, the request carries the website domain.

But the original batch tag script loaded WordPress directly without setting the corresponding hostname beforehand.

As a result, the two execution environments might use different cache locations.

This means:

The command line had just created the English tags and updated the cache it used.

But the other cache used by website requests might still hold the stale data from before the English tags were created.

The problem was not that the database failed to save successfully, but that the website was reading stale cache.

This investigation did not stop at the speculation stage.

By inspecting the actual objects saved in Redis in a read-only manner, I found the corresponding evidence:

Translation GroupCorresponding Relationship in DatabaseCorresponding Relationship in Website Stale Cache
46229Chinese 46228, English 46232Only Chinese 46228
46231Chinese 46230, English 46234Only Chinese 46230

These two cache records exactly corresponded to the two missing new English tags.

In other words, the translation groups in the database already contained both Chinese and English members, but the translation groups in the website’s stale cache still only had the Chinese members.

Next, I needed to see how SlyTranslate would handle this situation.

4. Why Were Only New Tags Missing, and Without Any Errors?

After checking SlyTranslate’s code, I found that the tag migration process roughly works like this:

First, it reads all tags associated with the Chinese article.

Then, it calls Polylang’s API one by one to query the English tag corresponding to each Chinese tag.

If the query is successful, it adds the English tag to the pending association list.

If the query fails, the plugin decides whether to automatically create an English tag based on its configuration.

And my website did not have the feature enabled to automatically create tags when translating articles.

The reason is simple: I already have a dedicated tag synchronization script, and I prefer to prepare the English tags first before translating the article.

Therefore, when Polylang returned “no corresponding English tag found” due to the stale cache, SlyTranslate would simply skip this tag.

It would not stop the entire translation process.

Nor would it remind me which tags were missing.

Ultimately, the plugin only associated the 6 English tags it successfully found to the draft and then returned success.

This explains the phenomenon observed earlier:

  • For English tags that had existed for a while, the relationship in the cache was also complete, so they could be inherited normally.
  • For newly created English tags, the website’s stale cache did not yet have the corresponding relationship, so they were skipped.
  • The entire article was still translated successfully, but the number of tags was short by two.

This trigger chain has been verified through actual cache contents and offline testing.

However, it is still necessary to distinguish between evidence and inference here.

We did not capture the per-tag logs inside the translation request that failed, so we cannot say we have 100% reconstructed every single function call at that time.

A more accurate conclusion is: We found stale cache that directly matched the failure phenomenon, and we can use this real cache data to reproduce the result of “translation successful, but missing two new tags.”

For me, this is much more valuable than simply guessing a Redis cache anomaly.

5. Initially Considered Fixing Cache Synchronization, Then Changed Direction

After finding the issue with inconsistent cache namespaces, my initial consideration was to fix the batch tag script.

The method was not complicated.

Before loading WordPress, set the hostname for the command line to match the website request.

This way, the command line and the website could use the same cache namespace.

At the same time, I would modify SlyTranslate so that it explicitly throws an error when it finds an incomplete tag relationship, instead of continuing to skip.

This solution could solve part of the problem and aligned well with normal cache maintenance practices.

But upon further consideration, I felt there was an even simpler direction.

Since the problem occurred when reading tag relationships during article translation, could I just make this specific step read the latest data?

My requirement was actually very clear:

I did not require the entire WordPress site to bypass the cache.

Nor did I need to redesign Redis’s cache management mechanism.

And there was certainly no need to modify the internal code of Polylang or W3 Total Cache for this.

I just wanted to ensure that when translating an article, I could accurately find the English tag corresponding to each Chinese tag.

Therefore, I leaned towards keeping the fix confined within SlyTranslate.

This also aligns with a principle I have gradually formed when dealing with project performance and architecture issues recently:

First, solve the problem that is actually affecting current usage.

If a problem can be solved through a localized modification, do not rush to expand it into a system-wide architectural overhaul.

6. Ultimately, Only One File in SlyTranslate Was Modified

The final chosen solution only modifies:

PolylangAdapter.php

It mainly does three things.

1. Bypass Query Result Cache When Reading Source Article Tags

When the plugin fetches the Chinese article tags, it no longer relies on query results that might be stale.

However, simply turning off this layer of cache is not enough.

Because when Polylang queries the tag relationships, it might still internally read another layer of object cache.

This was the key to this failure.

2. Read the Database Directly When Querying Tag Relationships

Added a new internal function specifically for tag migration:

get_uncached_tag_translation()

It obtains the Chinese-English tag relationships saved by Polylang through a parameterized, read-only database query.

This way, even if Redis still holds the old translation groups, it will not affect this step from fetching the latest mapping.

It should be noted that this solution uses the internal data storage format of the current Polylang version.

It is not a generic feature provided by Polylang’s official public API.

Therefore, when upgrading Polylang in the future, it will be necessary to re-confirm whether this part of the code is still applicable.

I think this trade-off is acceptable.

After all, keeping the dependency confined to a small, scoped function is more manageable than overhauling the site-wide cache mechanism just to fix a tag migration issue.

3. Check Again After Tags Are Written

The previous implementation had an obvious shortcoming.

After calling the WordPress tag association API, it did not confirm whether all tags were actually associated successfully.

Now, a completeness check has been added.

For example, if 8 English tags are expected to be associated, then after performing the tag association and related synchronization operations, it needs to re-read the actual tag list.

It compares not only the count but also the specific tag IDs.

Only when the expected set and the actual set are completely identical can the tag migration be considered successful.

If tags are missing, extra tags appear, or the database query fails, it explicitly returns an error.

This way, even if partial tag write failures occur for other reasons in the future, the situation where “the article translation succeeds, but tags are quietly missing a few” will not easily happen again.

The entire solution does not clear the Redis cache, nor does it introduce an automatic re-translation mechanism.

At the same time, it adopts a stricter policy: all English tag relationships must be prepared before translation. If the relationships are incomplete, it reports an error instead of skipping tags and continuing to complete the draft.

7. Before Deployment, First Verify If the Real Failure Can Be Reproduced

This time, I did not directly upload the modified PHP file to the server.

I first used the previously saved real cache data for offline testing.

The focus of the test was not simply to confirm that the function could run normally, but to confirm whether the original failure could be accurately reproduced, and whether the modified program could avoid the same result.

The most important comparison was:

Test CaseOriginal SlyTranslateModified Version
Using actual stale cache dataReturns success, only associates 6 tagsCorrectly associates all 8 tags
Tag relationship incompleteMight skip directlyExplicitly throws an error
Only a portion of tags writtenMight still return successCheck fails
A subsequent operation removes a tagLacks final verificationCan detect the omission

Ultimately, all 23 offline checks passed.

The PHP syntax check also passed.

Of course, offline testing still cannot replace the real website environment.

So I decided to deploy the fix first, and then verify it using the next regularly published blog post.

This way, there was no need to specifically create test articles, nor to repeatedly call the translation model for already translated articles.

8. Two Minor Issues Encountered During Production Deployment

After the code fix was completed, I still needed to deploy the file to the production server and commit it to GitHub.

During deployment, I required verifying the hash of the original production file first to confirm it exactly matched the locally analyzed version.

Then I backed up the original file before replacing it with the fixed version.

The entire process only involved a single PHP file in SlyTranslate.

However, when I ran the deployment command for the first time, an error occurred:

php: 未找到命令

The reason was not that PHP was not installed on the server, but that the PHP executable was not added to the default search path of the current command line.

The actual PHP path used by the server is:

/usr/local/php/bin/php

After switching to this path, the PHP syntax check passed, and the production file was successfully replaced.

Next, I encountered a minor issue when committing to GitHub.

Because a code patch file was saved in the repository, Git’s whitespace check identified some context indentation within the patch as a formatting issue.

But this content belonged to the patch file itself and was not an error in the actual PHP code.

Therefore, I did not arbitrarily modify the patch content just to suppress the warning.

Instead, I separately checked the formatting of the real code and whether the patch could be applied correctly.

After the checks passed, I successfully committed and pushed to GitHub.

This commit:

36a9621: Fix missing newly added English tags during SlyTranslate translation

At this point, the code fix and production deployment were fully completed.

But I had not yet directly classified this issue as completely resolved.

Because there was one last step: verification using a real new article.

9. Verification Passed on the Next Real Blog Post

After the fix was deployed, I continued processing the next development blog post following my usual workflow.

I did not redesign the testing process, nor did I specifically create a test article.

After normally completing the Chinese article publication, tag preparation, and English translation, I checked the newly generated English article.

This time, the English article ID was 27896.

The article was associated with the full set of 8 English tags:

  • code block
  • Code Comments
  • Completeness Audit
  • Go
  • Go Documentation
  • localization
  • Multilingual Translation
  • Translation Completeness

I first confirmed the tag list in the WordPress backend.

[Figure 1: The English article edit page after the fix. The tag panel on the right fully displays 8 English tags, and the issue of missing tags no longer occurs.]
[Figure 1: The English article edit page after the fix. The tag panel on the right fully displays 8 English tags, and the issue of missing tags no longer occurs.]

Subsequently, I opened the live front-end page of the English article.

The bottom of the page also displayed the full 8 tags.

[Figure 2: The tag list at the bottom of the English article's live front-end page. All 8 tags are displayed normally, confirming that the front-end display results are consistent with the backend.]
[Figure 2: The tag list at the bottom of the English article’s live front-end page. All 8 tags are displayed normally, confirming that the front-end display results are consistent with the backend.]

At this point, the fixed normal translation process has passed verification with a real article.

This is more indicative that the modification actually solved the problem encountered in current usage than simply seeing the PHP syntax check pass or all offline tests succeed.

However, I did not thereby judge the entire WordPress caching system to be completely free of issues.

This modification solved the problem of SlyTranslate reading stale translation mappings when migrating article tags.

If other areas still rely on different cache namespaces, there might be independent cache synchronization issues to deal with in the future.

But those issues do not fall within the scope of what had to be fixed this time.

10. A Few Takeaways from This Troubleshooting

Looking back at the entire process, I think there are three points worth recording.

First, just because the data in the database is correct does not mean the data the program actually reads is correct.

Initially, I had confirmed that the newly added English tags existed and that Polylang’s Chinese-English relationships were correct.

If I had only checked the database, it would have been easy to assume that the tag synchronization step was completely normal.

But the data the program actually uses might come from the cache.

When the cache is inconsistent with the database, a situation arises where the database is correct, but the program’s behavior is wrong.

Second, do not ignore the specific content just because the quantity looks normal.

There was a rather obvious pattern to the missing new tags this time:

Old tags were normal, new tags were missing.

This pattern ultimately helped us narrow the investigation scope down to the caching of new translation relationships, rather than continuing to suspect the entire tag association process.

The subsequent completeness check also no longer just compares the tag count, but compares the specific tag sets.

Third, fixing a problem does not necessarily require overhauling the entire related system.

When first discovering that the command line and website cache were out of sync, it is easy to expand the goal into fixing the entire caching system.

But my actual need was just to ensure that the correct tag relationships could be fetched when translating articles.

In the end, although the single-file solution added more lines of code, it only affects one specific business step.

For me, this scope of change is easier to understand and more convenient to maintain in the future.

Finally

This issue seemed to be nothing more than the English article missing two tags.

But from initially confirming the database relationships, to discovering the cache namespace difference, then modifying the plugin, completing deployment, and verifying with a real article, it involved multiple steps across WordPress, Polylang, Redis, W3 Total Cache, and SlyTranslate.

Ultimately, I did not choose to clear the cache site-wide, nor did I re-translate the already generated articles.

Instead, I had SlyTranslate read the latest translation relationships when migrating tags, and check if the results were complete after writing.

Now, the next real blog post has passed verification.

This fix has also been committed to GitHub, making it convenient to review the modification details and maintain it in the future.

For me, the most important takeaway is not having written a few dozen more lines of PHP code, but rather further validating a troubleshooting approach:

First, find out which layer of data the program actually read incorrectly, and then decide which step should be fixed.

If a problem can be solved with a localized modification, there is no need to rush into expanding it into a site-wide overhaul.

关于作者 · 持续学习与技术实践

我是一名拥有 15 年以上开发经验的技术从业者,自 2013 年起持续运营个人技术博客,记录工作与个人项目中遇到的真实问题,以及解决问题过程中的探索、实践和思考。

除了技术写作,我也在持续开发和维护自己的独立项目,探索新技术和新工具在实际场景中的应用。我始终相信,没有不值得去解决的问题,也没有不值得去学习的技术。

如果这篇文章对你有所帮助,欢迎浏览博客中的其他文章,也欢迎交流技术经验与想法。

关于我  ·  GitHub  ·  邮件联系