Skip to main content

Localization

ZiWound supports 9 languages (Persian, English, Arabic, Chinese, Portuguese, Spanish, Dutch, Turkish, Russian) and implements localization two different ways. Which one you choose depends on whether a document is inherently multi-language or submitted in one language.

Pattern A โ€” localizedWarInfo: content in every language, stored togetherโ€‹

Used by the location models (country/province/city), user.bio, and blogPost.body. The field is a nested object with one key per language โ€” defined once in models/utils/localizedFields.ts:

export const localizedWarInfo = object({
fa: optional(string()), en: optional(string()), ar: optional(string()),
zh: optional(string()), pt: optional(string()), es: optional(string()),
nl: optional(string()), tr: optional(string()), ru: optional(string()),
});
// country.ts โ€” the wars_history field
wars_history: localizedWarInfo,

// blogPost.ts โ€” article body in all 9 languages
body: optional(localizedWarInfo),

When to use it: content that is the same document regardless of language โ€” a country's war history, an article, a bio. It's stored once, edited in place, and the client projects only the language it needs:

get: { name: true, wars_history: { en: true } } // client asks for English only

Since there's a single row, there's no selected_language โ€” every language is always present (or undefined until filled in).

Pattern B โ€” selected_language: one record per languageโ€‹

Where ZiWound needs per-language content to diverge (different records, different moderation), it instead stores the content in a shared field and pins a language on the row. See the model source and search acts in back/models/report.ts and back/src/report/.

When to use it: user-submitted content where one record is in one language (e.g. a report written in Persian). Query filters on the selected_language field:

// getRelated โ€” reports filtered by language
const language = set.selected_language || "fa";
filter.selected_language = language;

Choosing between A and Bโ€‹

Pattern A (localizedWarInfo)Pattern B (selected_language)
Data modelone row, object of stringsone row per language
Used foradmin-curated, same-content (countries, articles)user-submitted, single-language (reports)
Editingedit all languages in placeadd rows per language
Queryproject the language you needfilter by selected_language

ZiWound's rule: curated content = Pattern A; user-submitted content = Pattern B. Both use the same 9-language key set, so the frontend's next-intl can render either transparently.

Frontend consumptionโ€‹

The Next.js frontend (front/src/app/[locale]/) uses next-intl and routes each locale through the [locale] segment. Requests carry the active language, and acts that need it (report submission, search) include selected_language in their set validator.

Next: Search & Indexes.