@yuneta/gobj-ui 7.26.31 → 7.26.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -95,8 +95,8 @@ consumers. They are independent snapshots (no shared git ancestry):
95
95
 
96
96
  | Line | Branch | Latest tag | Layout | Consumed by | How | Status |
97
97
  |------|--------|------------|--------|-------------|-----|--------|
98
- | **v2** | `main` | `7.26.x` | `src/` subdir | **gui_agent**, **gui_treedb**, **wattyzer**, the **yunovatios** GUIs | published npm `@yuneta/gobj-ui` (dist-tag `latest`) | active development |
99
- | **v1** | `v1` | `1.0.4` | `src/` subdir | **estadodelaire**, **hidraulia** | published npm `@yuneta/gobj-ui@^1.0.4` (dist-tag `legacy`) | frozen, maintenance-only |
98
+ | **v2** | `main` | `7.26.x` | `src/` subdir | **gui_agent**, **gui_treedb**, **wattyzer**, the **yunovatios** GUIs, **estadodelaire** (since 2026-10-13) | published npm `@yuneta/gobj-ui` (dist-tag `latest`) | active development |
99
+ | **v1** | `v1` | `1.0.4` | `src/` subdir | **hidraulia** | published npm `@yuneta/gobj-ui@^1.0.4` (dist-tag `legacy`) | frozen, maintenance-only |
100
100
 
101
101
  - **v2 / `main`** is the active development line: the declarative shell
102
102
  (legacy-stack-free since `3.0.0`). It is embedded as a git submodule in
@@ -109,9 +109,10 @@ consumers. They are independent snapshots (no shared git ancestry):
109
109
  (`npm run deploy-round` does the round). `test-app/` is the one exception:
110
110
  it consumes the working tree (`file:..`), which is what it is for.
111
111
  - **v1 / `v1`** is the frozen legacy-only stack (the declarative shell is not on
112
- this line). It is **published to npm**; estadodelaire and hidraulia depend on
113
- `@yuneta/gobj-ui@^1.0.4` from the registry. Land only maintenance fixes here,
114
- then `npm publish` a new `1.x`.
112
+ this line). It is **published to npm**; hidraulia depends on
113
+ `@yuneta/gobj-ui@^1.0.4` from the registry (estadodelaire left it for v2 on
114
+ 2026-10-13, and hidraulia is next). Land only maintenance fixes here, then
115
+ `npm publish` a new `1.x`.
115
116
 
116
117
  All new feature work lands on `main`/v2.
117
118
 
@@ -3835,28 +3836,57 @@ text is a key, `"configuración"` included.
3835
3836
  dependency of this package since `7.26.29`: an app need not declare it). A
3836
3837
  LOCALE is not a source: it is parsed, and then imported, so its top-level code
3837
3838
  runs -- the app's own code, but code. Comments are not code. A key is read
3838
- where it is WRITTEN -- given to `t()`, as an `i18n` or `data-i18n*` value of
3839
- an object literal, by `setAttribute()`, or (since `7.26.29`) written as a
3840
- member, `attrs.i18n = "save"`, `attrs["data-i18n-title"] = "save"` -- and
3841
- FOLLOWED where the source writes it out:
3839
+ where it is WRITTEN -- given to a translator, as an `i18n` or `data-i18n*`
3840
+ value of an object literal, by `setAttribute()`, or (since `7.26.29`) written
3841
+ as a member, `attrs.i18n = "save"`, `attrs["data-i18n-title"] = "save"` -- and
3842
+ FOLLOWED where the source writes it out.
3843
+
3844
+ A **translator** is one notion, the same everywhere the reader asks "is this a
3845
+ call of t()?" (since `7.26.32`; up to `7.26.31` it was a call NAMED `t` or
3846
+ `tr`): a name `t` or `tr` (whatever binds it), a member `.t` / `.tr`
3847
+ (`i18next.t`), i18next's `t` imported under any name (`import {t as tt} from
3848
+ "i18next"`), or an alias of one -- a const (`const translate = t`), a
3849
+ `.bind()` (`i18next.t.bind(i18next)`), a re-export of a module the reader
3850
+ reads --, called as `t(k)`, `t?.(k)`, `t.call(x, k)` or `t.apply(x, [k])`. An
3851
+ ARRAY given to it is i18next's list of fallback keys, and each one is a key:
3852
+ `t([specific, "generic"])` asks for "generic". The places, since `7.26.32`, also
3853
+ include `el.dataset.i18nTitle = k` (`dataset.i18nX` IS the attribute
3854
+ `data-i18n-x`), and an attribute or property NAMED by a const:
3855
+ `el.setAttribute(ATTR, k)`, `{[ATTR]: k}`, `x[ATTR] = k`, with `const ATTR =
3856
+ "data-i18n-title"`. What is followed:
3842
3857
 
3843
3858
  - a string literal, a template with no `${}`, and every branch of `?:`, `||`
3844
3859
  and `??` (the right one of `&&`), so
3845
3860
  `"data-i18n": pending ? "loading" : "click to load"` asks for both, and
3846
3861
  `comment || "print-tranger failed"` for its fallback;
3847
3862
  - a `const`, to its value, in the scope that declares it (a parameter or a
3848
- `let` of the same name hides it);
3863
+ `let` of the same name hides it), and what a destructuring reads from it
3864
+ (since `7.26.32`: `const {title = "untitled"} = LABELS`);
3865
+ - an IMPORT of a const or of a function, into the module it names, when the
3866
+ reader reads that module -- a file of the app, a module of this library;
3867
+ re-exports are followed (since `7.26.32`). A package (gobj-js, i18next) is
3868
+ not read, and neither is the export of a `let`;
3849
3869
  - a property of a const object literal: `DEFAULT_LABEL.ok`, and for a
3850
- computed one, `NOTES[verdict]`, every value the object holds;
3870
+ computed one, `NOTES[verdict]`, every value the object holds; also of a
3871
+ COPY of one (since `7.26.32`): `{...BASE, title: "t"}`,
3872
+ `Object.assign({}, BASE)`;
3851
3873
  - the element of a const array literal that a `.map()`, `.forEach()`, ...
3852
3874
  callback or a `for...of` walks, and what a destructuring reads from it
3853
3875
  (`for(const {key} of ROWS)`);
3854
3876
  - `String(x)`, and a template that is only `${x}`, as `x` (since `7.26.29`);
3855
- - the default of a parameter, `function f(msg = "fallback")`, besides the
3856
- parameter (since `7.26.29`);
3857
- - a call of a function of the same module whose every `return` is one of
3858
- these (since `7.26.29`). One that returns anything else -- a parameter, a
3859
- variable, a concatenation -- is not followed.
3877
+ - a parameter, however it is read (since `7.26.32`): `opts.title`,
3878
+ `opts?.title`, `arguments[1]`, `rest[0]` of a rest parameter, a destructuring
3879
+ in the parameter or in the body; and its default, `function f(msg =
3880
+ "fallback")`, besides the parameter (since `7.26.29`);
3881
+ - a call of a function whose every `return` is one of these (since
3882
+ `7.26.29`): of the same module, or (since `7.26.32`) imported from a module
3883
+ the reader reads, called by `.call()` / `.apply()` too. One that returns
3884
+ anything else -- a parameter, a variable, a concatenation -- is not
3885
+ followed. What a function returns is read ONCE, and a RECURSION is not
3886
+ followed: a call of a function that is being read is a value the reader
3887
+ cannot follow. Up to `7.26.31` a function was read again at every call of
3888
+ it, so a helper that calls itself in two branches (`label_of(n)` walking
3889
+ up a tree) took more than two minutes, and in three it never ended.
3860
3890
 
3861
3891
  ```js
3862
3892
  const KEY = "save";
@@ -3873,12 +3903,12 @@ function pick(ok) {
3873
3903
  t(pick(answer)); // asks for both
3874
3904
  ```
3875
3905
 
3876
- What it cannot follow cannot be known: a `let`, a parameter, an import, a
3877
- call, a concatenation, data from a backend. A key that arrives that way is
3878
- PUBLISHED by its module (below), and the place it is read is MARKED; a marked
3879
- `let` -- or a marked member or element of one -- is the one exception to "not
3880
- followed" (below). A source file that cannot be parsed fails the build, because
3881
- its keys would go unchecked.
3906
+ What it cannot follow cannot be known: a `let`, a parameter of an app's
3907
+ function, a call it does not read, a concatenation, data from a backend. A key
3908
+ that arrives that way is PUBLISHED by its module (below), and the place it is
3909
+ read is MARKED; a marked `let` -- or a marked member or element of one -- is the
3910
+ one exception to "not followed" (below). A source file that cannot be parsed
3911
+ fails the build, because its keys would go unchecked.
3882
3912
 
3883
3913
  **A key handed to a FUNCTION is read where it is handed.**
3884
3914
  `yui_shell_show_error(shell, "some records were gone before the delete")` asks
@@ -3900,10 +3930,16 @@ A position is an argument (`"1"`), a property of the object given there
3900
3930
  (`"2.title"`, also through `Object.assign()` or a spread of a const object),
3901
3931
  or a property of each object of an array (`"1.actions[].label"`). What is
3902
3932
  given at those positions is read like any other key (literals, consts,
3903
- branches): in the app's own code, on a call bound to an import of that
3904
- function from gobj-ui (an app function of the same name is not mistaken for
3905
- it), and in every module the app mounts, where an entry with
3906
- `exported: false` is a helper of its own module. A position may also hold
3933
+ branches): in the app's own code, on a call that reaches that function of
3934
+ gobj-ui (an app function of the same name is not mistaken for it), and in
3935
+ every module the app mounts, where an entry with `exported: false` is a
3936
+ helper of its own module. A call reaches it however it names it (since
3937
+ `7.26.32`; up to `7.26.31` only an import by its own name, or a namespace
3938
+ member): an import under any name, a re-export from a file of the app
3939
+ (`export {yui_shell_show_info as say} from "@yuneta/gobj-ui/src/shell_modals.js"`),
3940
+ a const alias (`const show = yui_shell_show_info`), a map in a const
3941
+ (`const API = {info: yui_shell_show_info}; API.info(...)`, imported too),
3942
+ `.call(x, ...)` and `.apply(x, [...])`. A position may also hold
3907
3943
  DOM, which is no key (since `7.26.29`): an array (a `createElement2` spec), a
3908
3944
  call of `createElement2()`, `createOneHtml()` or `html_markup()` imported
3909
3945
  from gobj-js, of `text_node()` from gobj-ui, of `document.createElement()`
@@ -3924,12 +3960,14 @@ gclass name, which titles it).
3924
3960
  (since `7.26.29`: up to `7.26.28` the table lacked 39 of them, and 6 entries
3925
3961
  lacked positions, so a literal handed to one went unasked -- REV-CLOUD
3926
3962
  V21.4.1). `src/i18n_key_functions.test.js` reads every module and finds each
3927
- function that hands one of its PARAMETERS on to a place a key is read --
3928
- `t()`, an `i18n`, `code` or `data-i18n*` value, a key position of a function
3929
- of the table -- the parameter itself or what is read from it (`opts.title`, a
3930
- destructured `{label}`, each `items[].name`), directly or through a `let` it is
3931
- given to. The search runs to a fixpoint: a function that hands its parameter
3932
- to one it found is one too.
3963
+ function that hands one of its PARAMETERS on to a place a key is read -- a
3964
+ translator (any of the forms above), an `i18n`, `code` or `data-i18n*` value
3965
+ (`el.dataset.i18nTitle` included), a key position of a function of the table
3966
+ -- the parameter itself or what is read from it: `opts.title`, `opts?.title`,
3967
+ a destructured `{label}` in the parameter or in the body, each `items[].name`,
3968
+ `arguments[1]`, `rest[0]`, a copy (`{...opts}`, `Object.assign({}, opts)`),
3969
+ directly or through a `let` it is given to. The search runs to a fixpoint: a
3970
+ function that hands its parameter to one it found is one too.
3933
3971
 
3934
3972
  - A function called by NAME -- in its module, or exported -- must be in the
3935
3973
  table with that position, or the test fails, naming the entry to add:
@@ -3946,7 +3984,47 @@ to one it found is one too.
3946
3984
  writes `i18n: /* i18n: data */ title_key`: the window's title is a key its
3947
3985
  host chose.
3948
3986
  - What a gobj keeps in its `priv` is its own state, not what a caller hands:
3949
- a path through the `priv` of a parameter is no position.
3987
+ a path through the `priv` of a parameter is no position. So is what is
3988
+ read from a parameter `gobj` (the convention of every gclass function):
3989
+ its attrs, `gobj_read_attr(gobj, "title")`, were set by whoever created it.
3990
+ - A parameter handed on through what NO position can name fails too (since
3991
+ `7.26.32`), unless it is marked where it is handed: a key transformed
3992
+ (`t(k.toLowerCase())`, `"prefix " + k`, a template with `${}`, a call of a
3993
+ helper that does not return it as it is), an iteration of
3994
+ `Object.entries(opts)`, a rest parameter walked (`rest.forEach(...)`),
3995
+ `arguments[i]` by a computed index, a part of a parameter that an array
3996
+ pattern destructures. Its callers' keys reach no reader, so either the key
3997
+ is handed on as it arrives -- and the function goes in the table -- or the
3998
+ hand-off is marked `/* i18n: data */` (or a list). The test names the
3999
+ function, the parameter and the line:
4000
+
4001
+ ```js
4002
+ function caption(gobj, k) { return t(k.toLowerCase()); }
4003
+ ```
4004
+
4005
+ ```
4006
+ x.js:1 caption() hands its parameter [1] to a place a key is read through `k.toLowerCase()`,
4007
+ which no position can name: hand the key on as it arrives (the function then goes in
4008
+ the table), or mark it where it hands it
4009
+ ```
4010
+
4011
+ It found four in the library itself: the column labels of a form's
4012
+ template in `build_html_form()` and `template2columns()`
4013
+ (`Object.entries(template)`: the host's schema, marked data), a label's key
4014
+ read back from its DOM (`name_form_control()` of `c_g6_nodes_tree.js`,
4015
+ marked data), and the message of `mark_field_validity()`, which mixes a key
4016
+ with the json field's own error (marked with its lists).
4017
+ - **The table is CORRECT, not only complete** (since `7.26.32`): the same
4018
+ search, started from NO table, must find every entry, at every position,
4019
+ with the same `exported`. An entry nothing forwards to -- a wrong position,
4020
+ a function that no longer hands it on -- fails, and so does an `exported`
4021
+ that disagrees with the module (it hides the calls of other modules). Up to
4022
+ `7.26.31` an extra entry passed every test. The check found two: an
4023
+ `exported: false` on a function its module exports (`create_floating_icon`),
4024
+ and `yui_selection_bar`'s `1.actions[].label`, true but invisible to the
4025
+ search, because the label travelled through a DOM property
4026
+ (`$b._yui_label`); the function now paints each label from the actions
4027
+ themselves, where the reader follows it.
3950
4028
 
3951
4029
  Naming a function moves the marker to where the value ENTERS: its callers give
3952
4030
  it a literal, a const, a position of their own, or a marker. The nav's items
@@ -3982,11 +4060,11 @@ that way. The same code with a `let` asks for neither.
3982
4060
  export const MY_VIEW_I18N_KEYS = ['shape', 'radius', 'apply to'];
3983
4061
  ```
3984
4062
 
3985
- The list is read as DATA, never run: `export const` (or `let`, `var`),
3986
- `const X = [...]; export {X}` (or `export {x as X_I18N_KEYS}`), wrapped in
3987
- `Object.freeze()` or not, each element a string literal, a template with no
3988
- `${}`, or (since `7.26.29`) a SPREAD of another array of the same module read
3989
- the same way -- a top-level `const`, or a list the module publishes:
4063
+ The list is read as DATA, never run: `export const`, or `const X = [...];
4064
+ export {X}` (or `export {x as X_I18N_KEYS}`), wrapped in `Object.freeze()` or
4065
+ not, each element a string literal, a template with no `${}`, or (since
4066
+ `7.26.29`) a SPREAD of another array of the same module read the same way -- a
4067
+ top-level `const`, which a list the module publishes is:
3990
4068
 
3991
4069
  ```js
3992
4070
  const COMMON_KEYS = ['shape', 'radius'];
@@ -3998,16 +4076,31 @@ Anything named `*_I18N_KEYS` (the name in any letter case) that no consumer
3998
4076
  can read **fails the build**, naming the file and the list, in the app's own
3999
4077
  code too: an export that is not such an array (an element that is a variable,
4000
4078
  a call, a template with `${}`, a spread of an import or of a `let`), a list
4001
- that is not exported, one declared inside a function or a block, one declared
4002
- by destructuring (`export const {X_I18N_KEYS} = tables`), one exported under a
4003
- name that does not end in `_I18N_KEYS`. Up to `7.26.27` the regex that read
4004
- the lists dropped such a list in silence, and a key holding `]` dropped the
4005
- whole array; up to `7.26.28` a spread of a const of the module was refused
4006
- too.
4079
+ declared with `let` or `var` (since `7.26.32`: it can be given other keys,
4080
+ which nobody reads), a list that is not exported, one declared inside a
4081
+ function or a block, one declared by destructuring (`export const
4082
+ {X_I18N_KEYS} = tables`), one exported under a name that does not end in
4083
+ `_I18N_KEYS`. Up to `7.26.27` the regex that read the lists dropped such a
4084
+ list in silence, and a key holding `]` dropped the whole array; up to
4085
+ `7.26.28` a spread of a const of the module was refused too.
4086
+
4087
+ **A published list is never MUTATED** (since `7.26.32`): it is read as it is
4088
+ WRITTEN, so a key it is given later is asked for by nobody. `X.push(k)`,
4089
+ `X.splice(i, 0, k)`, `X.unshift(k)`, `X.fill(k)`, `X[i] = k`, `X.length = n`,
4090
+ `Object.assign(X, ...)` and `delete X[i]` fail the build, in the module that
4091
+ publishes the list and in every module that imports it (under any name). Up
4092
+ to `7.26.31` an `export let` list was read and could be pushed into, and the
4093
+ pushed keys went unasked:
4094
+
4095
+ ```js
4096
+ export let MY_VIEW_I18N_KEYS = ["shape"]; // fails: a published list is an export const
4097
+ MY_VIEW_I18N_KEYS.push("radius"); // fails: "radius" would be asked for by nobody
4098
+ ```
4007
4099
 
4008
4100
  **A MARKER says where the keys of a place come from.** It is a comment that
4009
- stands right before the node it marks (only whitespace between): an argument,
4010
- a branch of one, a property value, or the whole call. Two kinds:
4101
+ stands right before the node it marks (only whitespace between, and since
4102
+ `7.26.32` a parenthesis: `/* i18n: data */ (a || b)` marks `a || b`): an
4103
+ argument, a branch of one, a property value, or the whole call. Two kinds:
4011
4104
 
4012
4105
  ```js
4013
4106
  yui_shell_show_error(shell, /* i18n: data */ comment || "print-tranger failed", {t: t});
@@ -4021,7 +4114,15 @@ confirm_then(gobj, /* i18n: C_YUI_FORM_I18N_KEYS */ kw.warning || "all changes w
4021
4114
  above) are still read.
4022
4115
  - `i18n: <NAME>_I18N_KEYS` (several by commas) -- the keys come from those
4023
4116
  published lists. The keys of the marked expression that the reader CAN
4024
- follow must be in them: a marker is a claim, and it is checked.
4117
+ follow must be in them: a marker is a claim, and it is checked. A key BUILT
4118
+ at run time is published that way and marked with its list -- gui_agent's
4119
+ monitor names a control's label `monitor ${control}`, and publishes the six
4120
+ keys that template builds in `C_AGENT_MONITOR_I18N_KEYS`. A template with
4121
+ `${}` under a list marker must have the SHAPE of some key of the lists
4122
+ (since `7.26.32`): `monitor ${control}` is checked against `^monitor .*$`,
4123
+ so a marker naming the wrong list fails. Every key of a published list is
4124
+ asked for, so a control whose key the list lacks is the one thing no check
4125
+ sees: the list sits next to the template, and a comment says so.
4025
4126
 
4026
4127
  **A `data` marker is a claim too** (since `7.26.29`). On what the reader
4027
4128
  follows to keys and nothing else -- a literal, a template with no `${}`, a
@@ -4042,6 +4143,31 @@ yui_shell_show_info(shell, /* i18n: data */ pick());
4042
4143
  `/* i18n: data */ comment || "print-tranger failed"` stays legal: `comment` is
4043
4144
  data, and the fallback is read anyway.
4044
4145
 
4146
+ Since `7.26.32` the claim is checked further:
4147
+
4148
+ - the call may be of a function IMPORTED from a module the reader reads (a
4149
+ file of the app, a module of the library), and the const may be imported:
4150
+ both are followed into their module, so `/* i18n: data */ pick()` fails
4151
+ there too;
4152
+ - a data marker on a call whose returns MIX keys with data fails: the reader
4153
+ does not read such a call (it reads a call only when it knows every
4154
+ return), so the marker would hide the key. Split the branches, so the key is
4155
+ handed where it is read and only the data is marked:
4156
+
4157
+ ```js
4158
+ function label(kw) { return kw.ok? "accepted" : kw.comment; }
4159
+ yui_shell_show_info(shell, /* i18n: data */ label(kw)); // fails
4160
+ yui_shell_show_info(shell, /* i18n: data */ kw.comment || "accepted"); // the same, split
4161
+ ```
4162
+
4163
+ The library had it twice: `refusal_text()` of the treedb views returned the
4164
+ backend's words or "print-tranger failed". It returns the words only now,
4165
+ and the call writes the fallback. Under a LIST marker such a call is
4166
+ checked, not refused: its keys are asked for and must be in the lists;
4167
+ - a data marker on an ARRAY OF STRING LITERALS fails: it is no DOM (a
4168
+ `createElement2` spec carries its attrs, an object, in its second slot), so
4169
+ the strings would be hidden.
4170
+
4045
4171
  **A marked `let` (or `var`) is held to its marker.** A marker that names a list
4046
4172
  used to let a `let` pass unread, whatever it held: a new literal branch on it
4047
4173
  was neither asked for nor checked (REV-CLOUD V20). Now, when what a marker
@@ -4102,10 +4228,19 @@ would not carry what is pushed into it to the reader.
4102
4228
 
4103
4229
  `validate-locales` checks every marker in what it scans, in an app's code too:
4104
4230
  a comment that starts with `i18n:` and is no marker, a marker that stands
4105
- before no place a key is read (a key position, `t()`, an `i18n` or
4231
+ before no place a key is read (a key position, a translator, an `i18n` or
4106
4232
  `data-i18n*` value), a marker that names a list no module the app mounts
4107
- publishes, a marked key that is not in the lists it names, a marked variable
4108
- that only ever holds literals -- each one fails, with the file and the line.
4233
+ publishes, a marked key that is not in the lists it names, a marked template
4234
+ with no key of its shape in them, a marked variable that only ever holds
4235
+ literals -- each one fails, with the file and the line. A marker that stands
4236
+ before no place says what it stands before, and why that is none (since
4237
+ `7.26.32`; it used to say only *"stands before no place a key is read"*):
4238
+
4239
+ ```
4240
+ main.js:8: the i18n marker stands before `1;`, the value a variable is DECLARED with: mark it
4241
+ where the variable is handed to a place a key is read. A marker marks the value it stands
4242
+ right before, where a key is read: ...
4243
+ ```
4109
4244
 
4110
4245
  **The rule is per CALL, in the library and in the app.**
4111
4246
  `src/i18n_key_functions.test.js` reads every module of the library with the
@@ -4132,7 +4267,11 @@ src/main.js:12: yui_shell_confirm_yesno() [2.title] is given opts, which the rea
4132
4267
 
4133
4268
  A TRANSLATED text handed to a key position fails even when it is marked: the
4134
4269
  function translates it again and marks it `data-i18n` with the translation,
4135
- so it never changes language. Hand it the key:
4270
+ so it never changes language. Hand it the key. A translated text is the value
4271
+ of any TRANSLATOR (since `7.26.32`; up to `7.26.31` only a call named `t`, so
4272
+ `tr("save")`, an alias of t, `t?.()` or `t.call()` passed marked -- and an
4273
+ unmarked `tr()` failed with *"cannot follow"*, whose advice was the marker that
4274
+ then let it pass):
4136
4275
 
4137
4276
  ```js
4138
4277
  yui_install_ask_once(shell, {t: t, message: t("install this app")}); // fails
@@ -4142,7 +4281,7 @@ yui_install_ask_once(shell, {t: t, message: "install this app"}); // the ke
4142
4281
  ```
4143
4282
  src/c_app.js:734 (call at line 732): yui_install_ask_once() [1.message] is given t("install this app"):
4144
4283
  a TRANSLATED text where a KEY goes (translated again and marked data-i18n with the translation,
4145
- it never changes language): hand it the key
4284
+ it never changes language, and no marker excuses it): hand the key, not t(key)
4146
4285
  ```
4147
4286
 
4148
4287
  In an app, what a wrapper of its own hands on is marked where it hands it:
@@ -4163,10 +4302,11 @@ module. The same test fails when a marker is not one
4163
4302
  or stands nowhere; when a marker names a list that is not published by its
4164
4303
  module or by one it imports (a consumer would not read it); when a marked key
4165
4304
  the reader can follow is not in the list; when a marked variable only ever
4166
- holds literals; when a list is not readable, holds a key that is not
4167
- lower-case ASCII, holds one twice, or holds one written nowhere else in its
4168
- module (stale); when an entry of the table names a function its module does
4169
- not declare.
4305
+ holds literals; when a list is not readable, is mutated, holds a key that is
4306
+ not lower-case ASCII, holds one twice, or holds one written nowhere else in
4307
+ its module (stale); when an entry of the table names a function its module
4308
+ does not declare, or (since `7.26.32`) a position the search for forwarders
4309
+ does not find.
4170
4310
 
4171
4311
  **Every key of every list is PINNED**: dropping it from its list fails a test,
4172
4312
  or loses nothing. For each key, one of these holds, or the test fails with
@@ -4210,19 +4350,41 @@ the node graph's key-taking helpers from its syntax tree, and
4210
4350
  `yui_dev_controls.test.js` builds the Developer window and compares every key
4211
4351
  it asks for or carries with the list.
4212
4352
 
4213
- **What is NOT pinned.** `t()` is not a key function, and only the positions of
4214
- `I18N_KEY_FUNCTIONS` are held to the per-call rule. So a key handed to `t()`,
4215
- `i18n` or a `data-i18n*` value by an UNMARKED `let`, a call the reader does not
4216
- follow or a concatenation is invisible to the validator and fails no test --
4217
- in the library and in an app alike. (A PARAMETER is not, in the library: the
4218
- forwarder test puts its function in the table, or demands a marker.) So is
4219
- what is written INTO a `const` table after its declaration (`LIST.push("k")`,
4220
- `TABLE.k = "k"`): the reader follows a const to its value, and only a marked
4221
- `let` is followed into what is written into it. It is found only by reading
4222
- the page in another language. In an app, a key its callers hand a wrapper of
4223
- its own reaches the locales only through a list the module publishes: the
4224
- wrapper's hand-off must be marked (the rule per call), and nothing ties its
4225
- callers' literals to the list.
4353
+ **What stays INVISIBLE** -- read by no check, failing nothing; found only by
4354
+ reading the page in another language:
4355
+
4356
+ - a key handed to a translator, an `i18n` or a `data-i18n*` value by an
4357
+ UNMARKED `let`, a call the reader does not follow (a recursion, one whose
4358
+ returns mix keys and data, a function of a package) or a computed string:
4359
+ a translator is not a key function, and only the positions of
4360
+ `I18N_KEY_FUNCTIONS` are held to the per-call rule -- in the library and in
4361
+ an app alike. (A PARAMETER is not, in the library: the forwarder test puts
4362
+ its function in the table, or demands a marker, also when no position can
4363
+ name it.);
4364
+ - a translator the reader does not know as one: a name other than `t` / `tr`
4365
+ that no const or import makes an alias of -- a parameter `translate`, a
4366
+ `let` alias, `opts.translate(k)`, `i18next.getFixedT()`;
4367
+ - a key function reached through a parameter (`apply(fn)` calling
4368
+ `fn(shell, "k")`), a `let` given more than one function, a map in a `let`,
4369
+ a computed member (`API[name](...)`), or `.apply()` of an array the source
4370
+ does not write out;
4371
+ - a `data-i18n*` attribute written through an alias of `dataset`
4372
+ (`const ds = el.dataset; ds.i18nTitle = k`), by `Object.assign()` into it,
4373
+ or named by a `let`;
4374
+ - what is written INTO a `const` table after its declaration
4375
+ (`TABLE.k = "k"`): the reader follows a const to its value, and only a
4376
+ marked `let` is followed into what is written into it (a published list
4377
+ that is written into fails: above);
4378
+ - what a module of a package exports (gobj-js, i18next), and an exported
4379
+ `let`;
4380
+ - in the library's forwarder search, a key stored in a heap object or a DOM
4381
+ node and read back somewhere else (`$b._yui_label` was one), and what a
4382
+ caller puts in a gobj's attrs (a window's `title` given at `gobj_create()`);
4383
+ - in an app, a key its callers hand a wrapper of its own reaches the locales
4384
+ only through a list the module publishes: the wrapper's hand-off must be
4385
+ marked (the rule per call), and nothing ties its callers' literals to the
4386
+ list -- nor a key built at run time (a template) to the list its marker
4387
+ names, beyond the shape of the template.
4226
4388
 
4227
4389
  **What fails the build, besides a key no locale defines:**
4228
4390
 
@@ -4233,9 +4395,12 @@ callers' literals to the list.
4233
4395
  - a locale that looks like one and exports no `{<code>}` with a
4234
4396
  `translation` dict;
4235
4397
  - a source file that cannot be parsed;
4236
- - a published list no consumer can read (above);
4237
- - a marker that is wrong (above), a `data` marker on what the reader follows
4238
- to keys included;
4398
+ - a published list no consumer can read (above), declared with `let` or
4399
+ `var`, or mutated;
4400
+ - a marker that is wrong (above): a `data` marker on what the reader follows
4401
+ to keys, or on what would hide a key (a call mixing keys and data, an array
4402
+ of string literals), a list marker on a template whose shape no key of the
4403
+ list has;
4239
4404
  - a key position of a key function, in the app's own code, given what the
4240
4405
  reader cannot follow and unmarked, or given a translated text (the rule per
4241
4406
  call, above).
@@ -10711,7 +10711,9 @@ function get_form_values(gobj, $form) {
10711
10711
  else value = value_.text;
10712
10712
  }
10713
10713
  break;
10714
- case "fieldset": break;
10714
+ case "fieldset":
10715
+ value = {};
10716
+ break;
10715
10717
  case "table":
10716
10718
  let template = (0, _yuneta_gobj_js.gobj_read_attr)(gobj, "template");
10717
10719
  if (!(0, _yuneta_gobj_js.empty_string)(name) && (0, _yuneta_gobj_js.kw_has_key)(template, name)) {
@@ -10733,7 +10735,7 @@ function get_form_values(gobj, $form) {
10733
10735
  break;
10734
10736
  default: (0, _yuneta_gobj_js.log_error)((0, _yuneta_gobj_js.sprintf)("%s: $form type unknown: '%s'", (0, _yuneta_gobj_js.gobj_short_name)(gobj), tag));
10735
10737
  }
10736
- value = form_value_2_treedb_value(gobj, field_desc, value);
10738
+ if (tag !== "fieldset") value = form_value_2_treedb_value(gobj, field_desc, value);
10737
10739
  let path;
10738
10740
  if ((0, _yuneta_gobj_js.empty_string)(prefix)) path = name;
10739
10741
  else path = prefix + "`" + name;
@@ -27190,12 +27192,14 @@ function answer_print_tranger_refused$1(gobj, path, answer) {
27190
27192
  }
27191
27193
  /************************************************************
27192
27194
  * The text of a transport's refusal: gobj_command() answers a string,
27193
- * a command response or a number.
27195
+ * a command response or a number. Data only ("" when it says
27196
+ * nothing): the caller writes its own key as the fallback, where
27197
+ * validate-locales reads it.
27194
27198
  ************************************************************/
27195
27199
  function refusal_text$1(ret) {
27196
27200
  if (typeof ret === "string" && ret) return ret;
27197
27201
  if (ret && typeof ret === "object" && typeof ret.comment === "string" && ret.comment) return ret.comment;
27198
- return "print-tranger failed";
27202
+ return "";
27199
27203
  }
27200
27204
  /************************************************************
27201
27205
  * The whole tranger, for the raw-json viewer.
@@ -27223,7 +27227,7 @@ function request_print_tranger$1(gobj, path) {
27223
27227
  }, gobj);
27224
27228
  if (ret) {
27225
27229
  (0, _yuneta_gobj_js.log_error)(ret);
27226
- answer_print_tranger_refused$1(gobj, path, { error: refusal_text$1(ret) });
27230
+ answer_print_tranger_refused$1(gobj, path, { error: refusal_text$1(ret) || "print-tranger failed" });
27227
27231
  return;
27228
27232
  }
27229
27233
  if (path) priv.drills_in_flight.add(path);
@@ -28364,8 +28368,8 @@ function yui_clear_selection(tabulator) {
28364
28368
  * through `refresh_language()`. Call it on EV_LANGUAGE_CHANGED.
28365
28369
  ***************************************************************/
28366
28370
  function yui_selection_bar(t, opts) {
28367
- let o = opts || {};
28368
- let actions = Array.isArray(o.actions) ? o.actions : [];
28371
+ const o = opts || {};
28372
+ const actions = Array.isArray(o.actions) ? o.actions : [];
28369
28373
  let name = o.name || "TABLE";
28370
28374
  let count = 0;
28371
28375
  let $count = (0, _yuneta_gobj_js.createElement2)([
@@ -28392,7 +28396,6 @@ function yui_selection_bar(t, opts) {
28392
28396
  ]],
28393
28397
  { click: action.on_click }
28394
28398
  ]);
28395
- $b._yui_label = action.label || "";
28396
28399
  $buttons.push($b);
28397
28400
  }
28398
28401
  let $clear = (0, _yuneta_gobj_js.createElement2)([
@@ -28425,15 +28428,18 @@ function yui_selection_bar(t, opts) {
28425
28428
  function label_of($b) {
28426
28429
  return $b.querySelector("span.icon ~ span");
28427
28430
  }
28431
+ function paint_label($b, key) {
28432
+ let $label = label_of($b);
28433
+ if ($label && key) $label.textContent = t(key);
28434
+ $b.title = key ? t(key) : "";
28435
+ $b.setAttribute("aria-label", $b.title);
28436
+ }
28428
28437
  function refresh() {
28429
28438
  $count.textContent = t("{{n}} selected", { n: count });
28430
- for (let $b of $buttons.concat([$clear])) {
28431
- let $label = label_of($b);
28432
- const key = $b === $clear ? "clear selection" : $b._yui_label;
28433
- if ($label && key) $label.textContent = t(key);
28434
- $b.title = key ? t(key) : "";
28435
- $b.setAttribute("aria-label", $b.title);
28436
- }
28439
+ actions.forEach((action, i) => {
28440
+ paint_label($buttons[i], action.label || "");
28441
+ });
28442
+ paint_label($clear, "clear selection");
28437
28443
  $el.classList.toggle("is-hidden", count <= 0);
28438
28444
  }
28439
28445
  function set_count(n) {
@@ -71278,12 +71284,14 @@ function answer_print_tranger_refused(gobj, path, answer) {
71278
71284
  }
71279
71285
  /************************************************************
71280
71286
  * The text of a transport's refusal: gobj_command() answers a string,
71281
- * a command response or a number.
71287
+ * a command response or a number. Data only ("" when it says
71288
+ * nothing): the caller writes its own key as the fallback, where
71289
+ * validate-locales reads it.
71282
71290
  ************************************************************/
71283
71291
  function refusal_text(ret) {
71284
71292
  if (typeof ret === "string" && ret) return ret;
71285
71293
  if (ret && typeof ret === "object" && typeof ret.comment === "string" && ret.comment) return ret.comment;
71286
- return "print-tranger failed";
71294
+ return "";
71287
71295
  }
71288
71296
  /************************************************************
71289
71297
  * Fetch the treedb's raw tranger (or one subtree when `path` is set) as
@@ -71312,7 +71320,7 @@ function request_print_tranger(gobj, path) {
71312
71320
  }, gobj);
71313
71321
  if (ret) {
71314
71322
  (0, _yuneta_gobj_js.log_error)(ret);
71315
- answer_print_tranger_refused(gobj, path, { error: refusal_text(ret) });
71323
+ answer_print_tranger_refused(gobj, path, { error: refusal_text(ret) || "print-tranger failed" });
71316
71324
  return;
71317
71325
  }
71318
71326
  if (path) priv.drills_in_flight.add(path);