Managing multilingual content is essential for global reach. TinaCMS provides versatile options to facilitate this. This guide focuses on two main strategies:
With directory-based localization, your content should be structured in a directory-based manner, where the locale (e.g., en, fr) lives underneath the collection root (e.g., blog, docs). For example:
.├── /blog/│ ├── /en/│ │ └── hello-world.md│ └── /fr/│ └── hello-world.md└── /docs/├── /en/│ └── my-doc.md└── /fr/└── my-doc.md
In your config.ts, you likely will have one collection that contains all your locales
export const config = {collections: [{label: 'Blog',name: 'blog',path: 'content/blog',// ...other settings},}
Whether you're using Next.js, or another framework, your routing logic should be updated to pick the correct locale based on either the URL or user setting.
// Example: Fetching the page list in NextJSconst getStaticPaths = async({ locales }) {// ...})
Using the locale, you can filter for document(s) based on the path
// /pages/post/[filename].tsx// `locale` is provided alongside `params`const getStaticProps = async({ params, locale }) {const tinaProps = await client.BlogPostQuery({// compose `relativePath` where `locale` is a sub-folder to the `post`relativePath: `${locale}/${params.filename}.mdx`,});return {props: {...tinaProps}}}
For more info on setting up the routing for NextJS-specific implementations, see our guide
With this setup, editors will browse locales for each collection via the document list.
If a user wants to create a new localized version of an existing document, they can click "duplicate document" from the document list, and prepend the desired locale in the new document's filename.
In this approach, each localized field contains nested values for multiple languages. For example, a single Markdown file might look like this:
{"title": {"en": "Hello","fr": "Bonjour"}}
You will need to modify your TinaCMS schema to include localized fields.
export const pageSchema = {label: 'Page',name: 'page',fields: [{label: 'Title',name: 'title',type: 'object',fields: [{type: 'string',name: 'en',label: 'English',},{type: 'string',name: 'fr',label: 'French',},],},// ...other fields],}
Note: If you are using markdown/mdx content, and want to use the markdown body for your content, you might prefer using the directory-based approach to localization.
In your site's components, you can then choose the correct localized field to display based on the current locale.
const PageComponent = ({ data, locale }) => {const title = data.title[locale]// ...display content}
TinaCMS will display all localized fields as children of the root-level field.
Last Edited: November 6, 2024© TinaCMS 2019–2024