106 lines
3.8 KiB
Plaintext
106 lines
3.8 KiB
Plaintext
-- allium: 1
|
|
-- bDS Internationalization
|
|
-- Scope: core (Wave 0 onward — split localization is mandatory)
|
|
-- Distilled from: src/main/shared/i18n.ts, i18n/locales/*.json
|
|
|
|
value SupportedLanguage {
|
|
code: String
|
|
-- en, de, fr, it, es
|
|
flag: String
|
|
-- en=GB, de=DE, fr=FR, it=IT, es=ES
|
|
}
|
|
|
|
config {
|
|
supported_languages: Set<SupportedLanguage> = {
|
|
SupportedLanguage(code: "en", flag: "GB"),
|
|
SupportedLanguage(code: "de", flag: "DE"),
|
|
SupportedLanguage(code: "fr", flag: "FR"),
|
|
SupportedLanguage(code: "it", flag: "IT"),
|
|
SupportedLanguage(code: "es", flag: "ES")
|
|
}
|
|
default_language: String = "en"
|
|
}
|
|
|
|
-- ─── Split localization ─────────────────────────────────────
|
|
--
|
|
-- Two independent locale scopes exist per running app. They are
|
|
-- resolved by separate rules from separate inputs and may differ.
|
|
|
|
entity LocaleScopes {
|
|
-- Singleton holding the two locale scopes.
|
|
ui_locale: String
|
|
-- Follows the OS system locale; used for menus and app chrome
|
|
render_locale: String
|
|
-- Follows the active project's mainLanguage; used for
|
|
-- template rendering and generated content
|
|
}
|
|
|
|
surface LocaleSurface {
|
|
facing _: LocaleConsumer
|
|
|
|
provides:
|
|
OsLocaleDetected(os_locale)
|
|
ProjectMainLanguageResolved(main_language)
|
|
MenuTranslationRequested(item_key)
|
|
RenderTranslationRequested(label_key)
|
|
}
|
|
|
|
-- ─── Language normalization ─────────────────────────────────
|
|
--
|
|
-- Both scopes normalize their input the same way: take the base
|
|
-- language code, fall back to the default when unsupported.
|
|
|
|
rule NormalizeUiLocale {
|
|
when: OsLocaleDetected(os_locale)
|
|
let base = base_language_code(os_locale)
|
|
-- black box: substring of the input before the first "-"
|
|
-- ("en-US" -> "en")
|
|
let codes = config.supported_languages -> code
|
|
ensures:
|
|
if base in codes:
|
|
LocaleScopes.ui_locale = base
|
|
else:
|
|
LocaleScopes.ui_locale = config.default_language
|
|
}
|
|
|
|
rule NormalizeRenderLocale {
|
|
when: ProjectMainLanguageResolved(main_language)
|
|
let base = base_language_code(main_language)
|
|
let codes = config.supported_languages -> code
|
|
ensures:
|
|
if base in codes:
|
|
LocaleScopes.render_locale = base
|
|
else:
|
|
LocaleScopes.render_locale = config.default_language
|
|
}
|
|
|
|
-- ─── Translation lookup scopes ──────────────────────────────
|
|
|
|
rule TranslateMenuLabel {
|
|
when: MenuTranslationRequested(item_key)
|
|
-- Menus follow the OS convention: labels match the system
|
|
-- language (UI locale), never the render locale.
|
|
ensures: MenuLabelTranslated(item_key, locale: LocaleScopes.ui_locale)
|
|
}
|
|
|
|
rule TranslateRenderLabel {
|
|
when: RenderTranslationRequested(label_key)
|
|
-- Template rendering i18n strings (date formats, archive labels,
|
|
-- "older posts", "newer posts", etc.) come from the locale JSON
|
|
-- files for the render locale.
|
|
ensures: RenderLabelTranslated(label_key, locale: LocaleScopes.render_locale)
|
|
}
|
|
|
|
-- ─── Search stemmer coverage ────────────────────────────────
|
|
|
|
invariant SnowballStemmerCoverage {
|
|
-- ISO 639-1 codes with a dedicated Snowball algorithm
|
|
-- (stemmer_for_language in bds-core fts.rs); nb/nn/no share the
|
|
-- Norwegian stemmer, unknown codes fall back to English.
|
|
-- Every UI language must have real stemming, not the fallback.
|
|
let stemmed_codes = {"ar", "da", "de", "el", "en", "es", "fi", "fr",
|
|
"hu", "it", "nb", "nn", "no", "nl", "pt", "ro", "ru", "sv", "ta", "tr"}
|
|
for lang in config.supported_languages:
|
|
lang.code in stemmed_codes
|
|
}
|