@yuneta/gobj-ui 7.26.30 → 7.26.32

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
@@ -78,6 +78,15 @@ Published as `@yuneta/gobj-ui`. Built on top of [`@yuneta/gobj-js`](https://gith
78
78
  > maximize/restore + close. The self-contained **"shade"** fallback (roll up to
79
79
  > the title bar in place) and its `is-shaded` CSS are **removed**; an app that
80
80
  > relied on shading needs to register a manager.
81
+ >
82
+ > **Where a window lives (7.26.31):** a `C_YUI_WINDOW` whose host names no
83
+ > `$parent` mounts in the **overlay layer of the shell** -- above the content,
84
+ > below the toolbar's dropdowns, the dialogs and the toasts -- and on
85
+ > `document.body` only when there is no shell. It used to be the body always,
86
+ > and since `.yui-shell` is `position: fixed` (a stacking context of its own)
87
+ > a window at z-index 11 covered the WHOLE shell, modal layer included: the
88
+ > confirmation of a window's own close opened under it, out of reach. A
89
+ > `$parent` the host names still wins.
81
90
 
82
91
  ## Two maintained lines
83
92
 
@@ -86,8 +95,8 @@ consumers. They are independent snapshots (no shared git ancestry):
86
95
 
87
96
  | Line | Branch | Latest tag | Layout | Consumed by | How | Status |
88
97
  |------|--------|------------|--------|-------------|-----|--------|
89
- | **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 |
90
- | **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 |
91
100
 
92
101
  - **v2 / `main`** is the active development line: the declarative shell
93
102
  (legacy-stack-free since `3.0.0`). It is embedded as a git submodule in
@@ -100,9 +109,10 @@ consumers. They are independent snapshots (no shared git ancestry):
100
109
  (`npm run deploy-round` does the round). `test-app/` is the one exception:
101
110
  it consumes the working tree (`file:..`), which is what it is for.
102
111
  - **v1 / `v1`** is the frozen legacy-only stack (the declarative shell is not on
103
- this line). It is **published to npm**; estadodelaire and hidraulia depend on
104
- `@yuneta/gobj-ui@^1.0.4` from the registry. Land only maintenance fixes here,
105
- 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`.
106
116
 
107
117
  All new feature work lands on `main`/v2.
108
118
 
@@ -3826,28 +3836,57 @@ text is a key, `"configuración"` included.
3826
3836
  dependency of this package since `7.26.29`: an app need not declare it). A
3827
3837
  LOCALE is not a source: it is parsed, and then imported, so its top-level code
3828
3838
  runs -- the app's own code, but code. Comments are not code. A key is read
3829
- where it is WRITTEN -- given to `t()`, as an `i18n` or `data-i18n*` value of
3830
- an object literal, by `setAttribute()`, or (since `7.26.29`) written as a
3831
- member, `attrs.i18n = "save"`, `attrs["data-i18n-title"] = "save"` -- and
3832
- 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:
3833
3857
 
3834
3858
  - a string literal, a template with no `${}`, and every branch of `?:`, `||`
3835
3859
  and `??` (the right one of `&&`), so
3836
3860
  `"data-i18n": pending ? "loading" : "click to load"` asks for both, and
3837
3861
  `comment || "print-tranger failed"` for its fallback;
3838
3862
  - a `const`, to its value, in the scope that declares it (a parameter or a
3839
- `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`;
3840
3869
  - a property of a const object literal: `DEFAULT_LABEL.ok`, and for a
3841
- 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)`;
3842
3873
  - the element of a const array literal that a `.map()`, `.forEach()`, ...
3843
3874
  callback or a `for...of` walks, and what a destructuring reads from it
3844
3875
  (`for(const {key} of ROWS)`);
3845
3876
  - `String(x)`, and a template that is only `${x}`, as `x` (since `7.26.29`);
3846
- - the default of a parameter, `function f(msg = "fallback")`, besides the
3847
- parameter (since `7.26.29`);
3848
- - a call of a function of the same module whose every `return` is one of
3849
- these (since `7.26.29`). One that returns anything else -- a parameter, a
3850
- 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.
3851
3890
 
3852
3891
  ```js
3853
3892
  const KEY = "save";
@@ -3864,12 +3903,12 @@ function pick(ok) {
3864
3903
  t(pick(answer)); // asks for both
3865
3904
  ```
3866
3905
 
3867
- What it cannot follow cannot be known: a `let`, a parameter, an import, a
3868
- call, a concatenation, data from a backend. A key that arrives that way is
3869
- PUBLISHED by its module (below), and the place it is read is MARKED; a marked
3870
- `let` -- or a marked member or element of one -- is the one exception to "not
3871
- followed" (below). A source file that cannot be parsed fails the build, because
3872
- 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.
3873
3912
 
3874
3913
  **A key handed to a FUNCTION is read where it is handed.**
3875
3914
  `yui_shell_show_error(shell, "some records were gone before the delete")` asks
@@ -3891,10 +3930,16 @@ A position is an argument (`"1"`), a property of the object given there
3891
3930
  (`"2.title"`, also through `Object.assign()` or a spread of a const object),
3892
3931
  or a property of each object of an array (`"1.actions[].label"`). What is
3893
3932
  given at those positions is read like any other key (literals, consts,
3894
- branches): in the app's own code, on a call bound to an import of that
3895
- function from gobj-ui (an app function of the same name is not mistaken for
3896
- it), and in every module the app mounts, where an entry with
3897
- `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
3898
3943
  DOM, which is no key (since `7.26.29`): an array (a `createElement2` spec), a
3899
3944
  call of `createElement2()`, `createOneHtml()` or `html_markup()` imported
3900
3945
  from gobj-js, of `text_node()` from gobj-ui, of `document.createElement()`
@@ -3915,12 +3960,14 @@ gclass name, which titles it).
3915
3960
  (since `7.26.29`: up to `7.26.28` the table lacked 39 of them, and 6 entries
3916
3961
  lacked positions, so a literal handed to one went unasked -- REV-CLOUD
3917
3962
  V21.4.1). `src/i18n_key_functions.test.js` reads every module and finds each
3918
- function that hands one of its PARAMETERS on to a place a key is read --
3919
- `t()`, an `i18n`, `code` or `data-i18n*` value, a key position of a function
3920
- of the table -- the parameter itself or what is read from it (`opts.title`, a
3921
- destructured `{label}`, each `items[].name`), directly or through a `let` it is
3922
- given to. The search runs to a fixpoint: a function that hands its parameter
3923
- 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.
3924
3971
 
3925
3972
  - A function called by NAME -- in its module, or exported -- must be in the
3926
3973
  table with that position, or the test fails, naming the entry to add:
@@ -3937,7 +3984,47 @@ to one it found is one too.
3937
3984
  writes `i18n: /* i18n: data */ title_key`: the window's title is a key its
3938
3985
  host chose.
3939
3986
  - What a gobj keeps in its `priv` is its own state, not what a caller hands:
3940
- 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.
3941
4028
 
3942
4029
  Naming a function moves the marker to where the value ENTERS: its callers give
3943
4030
  it a literal, a const, a position of their own, or a marker. The nav's items
@@ -3973,11 +4060,11 @@ that way. The same code with a `let` asks for neither.
3973
4060
  export const MY_VIEW_I18N_KEYS = ['shape', 'radius', 'apply to'];
3974
4061
  ```
3975
4062
 
3976
- The list is read as DATA, never run: `export const` (or `let`, `var`),
3977
- `const X = [...]; export {X}` (or `export {x as X_I18N_KEYS}`), wrapped in
3978
- `Object.freeze()` or not, each element a string literal, a template with no
3979
- `${}`, or (since `7.26.29`) a SPREAD of another array of the same module read
3980
- 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:
3981
4068
 
3982
4069
  ```js
3983
4070
  const COMMON_KEYS = ['shape', 'radius'];
@@ -3989,16 +4076,31 @@ Anything named `*_I18N_KEYS` (the name in any letter case) that no consumer
3989
4076
  can read **fails the build**, naming the file and the list, in the app's own
3990
4077
  code too: an export that is not such an array (an element that is a variable,
3991
4078
  a call, a template with `${}`, a spread of an import or of a `let`), a list
3992
- that is not exported, one declared inside a function or a block, one declared
3993
- by destructuring (`export const {X_I18N_KEYS} = tables`), one exported under a
3994
- name that does not end in `_I18N_KEYS`. Up to `7.26.27` the regex that read
3995
- the lists dropped such a list in silence, and a key holding `]` dropped the
3996
- whole array; up to `7.26.28` a spread of a const of the module was refused
3997
- 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
+ ```
3998
4099
 
3999
4100
  **A MARKER says where the keys of a place come from.** It is a comment that
4000
- stands right before the node it marks (only whitespace between): an argument,
4001
- 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:
4002
4104
 
4003
4105
  ```js
4004
4106
  yui_shell_show_error(shell, /* i18n: data */ comment || "print-tranger failed", {t: t});
@@ -4012,7 +4114,15 @@ confirm_then(gobj, /* i18n: C_YUI_FORM_I18N_KEYS */ kw.warning || "all changes w
4012
4114
  above) are still read.
4013
4115
  - `i18n: <NAME>_I18N_KEYS` (several by commas) -- the keys come from those
4014
4116
  published lists. The keys of the marked expression that the reader CAN
4015
- 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.
4016
4126
 
4017
4127
  **A `data` marker is a claim too** (since `7.26.29`). On what the reader
4018
4128
  follows to keys and nothing else -- a literal, a template with no `${}`, a
@@ -4033,6 +4143,31 @@ yui_shell_show_info(shell, /* i18n: data */ pick());
4033
4143
  `/* i18n: data */ comment || "print-tranger failed"` stays legal: `comment` is
4034
4144
  data, and the fallback is read anyway.
4035
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
+
4036
4171
  **A marked `let` (or `var`) is held to its marker.** A marker that names a list
4037
4172
  used to let a `let` pass unread, whatever it held: a new literal branch on it
4038
4173
  was neither asked for nor checked (REV-CLOUD V20). Now, when what a marker
@@ -4093,10 +4228,19 @@ would not carry what is pushed into it to the reader.
4093
4228
 
4094
4229
  `validate-locales` checks every marker in what it scans, in an app's code too:
4095
4230
  a comment that starts with `i18n:` and is no marker, a marker that stands
4096
- 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
4097
4232
  `data-i18n*` value), a marker that names a list no module the app mounts
4098
- publishes, a marked key that is not in the lists it names, a marked variable
4099
- 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
+ ```
4100
4244
 
4101
4245
  **The rule is per CALL, in the library and in the app.**
4102
4246
  `src/i18n_key_functions.test.js` reads every module of the library with the
@@ -4123,7 +4267,11 @@ src/main.js:12: yui_shell_confirm_yesno() [2.title] is given opts, which the rea
4123
4267
 
4124
4268
  A TRANSLATED text handed to a key position fails even when it is marked: the
4125
4269
  function translates it again and marks it `data-i18n` with the translation,
4126
- 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):
4127
4275
 
4128
4276
  ```js
4129
4277
  yui_install_ask_once(shell, {t: t, message: t("install this app")}); // fails
@@ -4133,7 +4281,7 @@ yui_install_ask_once(shell, {t: t, message: "install this app"}); // the ke
4133
4281
  ```
4134
4282
  src/c_app.js:734 (call at line 732): yui_install_ask_once() [1.message] is given t("install this app"):
4135
4283
  a TRANSLATED text where a KEY goes (translated again and marked data-i18n with the translation,
4136
- it never changes language): hand it the key
4284
+ it never changes language, and no marker excuses it): hand the key, not t(key)
4137
4285
  ```
4138
4286
 
4139
4287
  In an app, what a wrapper of its own hands on is marked where it hands it:
@@ -4154,10 +4302,11 @@ module. The same test fails when a marker is not one
4154
4302
  or stands nowhere; when a marker names a list that is not published by its
4155
4303
  module or by one it imports (a consumer would not read it); when a marked key
4156
4304
  the reader can follow is not in the list; when a marked variable only ever
4157
- holds literals; when a list is not readable, holds a key that is not
4158
- lower-case ASCII, holds one twice, or holds one written nowhere else in its
4159
- module (stale); when an entry of the table names a function its module does
4160
- 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.
4161
4310
 
4162
4311
  **Every key of every list is PINNED**: dropping it from its list fails a test,
4163
4312
  or loses nothing. For each key, one of these holds, or the test fails with
@@ -4201,19 +4350,41 @@ the node graph's key-taking helpers from its syntax tree, and
4201
4350
  `yui_dev_controls.test.js` builds the Developer window and compares every key
4202
4351
  it asks for or carries with the list.
4203
4352
 
4204
- **What is NOT pinned.** `t()` is not a key function, and only the positions of
4205
- `I18N_KEY_FUNCTIONS` are held to the per-call rule. So a key handed to `t()`,
4206
- `i18n` or a `data-i18n*` value by an UNMARKED `let`, a call the reader does not
4207
- follow or a concatenation is invisible to the validator and fails no test --
4208
- in the library and in an app alike. (A PARAMETER is not, in the library: the
4209
- forwarder test puts its function in the table, or demands a marker.) So is
4210
- what is written INTO a `const` table after its declaration (`LIST.push("k")`,
4211
- `TABLE.k = "k"`): the reader follows a const to its value, and only a marked
4212
- `let` is followed into what is written into it. It is found only by reading
4213
- the page in another language. In an app, a key its callers hand a wrapper of
4214
- its own reaches the locales only through a list the module publishes: the
4215
- wrapper's hand-off must be marked (the rule per call), and nothing ties its
4216
- 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.
4217
4388
 
4218
4389
  **What fails the build, besides a key no locale defines:**
4219
4390
 
@@ -4224,9 +4395,12 @@ callers' literals to the list.
4224
4395
  - a locale that looks like one and exports no `{<code>}` with a
4225
4396
  `translation` dict;
4226
4397
  - a source file that cannot be parsed;
4227
- - a published list no consumer can read (above);
4228
- - a marker that is wrong (above), a `data` marker on what the reader follows
4229
- 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;
4230
4404
  - a key position of a key function, in the app's own code, given what the
4231
4405
  reader cannot follow and unmarked, or given a translated text (the rule per
4232
4406
  call, above).