Тема и локализация в REEEL (
$theme
/
$strings
)
REEEL даёт два
ambient-объекта
, доступных в любом файле проекта
без объявления и без
JsonData
/
import
— ровно как
Theme.of(context)
/
Localizations.of(context)
во Flutter:
-
$theme— токены оформления (цвета, при необходимости — типографика и пр.); -
$strings— строки локализации активного языка.
Читаются они через
доступ к членам по точке
(
$theme.primary
,
$strings.welcome
), а действия (смена темы/языка) — через
вызов методов-членов
(
$theme.set("dark")
,
$strings.set("ru")
,
$strings.t("key", …)
).
ColoredBox color:$theme.backgroundPage
Center
Column mainAxisAlignment:center spacing:16
Text $strings.welcome
#style color:$theme.textPrimary fontSize:28 fontWeight:w700
Text {strings.t("greeting", name:"REEEL")}
#style color:$theme.primary
Row mainAxisAlignment:center spacing:8
FilledButton onPressed:(){theme.set("dark")}
Text $strings.themeDark
TextButton onPressed:(){strings.set("ru")}
Text "RU"
Внутри{…}имена пишутся без$(как у всех переменных в выражениях):{theme.primary},{strings.t("k")}. В позиции значения — с$:color:$theme.primary.
$theme
— тема и цвета
$theme.<token>
возвращает значение токена активного режима. Имена токенов — плоские (
primary
,
backgroundPage
,
backgroundSurface
,
textPrimary
,
textMuted
,
onPrimary
,
accent
, …). Цвет приходит строкой (
#AARRGGBB
) и резолвится обычным механизмом цвета, поэтому слот цвета принимает
$theme.primary
или
литерал
#hex
одинаково.
Встроенные члены:
| Член | Значение |
|---|---|
$theme.<token>
|
значение токена (цвет/число/строка) |
$theme.mode
|
текущий режим (
"light"
/
"dark"
/ своё имя схемы)
|
$theme.set("dark")
|
переключить режим (вызывается из колбэка) |
Откуда берутся токены (слои)
Активная карта
$theme
собирается слоями,
позже — важнее
:
- База хоста — дефолтные токены (в редакторе — дизайн-токены редактора), чтобы экран без собственной темы выглядел осмысленно.
-
Тема проекта
— файлы
data/theme/<mode>.json(см. ниже): переопределяют только заданные в них токены; остальное берётся из базы.
// data/theme/dark.json — переопределяет часть токенов
{ "backgroundPage": "#FF1A1110", "textPrimary": "#FFF5EDE6", "primary": "#FFD9A066" }
Переключение
onPressed:(){ theme.set("light") }
— сменить режим. Выбор
сохраняется
(Storage) и восстанавливается при следующем запуске; одно значение синхронно во всех файлах.
Системная тема (
system
)
По умолчанию выбор темы —
system
: без своего переключателя проект следует за светлой/тёмной настройкой ОС (
$theme.mode == "system"
, эффективная яркость —
$theme.brightness
).
theme.set("dark")
/
"light"
фиксирует режим,
theme.set("system")
возвращает следование за ОС. В
редакторе
превью симулирует системную настройку тогглом
☀️/☾
рядом с тогглом ориентации; на
устройстве
берётся реальная
platformBrightness
.
$strings
— локализация
$strings.<key>
— строка активного языка. С аргументами/плюралом —
$strings.t(...)
.
| Член | Значение |
|---|---|
$strings.<key>
|
строка по ключу (или сам ключ, если не найдена) |
$strings.locale
|
текущая локаль (
"en"
/
"ru"
/ …)
|
$strings.set("ru")
|
сменить язык (из колбэка) |
$strings.t("key", name:…, 0:…)
|
строка + подстановка
{name}
/
{0}
|
Text $strings.welcome // простой ключ
Text {strings.t("greeting", name:"REEEL")} // подстановка {name}
Text {strings.t("items", "5")} // подстановка {0}
Text {plural($n, one:$strings.itemOne, other:$strings.itemMany)} // плюрал
Языки
обнаруживаются в runtime
из
data/strings/
проекта (никакого хардкода списка локалей). Выбор локали сохраняется в Storage и синхронен между файлами.
Системный язык (
system
)
По умолчанию выбор локали —
system
: без своего переключателя проект берёт язык по
приоритету предпочитаемых локалей ОС
среди доступных (
$strings.locale == "system"
, эффективная —
$strings.effectiveLocale
); если ни одна не подходит — фолбэк
locales.default
из
project.json
, иначе первая доступная.
strings.set("ru")
фиксирует язык,
strings.set("system")
возвращает следование за системой. В
редакторе
превью симулирует системный язык
селектором
рядом с тогглом темы; на
устройстве
берётся реальная локаль ОС.
Системный язык на вебе
Правило то же, меняется только его форма: у браузера нет «локалей ОС», он присылает их заголовком
Accept-Language
с весами
q
. Сервер выбирает лучшую из доступных по этому приоритету; не подошла ни одна —
locales.default
конфигурации, иначе первая доступная.
Отличий от устройства два, и оба следуют из того, что у страницы есть адрес.
Выбор живёт в URL, а не в Storage.
Каждая локаль имеет свой префикс, включая локаль по умолчанию:
/en/about
,
/ru/about
. Префиксованный адрес — это уже сделанный выбор, и согласование его не перебивает.
strings.set("ru")
уводит на
/ru/…
, и все ссылки, которые страница порождает, остаются внутри её локали. Поэтому согласование происходит
один раз
, на входе: первый же клик закрепляет посетителя на явном адресе.
Непрефиксованный адрес — точка входа.
/about
— не английская страница, а та, которая выбирает язык по заголовку. Она же объявлена как
x-default
, а
canonical
ведёт на однозначный
/<locale>/about
. Ответ несёт
Content-Language
и
Vary: Accept-Language
, а его
ETag
включает локаль — иначе общий кэш отдал бы одному посетителю страницу на языке другого.
Статический экспорт
согласовывать не может: сервера нет, читать заголовок некому. Он раскладывает по файлу на локаль (
/en/about
,
/ru/about
) и кладёт умолчание ещё и в корень. Клиентского редиректа по
navigator.language
в экспорте нет намеренно — клиентской локализации в REEEL нет вообще, и заводить её в одном месте значило бы развести экспорт со всеми остальными способами отдачи.
Файлы проекта:
data/theme/
и
data/strings/
Темы и переводы живут под каталогом
data/
проекта (рядом с
data/<...>.json
для
JsonData
):
<project>/
data/
theme/
light.json # токены светлой темы → $theme.* (mode "light")
dark.json # токены тёмной темы → $theme.* (mode "dark")
strings/
en.arb # строки en → $strings.* (locale "en")
ru.arb # строки ru → $strings.* (locale "ru")
reeel/
main.reeel
project.json
Правила обнаружения (выполняются хостом/превью при открытии проекта):
-
Тема:
каждый
data/theme/*.json→ режим по имени файла (dark.json→dark). -
Локали:
каждый
data/strings/*.arbили*.json→ локаль по имени файла (en.arb→en,app_ru.json→ru; берётся часть после последнего_, иначе всё имя). -
ARB — это JSON; служебные ключи ARB (
@@locale,@key) безвредны (не конфликтуют с обычными ключами).
Формат значений:
// data/theme/light.json — токен → цвет (#AARRGGBB или #RRGGBB)
{ "backgroundPage": "#FFFDF6EC", "textPrimary": "#FF2A1A12", "primary": "#FFB5651D" }
// data/strings/en.arb — ключ → строка (с плейсхолдерами {name} / {0})
{ "welcome": "Hello, REEEL", "greeting": "Welcome, {name}" }
Реактивность
$theme
/
$strings
реактивны:
theme.set(...)
/
strings.set(...)
(или загрузка файлов) обновляют ambient-значение, и все элементы, читающие
$theme.*
/
$strings.*
, перестраиваются автоматически — через тот же механизм подписки, что и обычные
@
-переменные.
Лучшие практики
-
Цвета — токенами, не hex:
color:$theme.primary, неcolor:#9B8AFB. Так экран следует теме проекта/бренда и переключается. -
Строки — ключами, не литералами:
Text $strings.welcome, неText "Welcome". -
Подстраховка
??: dotted-чтение неизвестного члена даётnull; в выражении прикрывайте —{theme.primary ?? "#000000"},{strings.title ?? "—"}. (Text $strings.xпри отсутствии строки деградирует в пустую строку, не падает.) -
Не объявляйте
@theme/@stringsсвоими переменными — затенят ambient.