examples: text keys - lang/i18n (k"" and kn"" keys, == on them, a @Text Key field's derived key, @TextKey, a component's t('key', ...) and its one line of English left, en.po with a plural and descriptions, tr.po missing, fuzzy and extra) and rejected/i18n_keys (a key en.po lacks, a plural key without msgid_plural, a Key / string mix-up, a derived key en.po lacks, a template's missing t() key, hole counts and an undescribed split); their cases in ludic-dev test (added, not run)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-29 23:38:03 +03:00
parent 4988adb170
commit f9808e31ec
17 changed files with 243 additions and 0 deletions

View file

@ -0,0 +1,3 @@
# i18n.ludic's items: a @Text name is a Key, derived when the row gives none (items.rope.name)
rope { weight: 0.4 }
lamp { name: k"items.lamp.name", weight: 1.2 }

View file

@ -0,0 +1,41 @@
# i18n.ludic — keys, not English (LANGUAGE.md, "Text keys"). A text the player reads is named by a
# key, k"pause.resume", of the builtin type Key: not a string, and no string is one. Its English is
# lang/en.po (package.ludic says `lang "lang" en`), and the compiler checks every key against it. A
# registry's @Text Key field a row leaves out is filled with its derived key - <registry>.<row>.<field>,
# or the registry's @TextKey prefix. At run time a Key is its text after a marker byte (1, or 2 for a
# plural kn"..."), so ludic.i18n's tr() is a cast; here tr, trf and trn are stand-ins that show the key.
#
# Running it prints: pause.resume yes no 13 12 hud.day(3) catch.count[2](trout) items.rope.name items.lamp.name steps.gather_wood.hint
program I18n {
property Item {
key: string = ""
@Text name: Key = null
weight: float = 0.0
}
registry Items of Item as IT from "data/i18n_items.lres"
property StepKind {
key: string = ""
@Text hint: Key = null
}
@TextKey("steps") registry StepKinds of StepKind as SK
def StepKinds gather_wood { }
# ludic.i18n's three, stood in for: the key's text without its marker
function tr(key: Key) -> string {
let s = string(key)
return s[1..len(s)]
}
function trf(key: Key, a: string) -> string { return `{tr(key)}({a})` }
function trn(key: Key, n: int, a: string) -> string { return `{tr(key)}[{n}]({a})` }
function yn(b: bool) -> string {
if b { return "yes" }
return "no"
}
entry {
let resume = k"pause.resume"
let same = resume == k"pause.resume"
let other = resume == k"pause.close"
var out = `{tr(resume)} {yn(same)} {yn(other)} {len(string(resume))} {len(string(kn"catch.count"))} {trf(k"hud.day", "3")}`
out = out + ` {trn(kn"catch.count", 2, "trout")} {tr(Items[IT_ROPE].name)} {tr(Items[IT_LAMP].name)} {tr(StepKinds[SK_GATHER_WOOD].hint)}`
print(out)
}
}

View file

@ -0,0 +1,9 @@
# i18n_ui.ludic — a component's template against lang/en.po: every t('key', ...) in a {hole} names
# a key en.po has, and gives as many values as its English has holes. Words a player would read that
# are not a key are "English left" (a warning, counted by ludic deps as english_left) - here the
# "Paused" line; an element with translate="no" is left alone. `ludicc --check` reports both.
import "ludic.ui"
program I18nUi {
import "parts/Pause.ludic"
entry { print(1) }
}

View file

@ -0,0 +1,40 @@
# The example's source language: the English of every key i18n.ludic and i18n_ui.ludic use.
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. On the pause menu: returns to play.
msgid "pause.resume"
msgstr "Resume"
#. On the pause menu: closes the menu and returns to play.
msgid "pause.close"
msgstr "Close"
#. On the map screen: closes the map.
msgid "map.close"
msgstr "Close"
#. The day on the HUD. {1} = the day number
msgid "hud.day"
msgstr "Day {1}"
#. A catch, counted. {1} = how many, {2} = the species
msgid "catch.count"
msgid_plural "catch.count"
msgstr[0] "{1} {2} caught"
msgstr[1] "{1} {2} "
"caught"
#. An item's name: a coil of rope.
msgid "items.rope.name"
msgstr "Rope"
#. An item's name: a brass lamp.
msgid "items.lamp.name"
msgstr "Lamp"
#. What a chapter step says to do: gather firewood.
msgid "steps.gather_wood.hint"
msgstr "Gather wood"

View file

@ -0,0 +1,32 @@
# Turkish: one key missing (hud.day), one fuzzy (pause.close) and one en.po does not have (old.title)
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
msgid "pause.resume"
msgstr "Devam et"
#, fuzzy
msgid "pause.close"
msgstr "Kapat"
msgid "map.close"
msgstr "Kapat"
msgid "catch.count"
msgid_plural "catch.count"
msgstr[0] "{1} {2} tutuldu"
msgstr[1] "{1} {2} tutuldu"
msgid "items.rope.name"
msgstr "Halat"
msgid "items.lamp.name"
msgstr "Fener"
msgid "steps.gather_wood.hint"
msgstr "Odun topla"
msgid "old.title"
msgstr "Eski"

View file

@ -0,0 +1,3 @@
# the key-based text example (i18n.ludic, i18n_ui.ludic): its languages are lang/<code>.po, and
# lang/en.po is the source every key is checked against
lang "lang" en

View file

@ -0,0 +1,7 @@
# Pause.ludic - i18n_ui.ludic's component: its text is keys (Pause.xml), one of them a field the
# template reads - a Key reaches a template as its marked text, for t(expr)
component Pause {
prop day: int = 1
prop caught: int = 0
back: Key = k"pause.close"
}

View file

@ -0,0 +1,8 @@
<div class="pause">
<p>{t('pause.resume')}</p>
<button title="{t('pause.close')}">{t('hud.day', day)}</button>
<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>