You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(news): school news in the guardian's language, with the AI notice and the original one click away (#837)
* docs(openspec): news-item-translation, school news in the reader's language with the AI notice
* feat(news): a news item keeps its AI translations next to the original, and the feed reads in the guardian's language
* feat(portal): a news page that shows the AI notice and the original one click away
Copy file name to clipboardExpand all lines: docs/operations/translated-messages.md
+4-1Lines changed: 4 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -17,13 +17,15 @@ Under **Messages** the parent picks a language in **Show messages in**. Each mes
17
17
18
18
The parent's own messages are never translated. **As written** switches translation off.
19
19
20
+
School news follows the same choice. Under **News** each item shows its title, then its text in the parent's language with the same notice and the same **Show the original text** button. The title stays as the school wrote it. The **News** page carries the same language picker, so a parent without conversations can pick a language there.
21
+
20
22
## What you need
21
23
22
24
Translation runs through Hermiq's message translation feature. It arrives switched off. Your data protection officer acknowledges it under **Settings > Algorithm register** in Hermiq, and an administrator enables it. Until then, and on an instance without Hermiq, every message shows as written. Nothing breaks.
23
25
24
26
## What is kept
25
27
26
-
The message keeps its original text. Each translation is stored next to it, once per language, with the language it came from, the model that made it and the notice sentence. A message is translated the first time someone reads it in that language, three messages per page load at most; the rest follow on the next load.
28
+
The message, or news item, keeps its original text. Each translation is stored next to it, once per language, with the language it came from, the model that made it and the notice sentence. A message is translated the first time someone reads it in that language, three messages per page load at most; the rest follow on the next load.
27
29
28
30
## Endpoints
29
31
@@ -32,5 +34,6 @@ The message keeps its original text. Each translation is stored next to it, once
32
34
|`GET`|`/portal/api/identity/details`| The account holder's own details, including `messageLanguage`|
33
35
|`PATCH`|`/portal/api/identity/details`| Set `messageLanguage` to a language tag such as `ar`, or `""` for as written |
34
36
|`GET`|`/api/messages/threads/{id}/messages`| The messages; a translated one carries `translation`|
37
+
|`GET`|`/api/news/feed`| The published news for this parent; a translated item carries `translation`|
35
38
36
39
A `translation` holds `targetLanguage`, `text`, `translatedByAi`, `sourceLanguage`, `sourceLanguageDetected`, `model`, `originalRef`, `disclosure` and `disclosureLanguage`.
Copy file name to clipboardExpand all lines: l10n/en.json
+3-1Lines changed: 3 additions & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -1519,7 +1519,9 @@
1519
1519
"Grant this request?": "Grant this request?",
1520
1520
"{name} can then see the cases of {party}.": "{name} can then see the cases of {party}.",
1521
1521
"Access granted.": "Access granted.",
1522
-
"Request refused.": "Request refused."
1522
+
"Request refused.": "Request refused.",
1523
+
"Plain text / markdown body, as written. An AI translation for a reader's language is kept in translations, never here.": "Plain text / markdown body, as written. An AI translation for a reader's language is kept in translations, never here.",
1524
+
"Server-managed. One entry per language this news item was translated into by AI: the translated text, the source language, the model, a reference to this item and the disclosure sentence. The body stays as written.": "Server-managed. One entry per language this news item was translated into by AI: the translated text, the source language, the model, a reference to this item and the disclosure sentence. The body stays as written."
Copy file name to clipboardExpand all lines: l10n/nl.json
+3-1Lines changed: 3 additions & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -1577,7 +1577,9 @@
1577
1577
"Grant this request?": "Deze aanvraag toekennen?",
1578
1578
"{name} can then see the cases of {party}.": "{name} kan dan de zaken van {party} zien.",
1579
1579
"Access granted.": "Toegang toegekend.",
1580
-
"Request refused.": "Aanvraag geweigerd."
1580
+
"Request refused.": "Aanvraag geweigerd.",
1581
+
"Plain text / markdown body, as written. An AI translation for a reader's language is kept in translations, never here.": "Platte tekst of markdown, zoals geschreven. Een AI-vertaling naar de taal van een lezer staat in de vertalingen, nooit hier.",
1582
+
"Server-managed. One entry per language this news item was translated into by AI: the translated text, the source language, the model, a reference to this item and the disclosure sentence. The body stays as written.": "Beheerd door de server. Eén regel per taal waarin dit nieuwsbericht door AI is vertaald: de vertaalde tekst, de brontaal, het model, een verwijzing naar dit bericht en de vermelding dat AI het vertaalde. De tekst zelf blijft zoals geschreven."
Copy file name to clipboardExpand all lines: lib/Settings/portaliq_mock_register.json
+20-4Lines changed: 20 additions & 4 deletions
Original file line number
Diff line number
Diff line change
@@ -2,7 +2,7 @@
2
2
"openapi": "3.0.0",
3
3
"info": {
4
4
"title": "portaliq demo data",
5
-
"version": "1.0.3",
5
+
"version": "1.0.4",
6
6
"description": "Demo data covering every schema this app supplies, offered as the first step of the app's setup walkthrough. Generated from the schemas themselves, so every object satisfies the schema that will validate it."
7
7
},
8
8
"x-openregister": {
@@ -2505,7 +2505,8 @@
2505
2505
"subjectRef": "Voorbeeld Subjectref 1",
2506
2506
"readAt": "2026-03-01T09:00:00+00:00"
2507
2507
}
2508
-
]
2508
+
],
2509
+
"translations": []
2509
2510
},
2510
2511
{
2511
2512
"@self": {
@@ -2514,7 +2515,7 @@
2514
2515
"slug": "newsitem-voorbeeld-title-2-2"
2515
2516
},
2516
2517
"title": "Voorbeeld Title 2",
2517
-
"body": "Voorbeeld Body 2",
2518
+
"body": "De school is vrijdag gesloten vanwege een studiedag.",
2518
2519
"target": {
2519
2520
"schoolRef": "Voorbeeld Schoolref 2",
2520
2521
"groupRefs": [
@@ -2534,6 +2535,20 @@
2534
2535
"subjectRef": "Voorbeeld Subjectref 2",
2535
2536
"readAt": "2026-03-02T09:00:00+00:00"
2536
2537
}
2538
+
],
2539
+
"translations": [
2540
+
{
2541
+
"targetLanguage": "ar",
2542
+
"text": "المدرسة مغلقة يوم الجمعة بسبب يوم دراسي للمعلمين.",
2543
+
"translatedByAi": true,
2544
+
"sourceLanguage": "nl",
2545
+
"sourceLanguageDetected": true,
2546
+
"model": "nextcloud",
2547
+
"originalRef": "portaliq:newsItem:demo-2",
2548
+
"disclosure": "تُرجم بواسطة الذكاء الاصطناعي. اللغة الأصلية: الهولندية. قد تحتوي هذه الترجمة على أخطاء.",
Copy file name to clipboardExpand all lines: lib/Settings/portaliq_register.json
+13-4Lines changed: 13 additions & 4 deletions
Original file line number
Diff line number
Diff line change
@@ -3,7 +3,7 @@
3
3
"info": {
4
4
"title": "Portaliq Register",
5
5
"description": "Portaliq — the fleet's shared external portal for clients and suppliers (ADR-046). Holds every portal account, page, session, submission, message, notification, audit entry, traffic record, form and landing-page submission the app reads and writes.",
6
-
"version": "0.36.2"
6
+
"version": "0.37.0"
7
7
},
8
8
"x-openregister": {
9
9
"type": "application",
@@ -20,7 +20,7 @@
20
20
"slug": "portaliq",
21
21
"title": "Portaliq Register",
22
22
"description": "Portaliq — the fleet's shared external portal for clients and suppliers (ADR-046). Holds every portal account, page, session, submission, message, notification, audit entry and traffic record the app reads and writes.",
23
-
"version": "0.36.2",
23
+
"version": "0.37.0",
24
24
"schemas": [
25
25
"exampleDocument",
26
26
"glossaryTerm",
@@ -6466,7 +6466,7 @@
6466
6466
"newsItem": {
6467
6467
"slug": "newsItem",
6468
6468
"icon": "FileDocument",
6469
-
"version": "0.1.0",
6469
+
"version": "0.2.0",
6470
6470
"title": "News item",
6471
6471
"authorization": {
6472
6472
"read": [
@@ -6495,7 +6495,7 @@
6495
6495
"body": {
6496
6496
"title": "Body",
6497
6497
"type": "string",
6498
-
"description": "Plain text / markdown body. Not translated by this change (see hermiq's message-translation-delegate).",
6498
+
"description": "Plain text / markdown body, as written. An AI translation for a reader's language is kept in translations, never here.",
6499
6499
"example": "De herfstvakantie loopt van 19 tot 23 oktober."
6500
6500
},
6501
6501
"target": {
@@ -6579,6 +6579,15 @@
6579
6579
}
6580
6580
},
6581
6581
"example": []
6582
+
},
6583
+
"translations": {
6584
+
"title": "Translations",
6585
+
"type": "array",
6586
+
"description": "Server-managed. One entry per language this news item was translated into by AI: the translated text, the source language, the model, a reference to this item and the disclosure sentence. The body stays as written.",
0 commit comments