Merge lang/i18nerror 25a57c09: English left is an error under a lang line; ludic.ui's own words are keys; reseeded
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
e2aa928e95
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
|
`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.)
|
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
|
**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` ->
|
`<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
|
`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**;
|
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`,
|
- **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
|
`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
|
inside a hole that reads as words, and a `@Text` row whose value is still English - is an
|
||||||
**warning**, and `ludic deps` counts them as `english_left`, a ratchet like every number there;
|
**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
|
- 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.
|
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.
|
#. What a chapter step says to do: gather firewood.
|
||||||
msgid "steps.gather_wood.hint"
|
msgid "steps.gather_wood.hint"
|
||||||
msgstr "Gather wood"
|
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"
|
msgid "old.title"
|
||||||
msgstr "Eski"
|
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>
|
<button>{t(back)}</button>
|
||||||
<p>{t('catch.count', caught, 'trout')}</p>
|
<p>{t('catch.count', caught, 'trout')}</p>
|
||||||
<p translate="no">Maroon Lake 0.5 <b>beta</b></p>
|
<p translate="no">Maroon Lake 0.5 <b>beta</b></p>
|
||||||
<p>Paused</p>
|
|
||||||
</div>
|
</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
|
# 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 {
|
program DataMissing {
|
||||||
property Thing {
|
property Thing {
|
||||||
key: string = ""
|
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"
|
msgid_plural "catch.count"
|
||||||
msgstr[0] "{1} fish"
|
msgstr[0] "{1} fish"
|
||||||
msgstr[1] "{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
|
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) }
|
if ui_st.ui_be != null and ui_st.ui_be.translate != null { return ui_st.ui_be.translate(s) }
|
||||||
return 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 {
|
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) }
|
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 = "" }
|
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 {
|
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")) }
|
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"))
|
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) }
|
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
|
return true
|
||||||
}
|
}
|
||||||
function ct_mouse_name(ui_st: UiState, v: int) -> string {
|
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_RIGHT { return ui_tk(ui_st, k"ui.right_click", "Right click") }
|
||||||
if v == UI_MOUSE_MIDDLE { return ui_tr(ui_st, "Middle click") }
|
if v == UI_MOUSE_MIDDLE { return ui_tk(ui_st, k"ui.middle_click", "Middle click") }
|
||||||
return ui_tr(ui_st, "Left 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)
|
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 {
|
function i18n_english_left(file: pointer, line: int, col: int, msg: pointer) -> void {
|
||||||
g_i18n_english_left += 1
|
g_i18n_english_left += 1
|
||||||
pwarn_at(file, line, col, `English left: {msg}`)
|
perr_at(file, line, col, `English left: {msg}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
# ---- the whole check -----------------------------------------------------------------------------
|
# ---- 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
|
# phase 26: a template checks clean against en.po, its one line of words a warning, and ludic deps
|
||||||
# counts it as english_left
|
# counts it as english_left
|
||||||
function i18n_check_case() -> void {
|
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`
|
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 }
|
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}`)
|
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`)
|
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)
|
ok(lbl)
|
||||||
}
|
}
|
||||||
# --check --diagnostics=json: refused, with exactly `n` ERRORS on stdout (warnings beside them are not
|
# --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()
|
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)")
|
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", "{\"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", "\"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("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")
|
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_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, "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/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", 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", 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, "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('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")
|
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)")
|
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