Repository navigation
v0.7.0
Added
-
Sequenced realtime frames. A
Logicthat also implementsrealtime.Framerframes every payload admitted to each member of its room with a per-connection sequence: 1 for the join snapshot, then 2, 3 and so on, restarting at 1 on reconnect. The host owns the wire format.Joinfails withrealtime.ErrEmptyFrame(or the wrappedFrameerror) when the framed snapshot is empty or fails, and the failure does not close the existing connection or admit the new peer. Admitted sends fromSnapshotkeep their sequence and are not rolled back; a send whoseFrameerrors or overflows the queue closes the existing connection, which stays closed. ALogicthat does not implementFrameris unchanged. See the realtime guide. -
Google Cloud Storage adapter (experimental, separate module).
storage/gcsis astorage.Storeon Google Cloud Storage, in its own Go module so no cloud SDK reaches a project that does not import it.gcs.Newauthenticates with application-default credentials, workload identity or explicit credentials, andgcs.NewFromClienttakes a client the host built. Writes are create-only and atomic, and a retried write that already committed is confirmed by a per-write token, checksum and size rather than reported as a collision. Experimental: tested against an in-process emulator, not yet against a real bucket. The module is versioned and tagged separately asstorage/gcs/vA.B.C, after the rootv0.7.0tag it requires. See the uploads guide. -
Admin bulk delete.
admin.Options.BulkDeletelets a staff account delete the checked rows of one changelist page after a confirmation dialog. The delete is one all-or-nothing transaction that cascades like a single-row delete, an invalid selection (empty, over a page, a wrongcount, a repeated or non-canonical key) is rejected whole, and each committed delete logs oneadmin.EventBulkDelete(tango.admin.bulk_delete) event withmodel,selected_count(the rows selected, not the database rows a cascade deleted) andusername. It is off by default, needs JavaScript and never spans more than a page. See admin registration. -
Grouped filters.
Query.Filteris an optional boolean tree ANDed withWhereandAny, built withdb.Cond,db.And,db.Oranddb.Not(theExprandExprOptypes). AQuerywithout aFilterproduces exactly the SQL it did before. A filter holds at mostMaxFilterNodes(100) nodes andMaxFilterDepth(8) levels. -
IN,IS NULLandIS NOT NULL.OpInwithdb.In(field, slice)(a nil or empty list matches nothing on every dialect; at mostMaxInValues, 1,000, values per list andMaxQueryArgs, 10,000, bind values per query; a zerotime.Timeis rejected), andOpIsNull/OpIsNotNullwithdb.IsNull/db.IsNotNull, which take no value. All three work inWhere,AnyandFilter. A foreign key left at zero is stored asNULL, soOwnerID = 0still never matches (unchanged); useIsNullfor unset keys. -
Store.FirstandStore.Exists.Firstreads one row (primary-key order unlessOrderByis set,ErrNotFoundnaming the model when none, the destination untouched on any error);Existsreports whether any row matches. They validate and build SQL exactly asListandCountdo. -
Typed query errors.
ErrInvalidQuery,ErrQueryLimit,*QueryErrorand*QueryLimitError. Every malformed query is refused before any SQL runs. The existingWhere/Anyvalidation errors keep their text and now also matchErrInvalidQuery. -
Locale resolvers.
i18n.Resolver(a locale source that also names the request headers it depends on), the helpersFromQuery,FromCookieandFromHost,Chain(the host's order, thenAccept-Language),FuncResolverfor a plain function, andMiddlewareResolverto install one. Each helper returns only a locale with a registered catalog and none sets a cookie.MiddlewareandDefaultLocaleResolverare unchanged. -
i18n/i18ntest.Isolate. Test support:i18ntest.Isolate(t)gives a test an empty i18n state (catalogs, theOnMissinghook and its once-state, the observed keys) and restores the previous state int.Cleanup. A test that uses it must not be parallel. -
i18n.Keysandi18n.Validate.Trecords each (key, fallback) pair the first time it runs with a resolved locale (nothing is recorded, and nothing allocated, when no locale is set).Keys()lists them;Validate()checks every catalog against them and returns aReportwithProblems(ProblemMismatch,ProblemEmpty,ProblemConflictingFallbackwhen a key is seen with two fallbacks,ProblemRegistryFull) and the advisory, sortedUnobservedcatalog keys. It covers only what has run in this process, and the registry holds at most 4,096 keys, so fallbacks must be stable literals. -
i18n.OnMissing. A development hook that reports a missing translation (an absent key, an empty value or a value whose format verbs do not match the fallback's) with the resolved locale and key, at most once per pair. It isnilby default, runs outside the package's locks, andOnMissing(nil)clears it. -
The admin's remaining English is translatable.
<html lang>follows the selected catalog on every admin page and on the Markdown preview document (enwhen none is selected). The Markdown and JSON widgets' tab labels, buttons, character count,aria-labels, iframe title, script messages (Nothing to format.,Preview failed.and the rest, delivered asdata-msg-*attributes only when a locale was resolved), and the JSON validation errors now useadmin.*keys with the current English as fallback. An app without i18n renders the same output as before. A test keeps new literal text out of admin's templates. -
Translatable accounts pages. The register, login, password-reset and email-verification pages and their form errors (including the generic login error, which stays one message for every failure) go through
i18n.Tunderaccounts.*keys with the current English as fallback. A page declares<html lang>only when a catalog was selected, so an app without i18n renders exactly what it did before. The JSON API'serrortext stays English; clients branch oncode. -
Accounts emails in the request's language. The verification and reset emails are rendered in the locale of the request that queued them (
accounts.email.*keys, English fallback). The locale is captured when the email is queued, before any account lookup; the queued job keeps nothing else of the request. A translated body that lacks the%slink placeholder falls back to the English body, so the link is never dropped. -
The
tango newprojectscaffold points at the i18n guide. The generated middleware list carries a two-line comment saying how to opt in (i18n.Middleware(nil)and catalogs). The generated project is otherwise unchanged. -
examples/api-with-adminin Spanish. The example serves the admin in Spanish end to end: a completeescatalog, a smalles-MXoverride, a?lang=→ cookie →Accept-Languageorder and a language switcher, with a test that walks the pages in both locales and checksOnMissingandValidate. tanGO itself still ships no catalogs. -
examples/accounts-mailin Spanish. Registering, confirming an address and resetting a password run in Spanish, emails and links included, with a test that walks the flow and fails on any missing key.
Changed
-
The i18n middleware sets
VaryandContent-Language. Withi18n.Middleware(nil)every response getsVary: Accept-Language, so a cache in front of the app no longer serves one language to another visitor;MiddlewareResolver(Chain(...))sends the tokens its resolvers need (CookieforFromCookie).Content-Languagenames the selected catalog's locale, and only when a catalog was selected. ExistingVaryvalues, including ones a downstream handler adds, are merged without repeating a token (ignoring case). A resolver passed as a plain function adds noVary(useFuncResolverto declare one). Nothing changes for an app without the middleware. -
A bad catalog value falls back instead of printing garbage.
i18n.Tnow ignores a catalog value that is empty or whose format verbs differ from the fallback's (positional verbs such as%[2]scount, so reordering arguments is still fine), tries the base language, and then shows the fallback. Before,"Nuevo"for"New %s"renderedNuevo%!(EXTRA string=Pet)and an empty value rendered nothing. A literal%in a fallback must be%%once arguments are passed. -
Upgrade note: literal
%in i18n fallbacks. The fallback is a format string whenever arguments are passed, so a bare%is read as a format verb and must be escaped. Changei18n.T(ctx, "sale.label", "Save 10% on %s", item)toi18n.T(ctx, "sale.label", "Save 10%% on %s", item). Search the project'si18n.Tcalls and catalogs for%followed by a letter or space before relying on the new fallback rule.
Fixed
- Locale matching ignores case and the
-/_separator.Accept-Language: es-mx,ES-MXandes_MXnow select a catalog registered ases-MX(before,es-mxskipped the regional catalog andES-MXmatched nothing, not evenes). The locale the middleware stores is the catalog's own spelling, and catalogs registered under two spellings of one locale are merged.