mirror of
https://github.com/PR0M3TH3AN/VoxVera.git
synced 2026-10-27 09:40:40 +00:00
Flag flyer language in Nostr events
This commit is contained in:
1 parent
5afda54a21
commit
1bda451604
4 files changed
+63
-2
No files matched your search
@@ -61,6 +61,14 @@ Required tags:
|
||||
- `["t", "voxvera"]`
|
||||
- `["t", "flyer"]`
|
||||
|
||||
Recommended language tags:
|
||||
|
||||
- `["language", "<lang>"]`
|
||||
- `["L", "ISO-639-1"]`
|
||||
- `["l", "<lang>", "ISO-639-1"]`
|
||||
|
||||
The payload `lang` field is authoritative. The language tags exist for relay/search metadata and as a fallback for older events whose payload omitted `lang`.
|
||||
|
||||
Payload:
|
||||
|
||||
```json
|
||||
@@ -118,6 +126,9 @@ Nostr editor/viewer controls and Nostr-specific flyer labels come from `NOSTR_UI
|
||||
|
||||
Language changes should:
|
||||
|
||||
- publish the selected flyer language in payload `lang`
|
||||
- publish matching Nostr language tags
|
||||
- infer language from event tags if a fetched payload omits `lang`
|
||||
- update default flyer content while the flyer is still using default content
|
||||
- preserve user-authored or fetched event content
|
||||
- update editor/viewer tool labels
|
||||
|
||||
@@ -751,6 +751,7 @@ Join us in a revolution that values truth and transparency. Together, we can bui
|
||||
|
||||
function buildUnsignedEvent(payload) {
|
||||
const folderName = slugify(payload.folder_name);
|
||||
const lang = supportedLang(payload.lang || FALLBACK_LANG);
|
||||
return {
|
||||
kind: EVENT_KIND,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
@@ -759,7 +760,9 @@ Join us in a revolution that values truth and transparency. Together, we can bui
|
||||
["t", "voxvera"],
|
||||
["t", "flyer"],
|
||||
["title", payload.headline || payload.title || folderName],
|
||||
["language", payload.lang || "en"]
|
||||
["language", lang],
|
||||
["L", "ISO-639-1"],
|
||||
["l", lang, "ISO-639-1"]
|
||||
],
|
||||
content: JSON.stringify(payload)
|
||||
};
|
||||
@@ -1011,10 +1014,21 @@ Join us in a revolution that values truth and transparency. Together, we can bui
|
||||
const hasTag = (name, value) => Array.isArray(event.tags) && event.tags.some((tag) => tag[0] === name && tag[1] === value);
|
||||
if (!hasTag("t", "voxvera") || !hasTag("t", "flyer")) throw new Error("Event is missing VoxVera flyer tags.");
|
||||
const payload = JSON.parse(event.content);
|
||||
const taggedLang = languageFromTags(event.tags);
|
||||
if (!payload.lang && taggedLang) {
|
||||
payload.lang = taggedLang;
|
||||
}
|
||||
validatePayload(payload);
|
||||
return payload;
|
||||
}
|
||||
|
||||
function languageFromTags(tags) {
|
||||
if (!Array.isArray(tags)) return "";
|
||||
const languageTag = tags.find((tag) => Array.isArray(tag) && tag[0] === "language" && tag[1]);
|
||||
const labelTag = tags.find((tag) => Array.isArray(tag) && tag[0] === "l" && tag[1]);
|
||||
return supportedLang((languageTag && languageTag[1]) || (labelTag && labelTag[1]) || "");
|
||||
}
|
||||
|
||||
function makeQrSvg(value) {
|
||||
if (!value) return "";
|
||||
if (typeof qrcode !== "function") {
|
||||
|
||||
@@ -29,7 +29,14 @@ def _event(payload=None, **overrides):
|
||||
event = {
|
||||
"kind": 30078,
|
||||
"content": json.dumps(payload or _payload()),
|
||||
"tags": [["d", "voxvera:nostr-flyer"], ["t", "voxvera"], ["t", "flyer"]],
|
||||
"tags": [
|
||||
["d", "voxvera:nostr-flyer"],
|
||||
["t", "voxvera"],
|
||||
["t", "flyer"],
|
||||
["language", "en"],
|
||||
["L", "ISO-639-1"],
|
||||
["l", "en", "ISO-639-1"],
|
||||
],
|
||||
}
|
||||
event.update(overrides)
|
||||
return event
|
||||
@@ -43,6 +50,21 @@ def test_validate_accepts_nostr_event_payload():
|
||||
assert validate_event_source(_event())["content"] == "This flyer came from a Nostr event."
|
||||
|
||||
|
||||
def test_validate_uses_language_tag_when_payload_lang_is_missing():
|
||||
payload = _payload()
|
||||
payload.pop("lang")
|
||||
event = _event(payload, tags=[
|
||||
["d", "voxvera:nostr-flyer"],
|
||||
["t", "voxvera"],
|
||||
["t", "flyer"],
|
||||
["language", "es"],
|
||||
["L", "ISO-639-1"],
|
||||
["l", "es", "ISO-639-1"],
|
||||
])
|
||||
|
||||
assert validate_event_source(event)["lang"] == "es"
|
||||
|
||||
|
||||
def test_normalize_maps_to_flyer_config_defaults():
|
||||
defaults = {
|
||||
"folder_name": "voxvera",
|
||||
|
||||
@@ -137,6 +137,10 @@ def _extract_payload(source: dict[str, Any]) -> dict[str, Any]:
|
||||
raise NostrValidationError(f"Nostr event content is not valid JSON: {exc.msg}") from exc
|
||||
if not isinstance(payload, dict):
|
||||
raise NostrValidationError("Nostr event content must decode to a JSON object.")
|
||||
if not payload.get("lang"):
|
||||
tagged_lang = _language_from_tags(source.get("tags", []))
|
||||
if tagged_lang:
|
||||
payload["lang"] = tagged_lang
|
||||
return payload
|
||||
|
||||
|
||||
@@ -182,6 +186,16 @@ def _has_tag(source: dict[str, Any], name: str, value: str) -> bool:
|
||||
return any(isinstance(tag, list) and len(tag) >= 2 and tag[0] == name and tag[1] == value for tag in tags)
|
||||
|
||||
|
||||
def _language_from_tags(tags: Any) -> str:
|
||||
if not isinstance(tags, list):
|
||||
return ""
|
||||
for tag_name in ("language", "l"):
|
||||
for tag in tags:
|
||||
if isinstance(tag, list) and len(tag) >= 2 and tag[0] == tag_name:
|
||||
return _normalize_lang(str(tag[1]))
|
||||
return ""
|
||||
|
||||
|
||||
def _normalize_lang(lang: str) -> str:
|
||||
return lang if lang in SUPPORTED_LANGS else "en"
|
||||
|
||||
|
||||
Reference in new issue
Block a user