docs: LANGUAGE.md's Text keys (Key, k"..." / kn"...", the lang line, derived @Text keys and @TextKey, the checks, the schema's "lang"), the lang manifest key in SHIPPING.md, and the changeset
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
f9808e31ec
commit
66b8e51026
3 changed files with 89 additions and 0 deletions
75
LANGUAGE.md
75
LANGUAGE.md
|
|
@ -800,6 +800,7 @@ object with `"schema_version": 1`:
|
||||||
`handler` the `fn` value it passes last (else null). A tag worked out at run time cannot be known
|
`handler` the `fn` value it passes last (else null). A tag worked out at run time cannot be known
|
||||||
and is left out.
|
and is left out.
|
||||||
- `units` - `@Unit`'s canonical spellings.
|
- `units` - `@Unit`'s canonical spellings.
|
||||||
|
- `lang` - the text keys and the languages (see Text keys), or null without a `lang` line.
|
||||||
|
|
||||||
Each list is sorted by name (then file and line), a registry's entries are in index order, and the
|
Each list is sorted by name (then file and line), a registry's entries are in index order, and the
|
||||||
paths are the ones the compiler read, so two runs over the same source write the same file. The
|
paths are the ones the compiler read, so two runs over the same source write the same file. The
|
||||||
|
|
@ -810,6 +811,79 @@ worked out once and only when the program names it - so a tool talking to a runn
|
||||||
whether it was compiled from the data in front of it. It is 0 in a release (`ludicc --release`,
|
whether it was compiled from the data in front of it. It is 0 in a release (`ludicc --release`,
|
||||||
which `ludic bundle` passes).
|
which `ludic bundle` passes).
|
||||||
|
|
||||||
|
### Text keys (`Key`, `k"..."`, `lang`)
|
||||||
|
|
||||||
|
A text the player reads is named by a KEY, and the key's English is a language file like any
|
||||||
|
other's. `package.ludic` says where the languages are and which is the source:
|
||||||
|
|
||||||
|
```
|
||||||
|
lang "assets/lang" en # the directory, and the source language: assets/lang/en.po
|
||||||
|
```
|
||||||
|
|
||||||
|
In code a key is a literal of the builtin type `Key` - `k"module.purpose"`, or `kn"..."` for a key
|
||||||
|
whose text has plural forms - written against its quote (`k "x"` is a name and a string, as ever):
|
||||||
|
|
||||||
|
```ludic
|
||||||
|
# doc-check: skip — tr / trf / trn are ludic.i18n's
|
||||||
|
let title = tr(k"pause.resume") # tr(key: Key) -> string
|
||||||
|
let day = trf(k"hud.day", txt_num(n)) # the English's holes {1}..{4}, in order
|
||||||
|
let got = trn(kn"catch.count", n, fish) # {1} is the count, the rest follow
|
||||||
|
```
|
||||||
|
|
||||||
|
A `Key` is not a `string`, and a `string` is not a `Key`: giving one where the other is wanted is an
|
||||||
|
error either way, and so is `+` on a key. Keys compare with `==` and `!=`, and are fields, parameters,
|
||||||
|
results, list elements and registry values like any other type (`null` is none). At run time a key is
|
||||||
|
its text after a marker byte - byte 1, or 2 for `kn"..."` - so `k"pause.resume"` is the string
|
||||||
|
`"\x01pause.resume"`: the runtime's `tr` is a cast, and a translator knows a key from English.
|
||||||
|
`string(key)` gives that marked text, for a runtime that needs it; a program itself makes text with
|
||||||
|
`tr`. (A program that declares its own type named `Key` keeps it; the builtin is then out of reach.)
|
||||||
|
|
||||||
|
**The data.** A registry's `@Text` field is text by derivation: its key is
|
||||||
|
`<registry>.<row>.<field>` - the registry's name in snake case (`Items` -> `items`, `GearKinds` ->
|
||||||
|
`gear_kinds`), a list field adding `.<index>` and a nested record `.<field>` - or, with
|
||||||
|
`@TextKey("steps")` on the registry, `steps.<row>.<field>`. A `@PerMap` table's is
|
||||||
|
`maps.<map>.<table>.<row>.<field>`. When the field's type is `Key` and a row of a compiled registry
|
||||||
|
gives it no value, the compiler fills in the derived key (marked, as a literal is), so code writes
|
||||||
|
`tr(Items[i].name)` and the `.lres` never spells a key; a row may still name one, `name:
|
||||||
|
k"items.lamp.name"`, in a resource file or a map's file alike.
|
||||||
|
|
||||||
|
```ludic
|
||||||
|
# doc-check: skip — the data file is the game's
|
||||||
|
property Item {
|
||||||
|
key: string = ""
|
||||||
|
@Text name: Key = null # items.<row>.name, filled in when the row gives none
|
||||||
|
}
|
||||||
|
registry Items of Item as IT from "data/items.lres"
|
||||||
|
@TextKey("steps") registry StepKinds of StepKind from "steps.lres" # steps.<row>.<field>
|
||||||
|
```
|
||||||
|
|
||||||
|
**The templates.** A component's text is a key too: `{t('pause.resume')}`, `title="{t('pause.close')}"`,
|
||||||
|
`{t('hud.day', day)}`; `t(expr)` takes a key worked out at run time (a `Key` field reaches a template
|
||||||
|
as its marked text). Words outside an element with `translate="no"` are English still waiting for a
|
||||||
|
key.
|
||||||
|
|
||||||
|
**The checks.** With a `lang` line and its source `.po` there, the compiler holds every key to it,
|
||||||
|
each diagnostic at its `file:line:col`:
|
||||||
|
|
||||||
|
- a `k"..."` literal, a template's `t('...')` literal, or a key a data row names or derives that the
|
||||||
|
source `.po` does not have is an **error**; so is a `kn"..."` whose entry has no `msgid_plural`;
|
||||||
|
- a call of `trf` or `trn` with a key literal, and a template's `t('key', ...)`, gives as many values
|
||||||
|
after the key as the English's highest hole `{n}` (`trn`'s count is `{1}`), or it is a **warning**;
|
||||||
|
- **English left** - a template's words outside `translate="no"`, a text attribute's words (`title`,
|
||||||
|
`label`, `hint`, `text`, `caption`, and any other whose value is not a keyword), a quoted choice
|
||||||
|
inside a hole that reads as words, and a `@Text` row whose value is still English - is a
|
||||||
|
**warning**, and `ludic deps` counts them as `english_left`, a ratchet like every number there;
|
||||||
|
- one English under several keys (a split) wants a `#.` description on each, for a translator to
|
||||||
|
tell them apart: a **warning** at the `.po`'s line.
|
||||||
|
|
||||||
|
With no `lang` line, or no source `.po`, nothing is checked, and the first key literal says so once.
|
||||||
|
The source `.po` is gettext's: `msgid` (the key), `msgid_plural` and `msgstr[n]`, `#,` flags
|
||||||
|
(`fuzzy`), `#.` descriptions, `msgctxt` (read, keying nothing), strings over several lines; `#~`
|
||||||
|
entries are obsolete. `ludic schema` carries a `"lang"` object (null without a `lang` line): every
|
||||||
|
key used with its kind (`code`, `template`, `data`), its sites, its English, whether it is a plural,
|
||||||
|
and its description; `unused` (the source's keys used nowhere), `undescribed`, `split`, and per other
|
||||||
|
language in the directory its `missing`, `fuzzy` and `extra` keys.
|
||||||
|
|
||||||
### Default parameters, and calls that name what they change
|
### Default parameters, and calls that name what they change
|
||||||
|
|
||||||
A parameter can have a default, and a call leaves out what it does not change - the last ones when
|
A parameter can have a default, and a call leaves out what it does not change - the last ones when
|
||||||
|
|
@ -1330,6 +1404,7 @@ for a complete title screen.
|
||||||
| `bool` | boolean | `i32` |
|
| `bool` | boolean | `i32` |
|
||||||
| `entity` | entity handle | `i32` |
|
| `entity` | entity handle | `i32` |
|
||||||
| `string` | text (a string literal, an interpolation, a concatenation) | `ptr` |
|
| `string` | text (a string literal, an interpolation, a concatenation) | `ptr` |
|
||||||
|
| `Key` | a text key, `k"pause.resume"` (see Text keys) - not a `string`, and no string is one | `ptr` |
|
||||||
| `pointer` | raw address (runtime/FFI, records, anything untyped) | `ptr` |
|
| `pointer` | raw address (runtime/FFI, records, anything untyped) | `ptr` |
|
||||||
| `byte` | one byte value (what `p[i]` on a `bytes` buffer reads) | `i8` |
|
| `byte` | one byte value (what `p[i]` on a `bytes` buffer reads) | `i8` |
|
||||||
| `bytes` | buffer of bytes — `b[i]` reads/writes one byte | `ptr` |
|
| `bytes` | buffer of bytes — `b[i]` reads/writes one byte | `ptr` |
|
||||||
|
|
|
||||||
13
changes/text-keys.md
Normal file
13
changes/text-keys.md
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
bump: minor
|
||||||
|
type: feature
|
||||||
|
**Text keys: `k"pause.resume"`, a `Key`, checked against `en.po`.** A text the player reads is named by
|
||||||
|
a key of the new builtin type `Key` (`kn"..."` for a plural) - not a string, and no string one; at run
|
||||||
|
time its text after a marker byte, so a runtime's `tr` is a cast. `lang "assets/lang" en` in
|
||||||
|
`package.ludic` names the source language's `.po`, and the compiler holds every key to it: a key
|
||||||
|
literal, a template's `t('key', ...)` and a registry's derived `@Text` key (`<registry>.<row>.<field>`,
|
||||||
|
or `@TextKey("steps")`'s prefix; filled into a `Key` field a row leaves out) must be there, a plural
|
||||||
|
one with a `msgid_plural`; `trf` / `trn` / `t()` given another number of values than the English has
|
||||||
|
holes, text still written in English (a template's words, a `@Text` row's value - counted as
|
||||||
|
`english_left` by `ludic deps`) and one English split into keys without `#.` descriptions are
|
||||||
|
warnings. `ludic schema` gains `"lang"`: every key with its sites, unused and undescribed keys, splits,
|
||||||
|
and each other language's missing, fuzzy and extra keys.
|
||||||
|
|
@ -49,6 +49,7 @@ pack "assets"
|
||||||
| `app out` | where to write the bundle | `build/<name>.app` |
|
| `app out` | where to write the bundle | `build/<name>.app` |
|
||||||
| `pack` | an asset root, repeatable | `assets/` if it exists |
|
| `pack` | an asset root, repeatable | `assets/` if it exists |
|
||||||
| `maps` | where the maps' own tables are, one directory a map (`@PerMap` registries, LANGUAGE.md) - the compiler builds it into the program and `ludicc --check` reads every map under it | `assets/maps` |
|
| `maps` | where the maps' own tables are, one directory a map (`@PerMap` registries, LANGUAGE.md) - the compiler builds it into the program and `ludicc --check` reads every map under it | `assets/maps` |
|
||||||
|
| `lang` | the languages' directory and the source language (`lang "assets/lang" en`): the compiler checks every text key against `<dir>/<source>.po` (LANGUAGE.md, Text keys) | none: no key is checked |
|
||||||
|
|
||||||
Only `app name` is worth setting deliberately. The identifier is derived from the
|
Only `app name` is worth setting deliberately. The identifier is derived from the
|
||||||
package path when you omit it - `git.workshopsoft.io/workshopsoft/maroon-lake`
|
package path when you omit it - `git.workshopsoft.io/workshopsoft/maroon-lake`
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue