Skip to content

Commit ae82d05

Browse files
i18n(plugin-approvals): ship the approval objects, notifications, record page and e-mail link line in ru, de and ko (#30830)
<!-- claude-projects-attribution: {"github_login":"os-elon-musk"} --> _Requested by **Elon** · [project thread](https://claude.ai/code/project/chan_01WedDzV26nniwxv7jC88B9D?thread=cmsg_01WedDzV26nniwxv7jC88B9D7v5H7BaFmL7sUCoN7tSxhm)_ domain:server#1 · os-elon-musk · session_016WV4cya5ej5GQV86RPbRPK Part of #30512 — package 9 of 11, second half (`@objectstack/plugin-approvals` in `ru`, `de`, `ko`; the first half, `zh-Hant`, `ar`, `fr`, `pt-BR`, landed in #30790). With it the package serves all eleven platform languages. The card stays open: settings and platform-objects follow, and the last PR of the series makes the bundle check read the platform locale table. Clause-②: no Metadata-protocol: none ## 维护者速读 - **改了什么:** 审批插件的 3 个对象(审批请求、审批动作、审批委派)的名称、字段、选项、帮助和动作,审批通知的 40 条句子,审批请求记录页的标题和「详情 / 时间线」两个标签,以及审批邮件里「打开审批」那一行,补上俄语、德语、韩语 3 种。繁中、阿语、法语、巴葡已在上半包(#30790)落地,至此本包 11 种语言齐全。 - **为什么改:** 平台语言表要求每个界面带齐 11 种语言;这是第 3 期按包补齐的第 9 个包的下半(每种语言 164 条对象文本 + 40 条消息)。用这 3 种语言的审批人和提交人看到的审批界面、通知和邮件不再是英文。 - **风险与回滚:** 只加翻译数据和注册行,不动逻辑、schema、接口;邮件模板的结构、选模板的规则和 `EMAIL_TEMPLATE_FLOOR_LOCALE` 都没动,只给「打开审批」那一行加了 3 种语言(按裁决 A)。原有 8 种语言一字未改。测试改动只是各语言测试把 3 种新语言加进列表(覆盖变宽,没有放宽任何断言)。回滚即撤 PR。 - **先看:** `src/translations/index.ts`(按表标签注册,zh-CN 在 zh-Hant 前,每种语言带页面标题和消息)、`src/approval-email-templates.ts` 的 `APPROVAL_EMAIL_LINK_LINES`、`src/translations/messages.ts`,再看 `sys-approval-request-page.test.ts`。 - **两分钟验证:** `node scripts/check-i18n-bundles.mjs --filter=approvals` 输出 `plugins/plugin-approvals in sync`;`pnpm --filter @objectstack/plugin-approvals test` 全绿。 - **需要你做的:** 无,席位落地。 ## What changes - Three new locales under the platform locale table's tags (`ru`, `de`, `ko`): each a `TAG.objects.generated.ts` with all 164 object leaves of `sys_approval_request`, `sys_approval_action` and `sys_approval_delegation`, plus its `TAG.source-hashes.generated.ts`, written by the repo's own `os i18n extract --fill=default --source-hashes` and served through `withSourceFallback` like the existing seven. `zh-CN` stays ahead of `zh-Hant` in `index.ts`. - The hand-written records gain an entry per new locale: `messages.ts` (the 40 approval notification sentences; `approvalMessages` widens to all eleven), `pages.ts` (the record page label `sys_approval_request_detail`, recorded against the same `en` digest the existing seven carry), and the record page's inline Details / Timeline labels in `sys-approval-request.page.ts`. - `approval-email-templates.ts`: `APPROVAL_EMAIL_LINK_LINES` (the line that opens the request from its e-mail, `{{approvalUrl}}` kept byte-identical) gains the three locales. This follows the seat's ruling A, as recorded in the handover commit `7db48bdd` and carried by #30790: the link line is added per locale; the e-mail templates' shape, the template selection and `EMAIL_TEMPLATE_FLOOR_LOCALE` are unchanged. - The extract command in `scripts/i18n-extract.config.ts` lists the three tags and its merge baseline imports the three bundles with their page entries, so `check:i18n` covers them from this PR on. ## Tests - `sys-approval-request-page.test.ts` pins the served locale set; it now lists the eleven locales this PR serves, and every served locale is still held to carrying the authored page label. - `approval-status-vocabulary.test.ts`, `approve-decision-question.test.ts`, `decision-question-i18n.test.ts`, `success-copy-names-request-i18n.test.ts` and `admin-copy-vocabulary.test.ts` add the three new bundles to their locale lists. Coverage widens; no assertion changed. ## Acceptance notes - **Quality bar (#17508 / PR #19345):** every leaf written for the reader; placeholders, code spans and identifiers byte-identical, checked mechanically per leaf against `en` for the object bundles and for `messages.ts` (key set, `{{holes}}`, `{holes}`, backtick spans, angle tokens: all three locales OK, zero diffs). - **Leaves equal to English, on purpose, each recorded by the extractor as a source copy:** `de` `Status`, the native word. In `messages.ts`, the reason separator `"; "` equals the English one in all three. - No cross-locale coincidence: `check:i18n-stale-fill` passes with the baseline untouched. - Carried unchanged from the released handover commit `2dffb78d` (session `session_01Utp6Cai5CRdAs4u9gTipWU`): the patch id of this PR's diff equals that commit's (`01aee2a9`). The serial row "after #30208 lands" was cleared by the seat (6672288503): #30208's rows that touch this package landed in PR #30551. - The existing `ja-JP` / `es-ES` bundles still carry English source copies on `sys_approval_delegation`; this PR does not touch the existing locales. - For the platform-objects PRs that follow: the account menu's `nav_account_approvals` label is pinned there to equal this package's `my_pending` label in each locale (the handover's cross-package pin); this PR fixes the `ru` / `de` / `ko` side of it. ## Verification <details><summary>Package suite, typecheck, build (verify lock)</summary> ``` turbo run build --filter=!@objectstack/docs: 118/118 tasks successful, exit 0 pnpm --filter @objectstack/plugin-approvals test && typecheck (at head c582c20, on main c17719d) Test Files 115 passed (115) Tests 1498 passed (1498) check:test-typecheck: OK (tsconfig.test.json) os-verify-lock: VERDICT command-exit 0 same suite at 89c1a860 (the same patch on top of #30790's final head): 115 files, 1498 passed, VERDICT command-exit 0 ``` </details> <details><summary>i18n gates, the derived families and the docs &amp; code guards lane</summary> ``` node scripts/check-i18n-bundles.mjs --filter=approvals (at c582c20) plugins/plugin-approvals in sync (21 bundle(s)) EXIT 0 node scripts/check-i18n-stale-fill.mjs (at c582c20) check-i18n-stale-fill: OK (11 bundle set(s) — no new stale fills, 1 baselined). EXIT 0 CI lane "Lint · docs & code guards", every command run locally (at c582c20) 56 commands (lane-verdict excepted), 56 x exit 0, check-cli-command-ids included dispatch-gates --commands (at 89c1a860): 61 families, each run and recorded with its exit code: 61 x exit 0 dispatch-gates --ran: 61 derived, 61 run, 0 NOT-MEASURED (a derived zero) re-derived at c582c20: the identical 61-command list pnpm lint (repo-wide, at 89c1a860): exit 0 changed lines 1474 (+1464 / -10), 19 files: under the 3000-line human-merge threshold ``` </details> The branch is one commit on `main` `c17719dd` (after #30790 landed). Its diff has the same patch id (`01aee2a9`) as the handover commit and as `89c1a860`, where the derived families ran on top of #30790's final head; none of the main commits in between touches `plugin-approvals`. The package suite, both i18n checks and the docs & code guards lane were re-run on this head. Dogfood: not run — content-only package PR, no `!` in the title and no `(narrowing)` arm. --- _Generated by [Claude Code](https://claude.ai/code/session_016WV4cya5ej5GQV86RPbRPK)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 25e1c1a commit ae82d05

19 files changed

Lines changed: 1464 additions & 10 deletions
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/plugin-approvals': patch
3+
---
4+
5+
i18n(plugin-approvals): the approval objects, notifications, record page and e-mail link line ship in Russian, German and Korean
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
The approval objects (`sys_approval_request`, `sys_approval_action`, `sys_approval_delegation`), the approval notifications' sentences, the approval request record page (its label and its Details / Timeline tabs) and the line that opens an approval from its e-mail now carry `ru`, `de` and `ko` as well, so the package ships all eleven platform languages. Each new locale is registered under its platform-table tag with its source-hash companion, its page entry and its messages, and the package's extract command lists the tags, so the existing `check:i18n` bundle check covers them. The e-mail templates' shape, the template selection and `EMAIL_TEMPLATE_FLOOR_LOCALE` are unchanged; only the link line is added per locale. Second half of package 9 of #30512.

‎packages/plugins/plugin-approvals/scripts/i18n-extract.config.ts‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* strings were seeded from @objectstack/platform-objects.)
99
*
1010
* os i18n extract packages/plugins/plugin-approvals/scripts/i18n-extract.config.ts \
11-
* --locales=zh-CN,ja-JP,es-ES,zh-Hant,ar,fr,pt-BR --fill=default --objects-only --no-metadata-forms \
11+
* --locales=zh-CN,ja-JP,es-ES,zh-Hant,ar,fr,pt-BR,ru,de,ko --fill=default --objects-only --no-metadata-forms \
1212
* --source-hashes \
1313
* --out=packages/plugins/plugin-approvals/src/translations
1414
*
@@ -64,6 +64,9 @@ import { zhHantObjects } from '../src/translations/zh-Hant.objects.generated.js'
6464
import { arObjects } from '../src/translations/ar.objects.generated.js';
6565
import { frObjects } from '../src/translations/fr.objects.generated.js';
6666
import { ptBRObjects } from '../src/translations/pt-BR.objects.generated.js';
67+
import { ruObjects } from '../src/translations/ru.objects.generated.js';
68+
import { deObjects } from '../src/translations/de.objects.generated.js';
69+
import { koObjects } from '../src/translations/ko.objects.generated.js';
6770
import { approvalsPageTranslations } from '../src/translations/pages.js';
6871

6972
/**
@@ -92,6 +95,9 @@ const config: ObjectStackDefinition = defineStack({
9295
{ ar: { objects: arObjects, pages: approvalsPageTranslations['ar'] } },
9396
{ fr: { objects: frObjects, pages: approvalsPageTranslations['fr'] } },
9497
{ 'pt-BR': { objects: ptBRObjects, pages: approvalsPageTranslations['pt-BR'] } },
98+
{ ru: { objects: ruObjects, pages: approvalsPageTranslations['ru'] } },
99+
{ de: { objects: deObjects, pages: approvalsPageTranslations['de'] } },
100+
{ ko: { objects: koObjects, pages: approvalsPageTranslations['ko'] } },
95101
],
96102
});
97103

‎packages/plugins/plugin-approvals/src/admin-copy-vocabulary.test.ts‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ import { zhHantObjects } from './translations/zh-Hant.objects.generated.js';
3333
import { arObjects } from './translations/ar.objects.generated.js';
3434
import { frObjects } from './translations/fr.objects.generated.js';
3535
import { ptBRObjects } from './translations/pt-BR.objects.generated.js';
36+
import { ruObjects } from './translations/ru.objects.generated.js';
37+
import { deObjects } from './translations/de.objects.generated.js';
38+
import { koObjects } from './translations/ko.objects.generated.js';
3639

3740
/** The design-record vocabulary an admin-facing string must not carry. */
3841
const DESIGN_RECORD_VOCABULARY: ReadonlyArray<readonly [string, RegExp]> = [
@@ -97,7 +100,7 @@ function isObjectSchema(v: unknown): v is ObjectLike {
97100
&& !!(v as ObjectLike).fields && typeof (v as ObjectLike).fields === 'object';
98101
}
99102

100-
const OBJECT_BUNDLES = { en: enObjects, 'zh-CN': zhCNObjects, 'ja-JP': jaJPObjects, 'es-ES': esESObjects, 'zh-Hant': zhHantObjects, 'ar': arObjects, 'fr': frObjects, 'pt-BR': ptBRObjects } as const;
103+
const OBJECT_BUNDLES = { en: enObjects, 'zh-CN': zhCNObjects, 'ja-JP': jaJPObjects, 'es-ES': esESObjects, 'zh-Hant': zhHantObjects, 'ar': arObjects, 'fr': frObjects, 'pt-BR': ptBRObjects, 'ru': ruObjects, 'de': deObjects, 'ko': koObjects } as const;
101104

102105
describe('approval objects: admin-facing copy carries no design-record vocabulary', () => {
103106
it('the matcher recognises each kind of vocabulary it exists to keep out', () => {

‎packages/plugins/plugin-approvals/src/approval-email-templates.ts‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export const APPROVAL_EMAIL_CHANNELS: readonly string[] = Object.freeze(['inbox'
5252
* The line that opens the request, per locale the approval catalog carries
5353
* (`translations/messages.ts`). `{{approvalUrl}}` is the payload member.
5454
*/
55-
export const APPROVAL_EMAIL_LINK_LINES: Readonly<Record<'en' | 'zh-CN' | 'ja-JP' | 'es-ES' | 'zh-Hant' | 'ar' | 'fr' | 'pt-BR', string>> = {
55+
export const APPROVAL_EMAIL_LINK_LINES: Readonly<Record<'en' | 'zh-CN' | 'ja-JP' | 'es-ES' | 'zh-Hant' | 'ar' | 'fr' | 'pt-BR' | 'ru' | 'de' | 'ko', string>> = {
5656
en: 'Open the approval: {{approvalUrl}}',
5757
'zh-CN': '打开审批:{{approvalUrl}}',
5858
'ja-JP': '承認を開く: {{approvalUrl}}',
@@ -61,6 +61,9 @@ export const APPROVAL_EMAIL_LINK_LINES: Readonly<Record<'en' | 'zh-CN' | 'ja-JP'
6161
ar: 'افتح الموافقة: {{approvalUrl}}',
6262
fr: 'Ouvrir l’approbation : {{approvalUrl}}',
6363
'pt-BR': 'Abrir a aprovação: {{approvalUrl}}',
64+
ru: 'Открыть согласование: {{approvalUrl}}',
65+
de: 'Genehmigung öffnen: {{approvalUrl}}',
66+
ko: '승인 열기: {{approvalUrl}}',
6467
};
6568

6669
/** The e-mail templates: every emailed topic in every catalog locale. */

‎packages/plugins/plugin-approvals/src/sys-approval-request-page.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ describe('sys_approval_request detail page — its label in the translation bund
308308

309309
it('every served locale carries the label, and a translated locale serves its translation', () => {
310310
const locales = Object.keys(ApprovalsTranslations).sort();
311-
expect(locales).toEqual(['ar', 'en', 'es-ES', 'fr', 'ja-JP', 'pt-BR', 'zh-CN', 'zh-Hant']);
311+
expect(locales).toEqual(['ar', 'de', 'en', 'es-ES', 'fr', 'ja-JP', 'ko', 'pt-BR', 'ru', 'zh-CN', 'zh-Hant']);
312312
for (const locale of locales) {
313313
const served = (ApprovalsTranslations as AnyRec)[locale]?.pages?.[NAME]?.label;
314314
const authored = (approvalsPageTranslations as AnyRec)[locale]?.[NAME]?.label;

‎packages/plugins/plugin-approvals/src/sys-approval-request.page.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,12 @@ export const SysApprovalRequestDetailPage: Page = {
122122
position: 'top',
123123
items: [
124124
{
125-
label: { en: 'Details', 'zh-CN': '详情', 'ja-JP': '詳細', 'es-ES': 'Detalles', 'zh-Hant': '詳細資料', 'ar': 'التفاصيل', 'fr': 'Détails', 'pt-BR': 'Detalhes' },
125+
label: { en: 'Details', 'zh-CN': '详情', 'ja-JP': '詳細', 'es-ES': 'Detalles', 'zh-Hant': '詳細資料', 'ar': 'التفاصيل', 'fr': 'Détails', 'pt-BR': 'Detalhes', 'ru': 'Подробности', 'de': 'Details', 'ko': '세부 정보' },
126126
icon: 'file-text',
127127
children: [{ type: 'record:details' }],
128128
},
129129
{
130-
label: { en: 'Timeline', 'zh-CN': '时间线', 'ja-JP': 'タイムライン', 'es-ES': 'Cronología', 'zh-Hant': '時間軸', 'ar': 'المخطط الزمني', 'fr': 'Chronologie', 'pt-BR': 'Linha do tempo' },
130+
label: { en: 'Timeline', 'zh-CN': '时间线', 'ja-JP': 'タイムライン', 'es-ES': 'Cronología', 'zh-Hant': '時間軸', 'ar': 'المخطط الزمني', 'fr': 'Chronologie', 'pt-BR': 'Linha do tempo', 'ru': 'Хронология', 'de': 'Verlauf', 'ko': '타임라인' },
131131
icon: 'history',
132132
children: [
133133
{
@@ -139,7 +139,7 @@ export const SysApprovalRequestDetailPage: Page = {
139139
sort: [{ field: 'created_at', order: 'desc' }],
140140
limit: 50,
141141
showViewAll: true,
142-
title: { en: 'Timeline', 'zh-CN': '时间线', 'ja-JP': 'タイムライン', 'es-ES': 'Cronología', 'zh-Hant': '時間軸', 'ar': 'المخطط الزمني', 'fr': 'Chronologie', 'pt-BR': 'Linha do tempo' },
142+
title: { en: 'Timeline', 'zh-CN': '时间线', 'ja-JP': 'タイムライン', 'es-ES': 'Cronología', 'zh-Hant': '時間軸', 'ar': 'المخطط الزمني', 'fr': 'Chronologie', 'pt-BR': 'Linha do tempo', 'ru': 'Хронология', 'de': 'Verlauf', 'ko': '타임라인' },
143143
},
144144
},
145145
],

‎packages/plugins/plugin-approvals/src/translations/approval-status-vocabulary.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ describe('approval status vocabulary (#7232)', () => {
115115
// Ratchet for statuses and locales added later: a new entry that reaches a
116116
// bundle un-translated is seeded from the source text, which is the enum
117117
// value itself, so this case catches it without naming it.
118-
for (const locale of ['en', 'zh-CN', 'ja-JP', 'es-ES', 'zh-Hant', 'ar', 'fr', 'pt-BR']) {
118+
for (const locale of ['en', 'zh-CN', 'ja-JP', 'es-ES', 'zh-Hant', 'ar', 'fr', 'pt-BR', 'ru', 'de', 'ko']) {
119119
const labels = resolvedStatusLabels(locale);
120120
expect(Object.keys(labels).sort()).toEqual([...APPROVAL_STATUSES].sort());
121121
const raw = Object.entries(labels)

‎packages/plugins/plugin-approvals/src/translations/approve-decision-question.test.ts‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ import { zhHantObjects } from './zh-Hant.objects.generated.js';
2626
import { arObjects } from './ar.objects.generated.js';
2727
import { frObjects } from './fr.objects.generated.js';
2828
import { ptBRObjects } from './pt-BR.objects.generated.js';
29+
import { ruObjects } from './ru.objects.generated.js';
30+
import { deObjects } from './de.objects.generated.js';
31+
import { koObjects } from './ko.objects.generated.js';
2932
import { SysApprovalRequest } from '../sys-approval-request.object.js';
3033

3134
interface CopyAction {
@@ -53,6 +56,9 @@ const TRANSLATED = [
5356
['ar', arObjects],
5457
['fr', frObjects],
5558
['pt-BR', ptBRObjects],
59+
['ru', ruObjects],
60+
['de', deObjects],
61+
['ko', koObjects],
5662
] as const;
5763

5864
describe('approval_approve — the decision question', () => {

0 commit comments

Comments
 (0)