This site was originally managed with WordPress, but after various twists and turns, I migrated it to Sanity and Astro. (You can read the full migration story here.) The move gave me much more freedom to add new features, so I decided to make it possible to publish English versions of my Japanese articles.
The immediate reason was that I had started running an online shop selling print-on-demand products to customers overseas, mainly in English-speaking countries. I wanted a site that could communicate not only the products themselves, but also the activities and ideas behind them.
At first, I thought it would be better to use a well-known platform, so I published my English-language articles on Medium. Managing content scattered across several platforms, however, was a hassle. Continuously maintaining a service with social-media-like features did not suit my temperament particularly well, either.
I wanted to handle as much as possible within my own site, so I added a system for manually creating English versions of articles to my Sanity and Astro setup.
This article is a record of how I added English support to a small personal website.
Reviewed by a person, rather than translated automatically
Displaying an automatically translated version of an entire page, as services such as note, Medium, and Reddit do, would be easier both to implement and to maintain.
Even so, I decided to prepare English versions that I had reviewed myself, because automatic translation can sometimes shift the intended meaning or nuance slightly.
There are also Japanese words that I deliberately want to leave as they are rather than localize. Given cultural differences, some passages might come across strangely if translated too directly. My choice of words is not always especially refined, either, which makes misunderstandings even more likely.
That said, “reviewed by a person” may be overselling it: my English is nowhere near fluent, so I am sure some awkward translations will still slip through. Even so, I wanted to decide for myself which expressions to preserve and where extra context was needed.
What I wanted the system to do
I organized the requirements for English support as follows:
- Continue publishing Japanese articles at
/{slug}/ - Publish translated articles at
/en/{slug}/ - Prevent publication while the English title or body is empty
- Let readers move between the Japanese and English versions
- Tell search engines that the two pages are corresponding versions in different languages
- Keep previous/next navigation, related articles, the table of contents, search, and footnotes in English as much as possible while someone is reading an English article
- Make it clear from the article list whether an English version is available
Rather than rebuilding the entire site as a fully multilingual website, I limited the first version to what could be added comfortably to my existing article-writing workflow.
Adding English fields to the Japanese article document
The Sanity log document already contained the Japanese title, body, meta description, slug, publication date, images, taxonomies, and other information.
I added a collapsible English translation fieldset to it.
| Field | Purpose |
|---|---|
englishPublished | Switch for publishing the English version |
titleEn | English title |
metaDescriptionEn | Description for English-language search results |
bodyEn | English body text |
Another option would have been to create a separate Sanity document for each translation, but this time I kept both languages in the same log. I wanted to keep the scope of the initial changes small and use a simple structure that would fit into the existing article-production process.
Keeping the two versions in one document allows them to share the slug, publication date, main image, categories, tags, series, and other fields. It also makes the relationship between the Japanese and English versions unambiguous.
The English body uses the same Portable Text structure as the Japanese body. I can still use the conversion from pasted Obsidian Markdown, images, Blog Cards, callouts, footnotes, separators, and the rest of the existing features.
This approach reflects my current requirements, however. If I eventually want completely separate publication dates, images, classifications, or editorial workflows for each language, I may have to consider a different structure. That sounds like an enormous amount of work, though, so I hope language barriers become much thinner before then.
Static pages remain bilingual for now
In addition to the regular articles managed in Sanity, the site has static pages written directly in Astro.
For now, those pages display Japanese and English together on the same page. A heading, for example, places the English after the Japanese, in a format like “日本語 / English.”
It seems possible to give each language its own URL, but I prioritized publishing English versions of the Sanity articles and left the existing bilingual format in place for static pages. This is a small personal site, and for my purpose of connecting it with an English-language POD shop, that is good enough.
The site as a whole therefore does not have a perfectly consistent multilingual structure. I am a little curious about what full multilingual support would look like, but I plan to reconsider it only if the current setup starts causing problems in day-to-day use.
Preventing unfinished translations from being published
Simply adding English fields could result in an /en/ page being generated for an article with only a title filled in, or with an empty body.
I therefore made titleEn and bodyEn required whenever englishPublished is turned on.
if (!enabled) return true
if (!document.titleEn?.trim()) return 'English titleを入力してください。'
if (!Array.isArray(document.bodyEn) || document.bodyEn.length === 0) {
return 'English bodyを入力してください。'
}
return trueI also narrowed the publication query on the Astro side with the following GROQ conditions:
englishPublished == true &&
defined(titleEn) &&
count(bodyEn) > 0In other words, the content is checked both in Sanity Studio and in the query that generates the page. Even if I accidentally turn on the publication switch by itself, the setup makes it less likely that an incomplete English page will be generated.
Generating /en/{slug}/
Japanese articles continue to be generated from src/pages/[slug].astro.
For English articles, I added src/pages/en/[slug].astro, which generates static pages only for articles that meet the publication conditions.
Japanese https://oneoffobject.com/example-slug/
English https://oneoffobject.com/en/example-slug/On the English page, the title, body, and meta description are replaced with the values from the English fields. The slug, publication date, images, taxonomies, and other shared information still come from the same document as the Japanese version.
I consolidated URL generation into a shared function so that individual components—such as previous/next navigation and related articles—would not each have to assemble the /en/ path for themselves.
Language switching, canonical URLs, and hreflang
A Read in English link appears on a Japanese article only when an English version exists. From the English article, readers can return to the original through a 日本語で読む link.
Articles without an English version do not display the language-switching link, so it cannot send readers to a nonexistent /en/ page.
In the HTML, the site switches between <html lang="ja"> and <html lang="en"> according to the language. Each language page uses its own URL as the canonical URL, while hreflang links identify the corresponding Japanese and English pages.
<link rel="canonical" href="https://oneoffobject.com/en/example-slug/">
<link rel="alternate" hreflang="ja" href="https://oneoffobject.com/example-slug/">
<link rel="alternate" hreflang="en" href="https://oneoffobject.com/en/example-slug/">
<link rel="alternate" hreflang="x-default" href="https://oneoffobject.com/example-slug/">I also switch the Open Graph locale from ja_JP for Japanese to en_US for English.
This tells search engines that the two pages are not duplicate copies of the same content, but corresponding pages in different languages.
Translating the UI around the article as well
Even if the body itself is in English, an article can still be awkward to read when the surrounding interface is filled with Japanese. With that in mind, I also made the following small elements change according to the page language:
- Table of contents
- Previous/next article navigation
- Related articles
- Date display
- Search dialog
- Theme switcher
- Header
aria-labelvalues - Footnote heading and back-to-text links
Previous and next articles are fetched by their English titles only when their translations have been published. This prevents someone reading in English from clicking “next” and suddenly landing on a Japanese-only page.
Some shared elements, including parts of the header and footer, mean that Japanese has not disappeared from English pages entirely. My goal was simply to minimize distracting information while someone is reading the article itself.
Supporting both languages in taxonomies and article lists
Translating the articles alone would still leave English-speaking readers struggling to move around the site if categories and tags were available only in Japanese.
I therefore added English names and descriptions to the category, tag, platform, series, and project taxonomies. I also added shared logic to avoid displaying the same term twice when its Japanese and English names are identical.
In article lists and the Recent Updates section on the homepage, the Japanese and English titles are shown as a pair, with a link to the English page only when a translation exists. I also added a filter to the Nonfiction index for showing articles according to whether an English version is available.
Japanese title
[EN] English title
Category: Japanese name (English name)The drawback is that term names can become long. Showing both the Japanese and English names also increases the amount of information in each listing. I could not think of a decisively better approach, however, and it is not a particularly serious problem at this point, so I kept this format.
I extended English support to taxonomies and static pages because I wanted readers to be able to make their way around the site, at least in a basic sense, without relying on the browser’s translation feature.
Separating Japanese and English search in Pagefind
This site uses Pagefind for static-site search.
When I added translated articles, I configured Japanese pages to enter a Japanese search index and English pages to enter an English search index. On English pages, the search UI initializes Pagefind in English mode.
Astro build
└── dist/
├── Japanese pages ──→ Pagefind ja index
└── /en/ pages, etc. ──→ Pagefind en indexI do not know how many people use the site search. To be honest, I suspect it is almost nobody. English search will also have limited value while only a small number of articles are available. Still, someone might use it someday, so I included it in the initial implementation.
From Publish to the production site
I divided the implementation into roughly four stages:
- Add the English fields,
/en/pages, and language switching - Add an English Pagefind search index
- Add bilingual support to taxonomies, article lists, and static pages
- Fix footnotes, duplicate labels, and other issues discovered after release
Publishing in Sanity Studio updates the production dataset; it does not directly rewrite the static HTML already online. On this site, however, a Sanity webhook calls a Cloudflare Pages Deploy Hook. Publishing therefore automatically starts a new Astro build and deployment. The Pagefind search indexes are rebuilt during that process as well.
In practice, the workflow looks like this:
Prepare the translation in Obsidian or elsewhere
↓
Paste it into Sanity Studio and review it
↓
Turn on englishPublished and select Publish
↓
The webhook automatically starts a Cloudflare Pages build
↓
Regenerate and deploy Astro’s static HTML and the Pagefind indexes
↓
After a few minutes, check the /en/ URL, hreflang, and English searchAfter publishing, I include waiting for the deployment and checking the production URL and English search as part of the article-publication workflow. Sometimes I skip those checks because I cannot be bothered, but they are supposed to be part of it.
I wrote more about the path from Sanity’s Publish action to the production site in an earlier article, “From WordPress to Sanity and Astro: Rebuilding My Personal Site.”
Small details I addressed later
After the initial implementation, I added English labels to taxonomy index pages that I had overlooked. I also changed the heading for footnotes in English articles to Footnotes, the return link to Back to text, and translated the relevant aria-label values.
English body text and page titles are easy to notice, but footnotes, accessibility labels, shared footers, and similar details are much easier to miss.
There is a very good chance that something is still slipping through somewhere. If I had planned for English support while migrating from WordPress to Sanity and Astro, the process might have gone more smoothly.
Still, nobody knows what tomorrow will bring, so there is not much to be done about that. Six months ago, it never even crossed my mind that I would start writing articles in English. Even with a make-it-up-as-I-go approach, things somehow work out if I check the affected areas and fix them little by little.
How I choose articles to translate, and what comes next
As of August 21, 2026, I am adding English versions of older articles at a pace of about one per day.
My criteria for choosing them are fairly loose. Unless something is clearly intended only for readers in Japan, I look over the article and decide more or less by feel. Reviewing the old article also gives me a chance to fix parts of the Japanese version that bother me, so I get two jobs done at once.
When I publish new Japanese articles, I also plan to release the English version at the same time whenever translating it seems worthwhile.
The feature is still new, and I have not seen any clear change in overseas readership or search traffic. I have no idea whether it will produce any results for the overseas POD shop that prompted the translation project. I did not exactly begin with solid evidence that it would. I enjoy making these small improvements, though, so I think I will keep going even if they turn out not to mean very much.
Conclusion
If you look into multilingual websites, you will find many possible approaches depending on the number of languages, the types of content, and the purpose of the site. There is information aimed at corporate websites and web services, but I could not find much about handling English support on a small personal site in quite this way. It may be too niche to be useful, but I decided to write it up anyway.
The setup includes compromises, such as placing Japanese and English together on static pages and accepting longer taxonomy labels. Even so, I now have a foundation for managing and publishing the English content that had been scattered across Medium from within my own site. That alone has made everything feel much tidier.
DID YOU ENJOY THIS ARTICLE?
制作活動を支援する / Support my work
文章、アート、デザイン、個人開発を続けるための支援を、コーヒー一杯分から受け付けています。 / Help me keep making essays, art, design, and independent software.

Information

