Authoring Translations
Dialects
The locale model is pure BCP 47 — there is no separate dialect taxonomy. A dialect is just a locale tag with variant/private-use subtags layered on a base locale: ar-SY-x-lattakia = base locale ar-SY (Arabic, region Syria) + private-use block x-lattakia (the Lattakia dialect of Syrian Arabic).
Reading a tag
import { parseLocale, hasDialect } from '@secundus-studio/stift-core'
parseLocale('ar-SY-x-lattakia')
// { tag: 'ar-SY-x-lattakia', language: 'ar', region: 'SY', variants: ['x-lattakia'], ... }
hasDialect('ar-SY-x-lattakia') // true — carries variant/private-use subtags
hasDialect('ar-SY') // false — plain base locale
Variant/private-use subtags are the dialect portion. The u/t Unicode extensions are not dialects — a numbering system is not a dialect, so don't create a locale file for one (e.g. ar-EG-u-nu-arab has a numbering system but no dialect).
Registering dialects
List every tag with translations in locales.supported:
locales: { default: 'en', supported: ['en', 'ar', 'ar-SY', 'ar-SY-x-lattakia'] }
A registered dialect is a first-class locale: its own locales/<tag>/<ns>.json files, its own compiled packs, its own RegisteredLocale union entry. Dialects may be sparse: a dialect file can ship only some namespaces; the generator completes the rest from the chain. A dialect that isn't registered still works — it falls back to its most specific registered ancestor (resolveSupportedLocale('en-GB', ['en', 'ar']) → 'en').
The inheritance chain
Every supported tag belongs to a chain from most-specific to least-specific, always terminated by the configured default locale:
ar-SY-x-lattakia ← most specific (Lattakia dialect of Syrian Arabic)
└─ ar-SY ← Syrian Arabic
└─ ar ← Arabic (Modern Standard)
└─ en ← default (the schema)
Two things consume this chain: fallback resolution (a missing key reads from the nearest registered ancestor) and translation inheritance (a value written at a less-specific level applies to every dialect below it unless overridden). Write only what differs per level:
// locales/ar/common.json — Modern Standard Arabic, applies to ar-SY and ar-SY-x-lattakia too
{ "greeting": "مرحباً، {name}!" }
// locales/ar-SY/common.json — Syrian Arabic
{ "greeting": "أهلاً، {name}!" }
// locales/ar-SY-x-lattakia/common.json — Lattakia dialect: override ONLY what differs
{ "greeting": "أهلين، {name}!" }
Don't copy the whole file down the chain — the generator inherits.
Dialect-safe persistence
Persistence and cache ops are dialect-aware — wiping a base locale never wipes its dialects (a naive prefix match would treat ar as a prefix of ar-SY and delete them too):
Stift.pack.wipePersisted({ except: 'ar' }) // keeps ar-SY and ar-SY-x-lattakia packs
More pack lifecycle: Offline & Persistence.
Next
- Config
locales.supported/default→ stift.config.ts - Authoring rules (schema, orphans) → Messages & Keys
- Per-locale defaults for params and units inherit down this same chain → Translating & Values, Units