dialectYamlTemplate top-level constant
String
const dialectYamlTemplate
Implementation
const String dialectYamlTemplate = r'''# ============================================================
# Dialect — Localization Convention
# ============================================================
# This file configures Dialect AND teaches AI assistants how
# to work with this project's translations.
#
# If you are an AI assistant: read this entire file before
# extracting or translating any strings. It is the spec.
#
# === The project ===
# See the `project:` block at the bottom of this file.
# Read it first — it tells you what the app does, which
# disambiguates glossary terms (e.g. "Trip" = a booked
# travel stay, not a corporate business trip).
#
# === Where strings live ===
# - Canonical source strings live in dialect/source/en.arb
# (and any other `dialect/source/<source_locale>.arb` if you
# change `source_locale` below).
# - Translated strings live in dialect/translations/<locale>.arb,
# one file per target locale.
# - The CLI syncs filtered output to each platform's directory
# (e.g. Flutter's `lib/l10n/`) on `dialect sync`.
#
# === Key naming ===
# - Keys are flat camelCase identifiers that are valid Dart
# method names:
# checkoutBookNow, commonCancel, settingsDarkMode
# No dots, no dashes, no leading digit, no underscores in
# normal use. This shape is required by Flutter's
# `flutter gen-l10n` and matches Dart method-name rules,
# so every key in en.arb becomes
# `AppLocalizations.of(context)!.<key>` after sync.
# - Logical grouping lives in metadata, NOT in the key:
# "checkoutBookNow": "Book Now",
# "@checkoutBookNow": {
# "namespace": "checkout",
# "description": "..."
# }
# The `namespace` controls which keys sync to which platform
# (see `platforms.<p>.namespaces` below). Cross-platform
# adapters use it to group output (e.g. one .strings file
# per namespace on iOS).
# - Two screens that currently render the same English string
# get separate keys (e.g. `homeHostedBy` and
# `checkoutHostedBy`). Identical-today is a coincidence,
# not a guarantee. Only use the `common` namespace when a
# key is *logically* shared (cancel / save / loading / delete).
#
# === After editing, normalize with the CLI ===
# You do not need to remember sort order, formatting, or
# indentation rules. Add or edit entries in any reasonable
# shape, then run:
# dialect check --fix # normalizes + flags issues
# dialect sync # generates outputs, then reports
# # the state it left you in
# Two commands, not three: `sync` ends by re-checking and
# printing one line ("check: no issues." or a count), so the
# confirming pass is already done. `dialect sync --verify` also
# makes any remaining error the exit code, which is the whole
# CI gate in one command.
# `dialect check --fix` deterministically sorts keys, moves
# `@@locale` to the top, places each `@key` block after its
# own key, strips any `@key` blocks accidentally added to
# translation files, and validates placeholder/plural shape.
# It also warns (`output_drift`) when a generated file holds
# keys the source does not — the one condition that makes the
# `sync` in step two refuse — so step one tells you whether
# this repo is in a state where sync can run.
# Spend your effort on the SEMANTIC parts (good descriptions,
# good translations, glossary application). The CLI handles
# the rest.
#
# If that warning fires on a project that used to add keys
# straight to the generated files: that habit made sense when
# `sync` deleted them, and `dialect sync --adopt` is the
# one-time migration back. It recovers each key's English,
# its `@key` metadata, and any translation that lived only in
# the output, then regenerates.
#
# === Copy policy lives in glossary.yaml ===
# `terms:` says what a translation must always say; `banned:`
# says what no value may say (checked in the source too). Both
# are warnings, so `dialect check --strict` is what makes them
# a gate. See dialect/glossary.yaml.
# `plural_shape` warns when a count is interpolated straight in
# front of a plural noun ("{count} people" renders "1 people").
# Wrap it in an ICU plural in the SOURCE — every translation
# inherits the shape from there.
#
# === @key metadata (semantic part — your responsibility) ===
# - Every key in the SOURCE ARB MUST have a "namespace" and a
# "description" field in its matching `@key` entry.
# "namespace": which group it belongs to (see platforms below).
# "description": what the string means *in context*, not
# just what it literally says.
# Bad: "description": "Book now button"
# Good: "description": "CTA on the checkout screen.
# 'Book' is a verb meaning 'make a reservation',
# NOT a physical book."
# - Add "context": "<screen_or_feature>" when the same word
# might mean different things in different places.
# - For strings with variables, add "placeholders" describing
# each variable's type and meaning.
# - Metadata lives only in the SOURCE ARB. Don't repeat it
# in translation files; the CLI will strip it if you do.
#
# === Placeholders ===
# - Use ICU MessageFormat for interpolation: "Hello {userName}"
# - Declare each placeholder in @key.placeholders with at
# least a `type` (`String`, `int`, `double`, `DateTime`).
# - Use the SAME placeholder name in every translation. Do
# not translate the name itself. (`dialect check` will
# catch mismatches.)
#
# === Slot budgets (size-aware translation) ===
# Text expands when translated — "Edit profile" (12) becomes
# Vietnamese "Chỉnh sửa trang cá nhân" (23) — and a faithful
# but long value silently breaks a tight button or chip.
# - If a string renders in a CONSTRAINED slot, say so on its
# SOURCE `@key` block:
# "@editProfile": { "x-slot": "button" } # policy below
# "@statusChip": { "x-max-length": 10 } # hard cap
# Slot policies live once in the `slots:` block near the
# bottom of this file (`max_ratio` = "stay within N× the
# source"; `max_length` = an absolute character cap).
# - This is OPT-IN. A key with neither field is never checked,
# so body copy, legal text and empty-state prose keep all
# the room they need. Only tag genuinely tight slots.
# - Do NOT write the constraint as prose in `description`
# ("must stay short", "sits in a 68px column"). A
# description is a hope; a budget is a check. Prose there
# cannot be enforced and cannot be handed to a translator.
# - The payoff is up front, not after the fact: `dialect
# translate` puts the resolved budget in the work list, so
# the agent writes the short faithful form the first time —
# shorten by WORD CHOICE (drop context-implied words: on a
# profile header, "Edit profile" → "Edit"), never by
# truncating mid-word or dropping a glossary term.
# - `dialect check` also flags a source string that busts its
# own budget — the slot is too tight even in English, which
# no translation can fix. Widen the slot or shorten the
# source. Warnings here are soft (`--strict-length` makes
# them fail; they can be acked).
#
# === Plurals ===
# - Use ICU plural select:
# "{count, plural, =1{1 item} other{{count} items}}"
# - Cover the CLDR plural categories required by the target
# locale IN ADDITION TO any `=N` exact-match cases. The
# two are independent: ICU evaluates `=N` first and falls
# back to the matching CLDR category. You need BOTH —
# not one or the other.
# Common CLDR sets:
# English: one / other
# German: one / other
# Spanish: one / other
# Arabic: zero / one / two / few / many / other
# Japanese: other (single-form)
# Vietnamese: other (single-form)
# - Mirror any `=N` exact-match cases the source provides.
# Don't synthesize extra `=N` cases the source doesn't have.
# - Worked example. Source (English):
# "{count, plural, =0{No items} =1{1 item} other{{count} items}}"
# Correct Arabic translation:
# "{count, plural,
# =0{لا توجد عناصر} =1{عنصر واحد}
# zero{لا توجد عناصر} one{عنصر واحد} two{عنصران}
# few{{count} عناصر} many{{count} عنصرًا} other{{count} عنصر}}"
# Notice: the `=0`/`=1` mirrors stay, AND all six CLDR
# categories are present. Dropping the categories and
# keeping only `=0`/`=1`/`other` is WRONG — counts of
# 2, 3, 4, 11, 100 etc. will fall to `other` instead of
# the correct grammatical form.
# - The CLI (`dialect check`) flags missing CLDR categories
# as an error in `--strict`. Trust it; it will tell you
# which category you missed.
#
# === Currency, units, dates, numbers ===
# - Do not translate currency symbols, units, or numeric
# values themselves.
# - Match the position of the currency symbol used in the
# source string. Per-locale repositioning of `$` is a
# runtime number-formatting concern (use `intl`), not a
# translation concern.
#
# === Glossary ===
# - Before translating, read dialect/glossary.yaml.
# - The `term` in the glossary is the canonical lemma (e.g.
# "Book" as a verb). Use the appropriate inflection or
# derivation in the target language — "Booking confirmed"
# becomes Spanish `Reserva confirmada` (noun form), not
# `Reservar confirmado` (mechanical verb substitution).
# The `meaning` field tells you which sense applies.
# - If a glossary term appears in a non-literal sense in a
# specific key (e.g. "Book club" meaning a physical book),
# mark the SOURCE key with the term waived in its `@key`
# block, and leave a brief note in the description:
# "@bookClub": { "glossary_exempt": ["Book"] }
# Name the terms. `"glossary_exempt": true` also works but
# waives EVERY term on that key — including ones the string
# should still honor — and a diff showing `true` tells a
# reviewer nothing about what was actually waived. One
# string may need two terms waived for two different and
# both-correct reasons; list them.
#
# === What NOT to extract ===
# The following are NOT user-facing copy and should remain
# hardcoded in source. Do not add them to ARB files.
# - Personal names (e.g. "Linh Nguyen" sample profile data)
# - Email addresses, phone numbers, URLs
# - Currency amounts ("82", "$246") — these are data,
# passed in as placeholders
# - Dates and times — formatted at runtime with `intl`
# - Language self-names in a language picker
# (the Spanish UI still says "Español" in its picker, so
# these names live in a per-locale data table, not in
# translation files)
# - Brand/product names you would not translate verbally
# - Demo or placeholder content that exists only to make
# the app look populated (sample listings, lorem ipsum)
# - LONG-FORM DOCUMENTS: privacy policies, terms of service,
# community guidelines, licences, changelogs. A document is
# a document, not a string catalogue. Shredding one into
# keys gives you meaningless key names, unreadable diffs,
# and clauses that drift apart between locales — and in a
# legal document a drifted clause is worse than no
# translation at all. Keep them as per-locale files
# (Markdown, MDX, one template per language), translate them
# as documents, and state on the page which language
# governs. Extract the CHROME around a document (nav labels,
# an "available in English only" notice); leave the body of
# it alone.
# If in doubt: ask whether the string would change for a
# different user. If yes, it's data, not copy. Then ask whether
# it is a sentence in a UI or a section of a document. If it's a
# document, it doesn't belong here either.
#
# === Workflow you should follow ===
# 1. Read dialect/dialect.yaml (this file) and dialect/glossary.yaml.
# 2. Read dialect/source/en.arb to see what keys already exist
# and the style/length of existing descriptions.
# 3. For extraction tasks: find user-facing strings in the
# source code that are not in the "What NOT to extract"
# list. For each, propose a flat camelCase key, assign a
# `namespace`, and add the key + `@key` block to
# dialect/source/en.arb. Replace the hardcoded string in
# source code with `AppLocalizations.of(context)!.<key>`
# (Flutter) or the platform's equivalent. Do NOT overwrite
# keys that already exist — they are the product of
# earlier decisions.
# 4. For translation tasks: for every key in dialect/source/en.arb,
# ensure a corresponding entry exists in each
# dialect/translations/<locale>.arb. Keep the same key name;
# translate only the value. Preserve placeholders and ICU
# plural structure exactly. Respect the glossary.
# Tip: `dialect translate` writes `.dialect/translate-plan.md` with
# a per-locale work list of exactly which keys are missing (and
# which locked translations went stale) — execute that plan.
# 5. Run `dialect check --fix && dialect sync && dialect check`
# to normalize, generate platform output, and validate.
#
# === Things you must NOT do ===
# - Do not rename existing keys without being asked.
# - Do not change source code to reference keys that don't exist.
# - Do not invent new @key fields outside this convention.
# - Do not translate the source locale itself.
# - Do not delete keys you don't recognize.
# - Do not mirror @key metadata into translation ARB files.
# - Do not use dotted keys (`checkout.bookNow`). They break
# `flutter gen-l10n`. Use `@key.namespace` metadata instead.
#
# ============================================================
project:
name: "Your project"
description: >
One-line description of what your app does. Translators
and AI assistants read this to disambiguate glossary terms.
# The oldest Dialect the conventions in this file rely on. `dialect init`
# stamps the version it ran as; `dialect check` fails when the binary on
# PATH is older, because a stale binary can silently do the wrong thing
# (pre-1.2 `sync` deleted keys that lived only in generated output). Raise
# this when you adopt a feature that needs a newer release — that is the
# one line that makes the requirement enforceable instead of folklore.
# Pre-release suffixes are ignored: 1.2.0-dev satisfies `1.2.0`.
toolchain:
min_version: {{DIALECT_VERSION}}
source_locale: en
target_locales: [] # add the locales you ship in, e.g. [es, fr, ja]
platforms:
flutter:
output: lib/l10n/
format: arb
namespaces: [common] # add more as your project grows
# Cross-stack sync: emit the SAME source to a backend service, in JSON.
# Uncomment and adjust to keep your Flutter app and backend in sync from
# one canonical source. Two formats:
# icu-json — preserves ICU plurals/select (backend has an ICU runtime)
# flat-json — collapses plurals to the `other` branch (no ICU runtime)
# Only keys whose @key.namespace is listed here sync to this platform.
# backend:
# output: api/locales/
# format: icu-json
# namespaces: [common, backend]
# Per-locale overrides for the length-ratio check.
# Each value is the [min, max] multiplier of source character length.
# Default for locales not listed: [0.3, 2.5].
length_ratio: {}
# Size-aware translation: budgets for tight, fixed-width UI slots.
# A key OPTS IN by tagging its SOURCE @key block with `x-slot: <name>` (a
# preset below) or a hard `x-max-length: <n>`. Keys with neither are never
# checked — body copy and anything that auto-sizes keeps all its room.
# max_ratio — stay within N× the source ("similar length"), floored at
# source+grace so short labels never false-trip.
# max_length — an absolute character cap (a real pixel-bounded slot).
# grace — optional; extra characters a ratio always allows (default 4).
# Soft by design: plain --strict leaves these warnings; --strict-length makes
# them fail. The real payoff is `dialect translate`, which hands the budget to
# the agent so the short faithful form is written up front.
# See the "Slot budgets" section in the header above.
slots: {}
# slots:
# button: { max_ratio: 1.4 } # "Edit profile" → "Chỉnh sửa", not the literal
# chip: { max_length: 10 }
# tab: { max_ratio: 1.2, grace: 2 }
# Publish immutable translation bundles for a backend to consume (v1.2).
# `dialect publish <env>` builds a content-hashed bundle and uploads it;
# `dialect pull <env>` fetches it (verifying integrity) in a deploy script.
# `target: local` writes to the filesystem; `s3` (R2/MinIO/AWS) is coming.
# publish:
# staging:
# target: local
# path: dist/locales/staging/ # where the bundle is written
# format: icu-json # or flat-json
# namespaces: [common, backend]
# output: api/locales/ # where `dialect pull` writes the files
''';