i18n 26.9, error mode: English left is an error under a lang line (a template's words, a text attribute's, a choice that reads as words, a @Text row's English), ludic deps still counting english_left; hole counts and undescribed splits stay warnings. ludic.ui's own words are keys - ui_tk(ui_st, k"ui.right_click", plain) for the key field's Right / Middle / Left click and press a key..., its plain text for a program with no translator; the examples' en.po / tr.po carry ui.*, i18n_ui checks clean, rejected/i18n_keys/english_left holds the error, data_missing counts 2; LANGUAGE.md and the changeset; on lang/foundations 2288feeb; reseeded, bootstrap-cfree fixpoint holds
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
2288feeb59
commit
25a57c099b
18 changed files with 3667 additions and 3585 deletions
10
LANGUAGE.md
10
LANGUAGE.md
|
|
@ -877,6 +877,12 @@ its text after a marker byte - byte 1, or 2 for `kn"..."` - so `k"pause.resume"`
|
|||
`trn` takes a plural key and `tr` / `trf` refuse one, with or without a `lang` line. (A program that
|
||||
declares its own type named `Key` keeps it; the builtin is then out of reach.)
|
||||
|
||||
**A package's own words** are keys too. ludic.ui's key field says "Right click" as `ui_tk(ui_st,
|
||||
k"ui.right_click", "Right click")`: translated through the program's translator when one is bound, the
|
||||
plain text otherwise (a program with no languages). A program with a `lang` line that imports ludic.ui
|
||||
therefore carries `ui.right_click`, `ui.middle_click`, `ui.left_click` and `ui.press_key` in its
|
||||
source `.po`, and the check says so if it does not.
|
||||
|
||||
**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
|
||||
|
|
@ -914,8 +920,8 @@ each diagnostic at its `file:line:col`:
|
|||
are padding and not counted), 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;
|
||||
inside a hole that reads as words, and a `@Text` row whose value is still English - is an
|
||||
**error** (a warning until the migration was done; `ludic deps` still counts it as `english_left`);
|
||||
- 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.
|
||||
|
||||
|
|
|
|||
8
changes/i18n-error-mode.md
Normal file
8
changes/i18n-error-mode.md
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
bump: minor
|
||||
type: feature
|
||||
**English left is an error (phase 26.9).** Under a `lang` line, a template's own words, a text
|
||||
attribute's, a quoted choice that reads as words and a `@Text` row still holding English now refuse
|
||||
the build, where they were warnings; `ludic deps` still counts them as `english_left`. Hole counts and
|
||||
undescribed splits stay warnings. ludic.ui's own words - the key field's "Right click", "Middle
|
||||
click", "Left click" and "press a key..." - are keys (`ui_tk(ui_st, k"ui.right_click", plain)`,
|
||||
`ui.*` in the program's `.po`), with their plain English for a program that binds no translator.
|
||||
|
|
@ -38,3 +38,19 @@ msgstr "Lamp"
|
|||
#. What a chapter step says to do: gather firewood.
|
||||
msgid "steps.gather_wood.hint"
|
||||
msgstr "Gather wood"
|
||||
|
||||
#. ludic.ui's key field: its value when the binding is the right mouse button.
|
||||
msgid "ui.right_click"
|
||||
msgstr "Right click"
|
||||
|
||||
#. ludic.ui's key field: the middle mouse button.
|
||||
msgid "ui.middle_click"
|
||||
msgstr "Middle click"
|
||||
|
||||
#. ludic.ui's key field: the left mouse button.
|
||||
msgid "ui.left_click"
|
||||
msgstr "Left click"
|
||||
|
||||
#. ludic.ui's key field: listening for the key the player presses next.
|
||||
msgid "ui.press_key"
|
||||
msgstr "press a key..."
|
||||
|
|
|
|||
|
|
@ -30,3 +30,15 @@ msgstr "Odun topla"
|
|||
|
||||
msgid "old.title"
|
||||
msgstr "Eski"
|
||||
|
||||
msgid "ui.right_click"
|
||||
msgstr "Sağ tık"
|
||||
|
||||
msgid "ui.middle_click"
|
||||
msgstr "Orta tık"
|
||||
|
||||
msgid "ui.left_click"
|
||||
msgstr "Sol tık"
|
||||
|
||||
msgid "ui.press_key"
|
||||
msgstr "bir tuşa bas..."
|
||||
|
|
|
|||
|
|
@ -4,5 +4,4 @@
|
|||
<button>{t(back)}</button>
|
||||
<p>{t('catch.count', caught, 'trout')}</p>
|
||||
<p translate="no">Maroon Lake 0.5 <b>beta</b></p>
|
||||
<p>Paused</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# phase 26: a @Text Key field a row leaves out takes its derived key (things.axe.name), and en.po must
|
||||
# have it; a @Text string field still holding English is a warning, counted as english_left
|
||||
# have it; a @Text string field still holding English is an error (26.9), counted as english_left
|
||||
program DataMissing {
|
||||
property Thing {
|
||||
key: string = ""
|
||||
|
|
|
|||
7
examples/rejected/i18n_keys/english_left.ludic
Normal file
7
examples/rejected/i18n_keys/english_left.ludic
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# phase 26.9: English left in a program with a `lang` line is an error - a template's own words, and
|
||||
# a text attribute's, outside translate="no"
|
||||
import "ludic.ui"
|
||||
program EnglishLeft {
|
||||
import "parts/Words.ludic"
|
||||
entry { print(1) }
|
||||
}
|
||||
|
|
@ -22,3 +22,19 @@ msgid "catch.count"
|
|||
msgid_plural "catch.count"
|
||||
msgstr[0] "{1} fish"
|
||||
msgstr[1] "{1} fish"
|
||||
|
||||
#. ludic.ui's key field: its value when the binding is the right mouse button.
|
||||
msgid "ui.right_click"
|
||||
msgstr "Right click"
|
||||
|
||||
#. ludic.ui's key field: the middle mouse button.
|
||||
msgid "ui.middle_click"
|
||||
msgstr "Middle click"
|
||||
|
||||
#. ludic.ui's key field: the left mouse button.
|
||||
msgid "ui.left_click"
|
||||
msgstr "Left click"
|
||||
|
||||
#. ludic.ui's key field: listening for the key the player presses next.
|
||||
msgid "ui.press_key"
|
||||
msgstr "press a key..."
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
# the key checks' refusals (key_missing, t_missing, key_mixup, data_missing, key_hole, key_rules): lang/en.po is the source
|
||||
# the key checks' refusals (key_missing, t_missing, key_mixup, data_missing, key_hole, key_rules, english_left): lang/en.po is the source
|
||||
lang "lang" en
|
||||
|
|
|
|||
4
examples/rejected/i18n_keys/parts/Words.ludic
Normal file
4
examples/rejected/i18n_keys/parts/Words.ludic
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Words.ludic - english_left.ludic's component: words where keys belong
|
||||
component Words {
|
||||
prop day: int = 1
|
||||
}
|
||||
6
examples/rejected/i18n_keys/parts/Words.xml
Normal file
6
examples/rejected/i18n_keys/parts/Words.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<div class="words">
|
||||
<p>{t('pause.resume')}</p>
|
||||
<p>Paused</p>
|
||||
<button title="Close it">{t('hud.day', day)}</button>
|
||||
<p translate="no">Maroon Lake 0.5</p>
|
||||
</div>
|
||||
|
|
@ -236,6 +236,12 @@ function ui_tr(ui_st: UiState, s: string) -> string {
|
|||
if ui_st.ui_be != null and ui_st.ui_be.translate != null { return ui_st.ui_be.translate(s) }
|
||||
return s
|
||||
}
|
||||
# ludic.ui's own words by key (ui.* in the program's en.po); a program with no translator bound has
|
||||
# no languages, and draws `plain`
|
||||
function ui_tk(ui_st: UiState, k: Key, plain: string) -> string {
|
||||
if ui_st.tr_no or ui_st.ui_be == null or ui_st.ui_be.translate == null { return plain }
|
||||
return ui_st.ui_be.translate(string(k))
|
||||
}
|
||||
function ui_sound(ui_st: UiState, name: string) -> void {
|
||||
if ui_st.ui_be != null and ui_st.ui_be.sound != null { ui_st.ui_be.sound(name) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ function ct_key_pressed_away(ui_st: mut UiState, hit: UiNode) -> void {
|
|||
if ui_st.ct_capture != "" and (hit == null or hit.key != ui_st.ct_capture) { ui_st.ct_capture = "" }
|
||||
}
|
||||
function ct_key_text(ui_st: UiState, n: UiNode) -> string {
|
||||
if ui_st.ct_capture == n.key { return ui_tr(ui_st, "press a key...") }
|
||||
if ui_st.ct_capture == n.key { return ui_tk(ui_st, k"ui.press_key", "press a key...") }
|
||||
if Value.kind(ui_attr(n, "shown")) != 0 { return ui_tr(ui_st, ui_attr_text(n, "shown")) }
|
||||
let v = Value.as_int(ui_attr(n, "value"))
|
||||
if v >= UI_MOUSE_LEFT and v <= UI_MOUSE_MIDDLE { return ct_mouse_name(ui_st, v) }
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ function ct_key_mouse(ui_st: mut UiState, root: UiNode) -> bool {
|
|||
return true
|
||||
}
|
||||
function ct_mouse_name(ui_st: UiState, v: int) -> string {
|
||||
if v == UI_MOUSE_RIGHT { return ui_tr(ui_st, "Right click") }
|
||||
if v == UI_MOUSE_MIDDLE { return ui_tr(ui_st, "Middle click") }
|
||||
return ui_tr(ui_st, "Left click")
|
||||
if v == UI_MOUSE_RIGHT { return ui_tk(ui_st, k"ui.right_click", "Right click") }
|
||||
if v == UI_MOUSE_MIDDLE { return ui_tk(ui_st, k"ui.middle_click", "Middle click") }
|
||||
return ui_tk(ui_st, k"ui.left_click", "Left click")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -325,9 +325,10 @@ function i18n_short(s: pointer) -> pointer {
|
|||
}
|
||||
return buf_str(b)
|
||||
}
|
||||
# 26.9: an error - a program with a `lang` line has no English of its own left to draw
|
||||
function i18n_english_left(file: pointer, line: int, col: int, msg: pointer) -> void {
|
||||
g_i18n_english_left += 1
|
||||
pwarn_at(file, line, col, `English left: {msg}`)
|
||||
perr_at(file, line, col, `English left: {msg}`)
|
||||
}
|
||||
|
||||
# ---- the whole check -----------------------------------------------------------------------------
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
|
|
@ -77,13 +77,13 @@ function permap_check_case() -> void {
|
|||
# phase 26: a template checks clean against en.po, its one line of words a warning, and ludic deps
|
||||
# counts it as english_left
|
||||
function i18n_check_case() -> void {
|
||||
let lbl = "--check: a component's template against en.po - clean, \"Paused\" English left (a warning), english_left 1 in ludic deps"
|
||||
let lbl = "--check: a component's template against en.po - clean, english_left 0 in ludic deps"
|
||||
let js = `{tmp_dir()}/dj_i18n.json`
|
||||
if not shq(`bin/ludicc examples/lang/i18n/i18n_ui.ludic --check --diagnostics=json > {js} 2>/dev/null`) { bad2(lbl, capture_line(`head -3 {js}`)); return }
|
||||
let got = capture(`cat {js}`)
|
||||
if not s_contains(got, "English left: a template's words") or not s_contains(got, "Paused") { bad2(lbl, capture_line(`head -3 {js}`)); return }
|
||||
if s_contains(got, "\"error\"") { bad2(lbl, capture_line(`head -3 {js}`)); return }
|
||||
let dp = capture(`bin/ludic deps examples/lang/i18n/i18n_ui.ludic 2>&1`)
|
||||
if not s_contains(dp, "english_left: 1") { bad2(lbl, `ludic deps said [{s_trim(dp)}]`); return }
|
||||
if not s_contains(dp, "english_left: 0") { bad2(lbl, `ludic deps said [{s_trim(dp)}]`); return }
|
||||
ok(lbl)
|
||||
}
|
||||
# --check --diagnostics=json: refused, with exactly `n` ERRORS on stdout (warnings beside them are not
|
||||
|
|
@ -1354,7 +1354,7 @@ function cmd_dev_test() -> int {
|
|||
i18n_check_case()
|
||||
feat_case("lang/i18n_nested/i18n_nested", "", "raids.camp.arrive.steps.0.text raids.camp.arrive.steps.1.text 2 raids.camp.said.1 0 raids.quiet.arrive.steps.0.text hud.day(3)", "i18n_nested.ludic (phase 26: derived keys below a row - a nested record's, a list item's, a @Text []Key filled from en.po; null is no text; trf's trailing \"\" not counted)")
|
||||
schema_case("lang/i18n/i18n", "{\"key\": \"items.rope.name\", \"kind\": \"data\", \"kinds\": [\"data\"], \"sites\": [\"examples/lang/i18n/data/i18n_items.lres:2:1\"], \"in_source\": true, \"english\": \"Rope\"", "--emit-schema: lang.keys - a derived key, its kind, its site and en.po's English")
|
||||
schema_case("lang/i18n/i18n", "\"unused\": [\"map.close\"]", "--emit-schema: lang.unused - en.po's keys the program uses nowhere")
|
||||
schema_case("lang/i18n/i18n", "\"unused\": [\"map.close\", \"ui.left_click\", \"ui.middle_click\", \"ui.press_key\", \"ui.right_click\"]", "--emit-schema: lang.unused - en.po's keys the program uses nowhere")
|
||||
schema_case("lang/i18n/i18n", "\"missing\": [\"hud.day\"], \"fuzzy\": [\"pause.close\"], \"extra\": [\"old.title\"]", "--emit-schema: lang.languages - another .po's missing, fuzzy and extra keys against en.po")
|
||||
schema_case("lang/i18n/i18n", "{\"english\": \"Close\", \"keys\": [\"map.close\", \"pause.close\"]}", "--emit-schema: lang.split - one English under several keys")
|
||||
schema_case("actions/modules", "{\"state\": \"Bag\", \"action\": \"PickUp\", \"module\": \"bag\", \"at\": \"examples/actions/mods/bag/index.ludic:4:16\"}", "--emit-schema: code.reducers - a reducer's state, action, module and place")
|
||||
|
|
@ -1387,8 +1387,9 @@ function cmd_dev_test() -> int {
|
|||
diag_json_case("rejected/i18n_keys/key_hole", 2, "a Key in a template's {hole}", "a Key in a template literal's hole is refused: it would draw its marked name, untranslated")
|
||||
diag_json_case("rejected/i18n_keys/key_rules", 3, "trn counts, so its key is a plural one", "trn wants a plural key, kn\"...\"")
|
||||
diag_json_case("rejected/i18n_keys/key_rules", 3, "a plural key needs its count", "tr / trf refuse a plural key")
|
||||
diag_json_case("rejected/i18n_keys/data_missing", 1, "things.axe.name (the derived key of a @Text field the row gives no value): the key things.axe.name is not in", "a @Text Key field a row leaves out: its derived key must be in en.po")
|
||||
diag_json_case("rejected/i18n_keys/data_missing", 1, "English left: things.axe.blurb is", "a @Text string field still holding English: a warning")
|
||||
diag_json_case("rejected/i18n_keys/data_missing", 2, "things.axe.name (the derived key of a @Text field the row gives no value): the key things.axe.name is not in", "a @Text Key field a row leaves out: its derived key must be in en.po")
|
||||
diag_json_case("rejected/i18n_keys/data_missing", 2, "English left: things.axe.blurb is", "a @Text string field still holding English: an error (26.9)")
|
||||
diag_json_case("rejected/i18n_keys/english_left", 2, "English left: a template's words (\\\"Paused\\\")", "a template's words outside translate=\"no\": an error (26.9)")
|
||||
diag_json_case("rejected/i18n_keys/t_missing", 1, "t('menu.gone'): the key menu.gone is not in", "a template's t('key') is checked against en.po")
|
||||
diag_json_case("rejected/i18n_keys/t_missing", 1, "t(): hud.day is given 0 value(s)", "a template's t('key') given fewer values than the English has holes: a warning")
|
||||
feat_case("lang/defaults", "", "a 300.0 8.0 / b 120.0 8.0 / c! 300.0 8.0 / d 300.0 2.0 / x / y", "defaults.ludic (L11: default parameters, positional then named arguments, in a generic too)")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue