openyida 2026.5.18 → 2026.5.19-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -8
- package/bin/yida.js +23 -4
- package/lib/app/create-app.js +18 -4
- package/lib/app/create-form.js +48 -37
- package/lib/app/create-page.js +24 -10
- package/lib/app/import-app.js +7 -5
- package/lib/app/page-linter.js +6 -1
- package/lib/app/update-app.js +7 -9
- package/lib/app/update-form-config.js +19 -9
- package/lib/auth/codex-login.js +2 -1
- package/lib/core/command-manifest.js +7 -4
- package/lib/core/env.js +1 -0
- package/lib/core/locales/ar.js +2 -0
- package/lib/core/locales/de.js +2 -0
- package/lib/core/locales/en.js +2 -0
- package/lib/core/locales/es.js +2 -0
- package/lib/core/locales/fr.js +2 -0
- package/lib/core/locales/hi.js +2 -0
- package/lib/core/locales/ja.js +2 -0
- package/lib/core/locales/ko.js +2 -0
- package/lib/core/locales/pt.js +2 -0
- package/lib/core/locales/vi.js +2 -0
- package/lib/core/locales/zh-HK.js +2 -0
- package/lib/core/locales/zh.js +2 -0
- package/lib/core/utils.js +15 -1
- package/lib/core/yida-i18n.js +129 -0
- package/lib/flash-note/build-flash-note-prompt.js +89 -17
- package/lib/i18n-management/i18n-management.js +783 -0
- package/lib/process/configure-process.js +31 -27
- package/lib/report/http.js +2 -1
- package/lib/samples/yida-custom-page/custom-page-template.js +242 -9
- package/package.json +1 -1
- package/scripts/e2e-real/skill-coverage.js +2 -0
- package/yida-skills/SKILL.md +16 -0
- package/yida-skills/references/formula-functions.md +11 -4
- package/yida-skills/skills/yida-app/SKILL.md +61 -7
- package/yida-skills/skills/yida-business-rule/SKILL.md +135 -0
- package/yida-skills/skills/yida-custom-page/SKILL.md +6 -1
- package/yida-skills/skills/yida-custom-page/references/assets-guide.md +4 -4
- package/yida-skills/skills/yida-custom-page/references/coding-guide.md +112 -4
- package/yida-skills/skills/yida-custom-page/references/component-jsx-guide.md +151 -23
- package/yida-skills/skills/yida-custom-page/references/design-system.md +2 -2
- package/yida-skills/skills/yida-flash-note-to-prd/SKILL.md +18 -3
- package/yida-skills/skills/yida-flash-note-to-prd/references/examples.md +51 -6
- package/yida-skills/skills/yida-flash-note-to-prd/references/flash-note-prd-template.md +66 -7
- package/yida-skills/skills/yida-flash-note-to-prd/references/flash-note-prompt.md +44 -9
- package/yida-skills/skills/yida-flash-note-to-prd/references/yida-field-types.md +4 -1
- package/yida-skills/skills/yida-formula/SKILL.md +18 -15
- package/yida-skills/skills/yida-i18n/SKILL.md +150 -0
package/README.md
CHANGED
|
@@ -69,7 +69,7 @@ OpenYida detects the active agent environment, workspace path, login state, and
|
|
|
69
69
|
openyida login
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
-
In Codex, Qoder, Wukong, Claude Code, OpenCode, Cursor, and other detected AI tools, OpenYida first tries local Chrome/Edge/Chromium CDP when no valid cached login exists. If local CDP is unavailable, it falls back to an AI-dialog QR handoff. The agent should render `qr_image_markdown` or paste `agent_response_markdown` directly in the conversation so the QR code is visible, then run `poll_command` after the user scans it with DingTalk. If image rendering is unavailable, fall back to `qr_url`. The explicit `openyida login --browser` command still prefers CDP first and uses Playwright as an optional browser fallback.
|
|
72
|
+
In Codex, QoderWork, Qoder, Wukong, Claude Code, OpenCode, Cursor, and other detected AI tools, OpenYida first tries local Chrome/Edge/Chromium CDP when no valid cached login exists. If local CDP is unavailable, it falls back to an AI-dialog QR handoff. The agent should render `qr_image_markdown` or paste `agent_response_markdown` directly in the conversation so the QR code is visible, then run `poll_command` after the user scans it with DingTalk. If image rendering is unavailable, fall back to `qr_url`. The explicit `openyida login --browser` command still prefers CDP first and uses Playwright as an optional browser fallback.
|
|
73
73
|
|
|
74
74
|
The explicit QR polling command remains available:
|
|
75
75
|
|
|
@@ -97,7 +97,7 @@ Build an IPD workflow for chip production, including approval nodes and dashboar
|
|
|
97
97
|
Generate a public landing page and publish it to my Yida app.
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
-
The agent can then call OpenYida commands to create the application, generate source files, publish pages, and return the final Yida URLs. In Codex, Qoder, and Wukong environments, successful creation and publish commands also include a browser handoff so the agent can open the resulting Yida page in the in-app browser. Use `--open` to force this handoff or `--no-open` to suppress it.
|
|
100
|
+
The agent can then call OpenYida commands to create the application, generate source files, publish pages, and return the final Yida URLs. In Codex, QoderWork, Qoder, and Wukong environments, successful creation and publish commands also include a browser handoff so the agent can open the resulting Yida page in the in-app browser. Use `--open` to force this handoff or `--no-open` to suppress it.
|
|
101
101
|
|
|
102
102
|
## Wukong Installation
|
|
103
103
|
|
|
@@ -123,6 +123,7 @@ export PATH="$HOME/.real/.bin/node/bin:$PATH"
|
|
|
123
123
|
| [OpenCode](https://opencode.ai) | Full support |
|
|
124
124
|
| [Cursor](https://cursor.com/) | Full support |
|
|
125
125
|
| [Visual Studio Code](https://code.visualstudio.com/) | Full support |
|
|
126
|
+
| [QoderWork](https://qoder.com) | Full support |
|
|
126
127
|
| [Qoder](https://qoder.com) | Full support |
|
|
127
128
|
| [Wukong](https://dingtalk.com/wukong) | Full support |
|
|
128
129
|
|
|
@@ -266,7 +267,9 @@ Run `openyida --help` or `openyida <command> --help` for detailed usage.
|
|
|
266
267
|
| `openyida org list` | List accessible organizations |
|
|
267
268
|
| `openyida org switch --corp-id <corpId>` | Switch organization without logging in again |
|
|
268
269
|
|
|
269
|
-
Environment selectors such as `--env intl`, `--intl`, `--overseas`, `--global`, and `--yidaapps` can be used on login-required commands to choose the target Yida environment for that run. The `intl` preset
|
|
270
|
+
Environment selectors such as `--env intl`, `--intl`, `--overseas`, `--global`, and `--yidaapps` can be used on login-required commands to choose the target Yida environment for that run. The `intl` preset uses `https://www.yidaapps.com` as the built-in Global YiDA entrypoint (not the bare `https://yidaapps.com` domain) and DingTalk International OAuth at `https://login.dingtalk.io`; business API requests still use the authenticated environment `baseUrl`, so customer custom subdomains are supported.
|
|
271
|
+
|
|
272
|
+
For overseas apps, pass `--locale en_US` or `--locale ja_JP` on creation commands, or set `OPENYIDA_CONTENT_LOCALE`. OpenYida writes YiDA resource names with `zh_CN`, `en_US`, and `ja_JP` values so Global YiDA does not fall back to Chinese-only metadata.
|
|
270
273
|
|
|
271
274
|
### Applications
|
|
272
275
|
|
|
@@ -274,9 +277,10 @@ Environment selectors such as `--env intl`, `--intl`, `--overseas`, `--global`,
|
|
|
274
277
|
|---------|-------------|
|
|
275
278
|
| `openyida app-list [--size N]` | List Yida applications |
|
|
276
279
|
| `openyida corp-efficiency [overview\|details\|detail\|groups\|notify] [options] [--open\|--no-open]` | Query enterprise efficiency metrics, detail report entries, and related notification actions |
|
|
277
|
-
| `openyida create-app "<name>"\|--name <name> [options] [--open\|--no-open]` | Create an application and output `appType` |
|
|
280
|
+
| `openyida create-app "<name>"\|--name <name> [options] [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Create an application and output `appType` |
|
|
278
281
|
| `openyida update-app <appType> --name "..."` | Update application metadata |
|
|
279
282
|
| `openyida app-permission <get\|set\|add\|remove\|search-user> ...` | Manage app primary admins, data admins, and developer members |
|
|
283
|
+
| `openyida i18n <overview\|config\|languages\|list\|upsert\|delete\|translate\|translate-all\|upgrade> <appType> ...` | Manage app multilingual copy and language configuration |
|
|
280
284
|
| `openyida export <appType> [output]` | Export an application migration package |
|
|
281
285
|
| `openyida import <file> [name]` | Import a migration package into a target environment |
|
|
282
286
|
|
|
@@ -284,18 +288,18 @@ Environment selectors such as `--env intl`, `--intl`, `--overseas`, `--global`,
|
|
|
284
288
|
|
|
285
289
|
| Command | Description |
|
|
286
290
|
|---------|-------------|
|
|
287
|
-
| `openyida create-form create <appType> "<name>" <fields.json> [--open\|--no-open]` | Create a form page |
|
|
288
|
-
| `openyida create-form update <appType> <formUuid> <changes.json> [--open\|--no-open]` | Update a form page |
|
|
291
|
+
| `openyida create-form create <appType> "<name>" <fields.json> [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Create a form page |
|
|
292
|
+
| `openyida create-form update <appType> <formUuid> <changes.json> [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Update a form page |
|
|
289
293
|
| `openyida create-form add-option <appType> <formUuid> <fieldLabel> <option1> [option2] ...` | Append options to a SelectField/RadioField/CheckboxField/MultiSelectField |
|
|
290
294
|
| `openyida list-forms <appType> [--keyword <text>]` | List forms in an application |
|
|
291
295
|
| `openyida get-schema <appType> <formUuid\|--all> [--field <labelOrFieldId>]` | Fetch one form schema, batch export all, or pick a single field's full props |
|
|
292
|
-
| `openyida create-page <appType> "<name>" [--mode dashboard] [--open\|--no-open]` | Create a custom display page; dashboard mode hides top/workbench chrome |
|
|
296
|
+
| `openyida create-page <appType> "<name>" [--mode dashboard] [--locale zh_CN\|en_US\|ja_JP] [--open\|--no-open]` | Create a custom display page; dashboard mode hides top/workbench chrome |
|
|
293
297
|
| `openyida generate-page <template> [--spec file]` | Generate custom page source from templates (`product-homepage`, `todo-mvc`) |
|
|
294
298
|
| `openyida build-page <sourceFile> [--output file\|--write]` | Build/fix Yida-compatible page source from OpenYida authoring JSX |
|
|
295
299
|
| `openyida check-page <sourceFile> [--compat] [--json]` | Check page compatibility; `.oyd.jsx` is compatibility-built before linting |
|
|
296
300
|
| `openyida compile <sourceFile> [--compat]` | Compile a custom page locally; `.oyd.jsx` sources are compatibility-built first |
|
|
297
301
|
| `openyida publish <sourceFile> <appType> <formUuid> [--compat] [--health-check] [--force] [--open\|--no-open]` | Compile and publish a custom display page; by default the target must be `formType=display` |
|
|
298
|
-
| `openyida update-form-config <appType> <formUuid> <isRenderNav> <title
|
|
302
|
+
| `openyida update-form-config <appType> <formUuid> <isRenderNav> <title> [--locale zh_CN\|en_US\|ja_JP]` | Update page/form display configuration |
|
|
299
303
|
|
|
300
304
|
### Data, Permissions, and Sharing
|
|
301
305
|
|
package/bin/yida.js
CHANGED
|
@@ -25,13 +25,16 @@ function isAgentEnvironment(env) {
|
|
|
25
25
|
env.CODEX_THREAD_ID ||
|
|
26
26
|
env.CODEX_HOME ||
|
|
27
27
|
env.CLAUDE_CODE ||
|
|
28
|
+
env.CLAUDE_CODE_ENTRYPOINT ||
|
|
28
29
|
env.OPENCODE ||
|
|
29
30
|
env.QODER_IDE ||
|
|
30
31
|
env.QODER_AGENT ||
|
|
32
|
+
env.QODERCLI_INTEGRATION_MODE ||
|
|
31
33
|
env.CURSOR_TRACE_ID ||
|
|
32
34
|
env.AGENT_WORK_ROOT ||
|
|
33
35
|
env.OPENYIDA_AGENT_MODE ||
|
|
34
|
-
(env.__CFBundleIdentifier || '').toLowerCase().includes('codex')
|
|
36
|
+
(env.__CFBundleIdentifier || '').toLowerCase().includes('codex') ||
|
|
37
|
+
(env.__CFBundleIdentifier || '').toLowerCase().includes('qoder')
|
|
35
38
|
);
|
|
36
39
|
}
|
|
37
40
|
|
|
@@ -438,9 +441,19 @@ async function main() {
|
|
|
438
441
|
if (browserResult) {
|
|
439
442
|
printLoginResult(browserResult);
|
|
440
443
|
} else {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
+
// CDP/Playwright 失败后的兜底策略:
|
|
445
|
+
// QoderWork 有 in-app browser,优先使用 browser handoff;其余走终端二维码
|
|
446
|
+
const { detectActiveTool } = require('../lib/core/utils');
|
|
447
|
+
const activeTool = detectActiveTool();
|
|
448
|
+
if (activeTool && activeTool.tool === 'qoderwork') {
|
|
449
|
+
const { codexLogin } = require('../lib/auth/codex-login');
|
|
450
|
+
const result = await codexLogin({ tool: 'qoderwork' });
|
|
451
|
+
printLoginResult(result);
|
|
452
|
+
} else {
|
|
453
|
+
const { startCodexQrLogin } = require('../lib/auth/qr-login');
|
|
454
|
+
const result = await startCodexQrLogin({ corpId: getArgValue(loginArgs, '--corp-id') });
|
|
455
|
+
printLoginResult(result);
|
|
456
|
+
}
|
|
444
457
|
}
|
|
445
458
|
}
|
|
446
459
|
} else if (shouldUseBrowserHandoffLogin(loginArgs)) {
|
|
@@ -698,6 +711,12 @@ async function main() {
|
|
|
698
711
|
break;
|
|
699
712
|
}
|
|
700
713
|
|
|
714
|
+
case 'i18n': {
|
|
715
|
+
const { run: runI18nManagement } = require('../lib/i18n-management/i18n-management');
|
|
716
|
+
await runI18nManagement(args);
|
|
717
|
+
break;
|
|
718
|
+
}
|
|
719
|
+
|
|
701
720
|
case 'data': {
|
|
702
721
|
if (args.length < 2) {
|
|
703
722
|
warn('用法: openyida data <action> <resource> [args] [options]');
|
package/lib/app/create-app.js
CHANGED
|
@@ -17,6 +17,7 @@ const {
|
|
|
17
17
|
requestWithAutoLogin,
|
|
18
18
|
} = require('../core/utils');
|
|
19
19
|
const { t } = require('../core/i18n');
|
|
20
|
+
const { buildYidaI18n, normalizeYidaLocale, resolveContentLocale } = require('../core/yida-i18n');
|
|
20
21
|
const { parseOpenOption, withBrowserHandoff } = require('../core/browser-handoff');
|
|
21
22
|
|
|
22
23
|
const DEFAULT_APP_OPTIONS = {
|
|
@@ -111,6 +112,7 @@ function parseCreateAppArgs(args) {
|
|
|
111
112
|
colour: null,
|
|
112
113
|
navTheme: null,
|
|
113
114
|
layoutDirection: null,
|
|
115
|
+
locale: null,
|
|
114
116
|
};
|
|
115
117
|
const positional = [];
|
|
116
118
|
|
|
@@ -154,6 +156,15 @@ function parseCreateAppArgs(args) {
|
|
|
154
156
|
params.layoutDirection = readOptionValue(args, index, arg);
|
|
155
157
|
index++;
|
|
156
158
|
break;
|
|
159
|
+
case '--locale':
|
|
160
|
+
case '--content-locale':
|
|
161
|
+
case '--lang':
|
|
162
|
+
params.locale = readOptionValue(args, index, arg);
|
|
163
|
+
if (!normalizeYidaLocale(params.locale)) {
|
|
164
|
+
throw new Error(`Unsupported locale: ${params.locale}`);
|
|
165
|
+
}
|
|
166
|
+
index++;
|
|
167
|
+
break;
|
|
157
168
|
default:
|
|
158
169
|
if (arg.startsWith('-')) {
|
|
159
170
|
throw new Error(`Unknown option: ${arg}`);
|
|
@@ -211,7 +222,7 @@ async function run(args) {
|
|
|
211
222
|
|
|
212
223
|
const { appName, description, icon, iconColor, colour, navTheme, layoutDirection } = params;
|
|
213
224
|
|
|
214
|
-
const { c, banner, step, label, info, success: chalkSuccess,
|
|
225
|
+
const { c, banner, step, label, info, success: chalkSuccess, result: chalkResult } = require('../core/chalk');
|
|
215
226
|
|
|
216
227
|
banner(t('create_app.title'));
|
|
217
228
|
label('Name', appName);
|
|
@@ -235,6 +246,9 @@ async function run(args) {
|
|
|
235
246
|
};
|
|
236
247
|
chalkSuccess(t('common.login_ready', authRef.baseUrl));
|
|
237
248
|
|
|
249
|
+
const contentLocale = resolveContentLocale({ locale: params.locale, baseUrl: authRef.baseUrl });
|
|
250
|
+
label('Locale', contentLocale);
|
|
251
|
+
|
|
238
252
|
// Step 2: 创建应用
|
|
239
253
|
step(2, t('create_app.step_create'));
|
|
240
254
|
|
|
@@ -261,14 +275,14 @@ async function run(args) {
|
|
|
261
275
|
const response = await requestWithAutoLogin((auth) => {
|
|
262
276
|
const postData = querystring.stringify({
|
|
263
277
|
_csrf_token: auth.csrfToken,
|
|
264
|
-
appName: JSON.stringify(
|
|
265
|
-
description: JSON.stringify(
|
|
278
|
+
appName: JSON.stringify(buildYidaI18n(appName, { en_US: appName, ja_JP: appName })),
|
|
279
|
+
description: JSON.stringify(buildYidaI18n(description, { en_US: description, ja_JP: description })),
|
|
266
280
|
icon: iconValue,
|
|
267
281
|
iconUrl: iconValue,
|
|
268
282
|
colour,
|
|
269
283
|
navTheme,
|
|
270
284
|
layoutDirection,
|
|
271
|
-
defaultLanguage:
|
|
285
|
+
defaultLanguage: contentLocale,
|
|
272
286
|
openExclusive: openExclusive,
|
|
273
287
|
openPhysicColumn: openPhysicColumn,
|
|
274
288
|
openIsolationDatabase: 'n',
|
package/lib/app/create-form.js
CHANGED
|
@@ -63,6 +63,7 @@ const path = require('path');
|
|
|
63
63
|
const querystring = require('querystring');
|
|
64
64
|
const { loadCookieData, triggerLogin, refreshCsrfToken, resolveBaseUrl, isLoginExpired, isCsrfTokenExpired } = require('../core/utils');
|
|
65
65
|
const { t } = require('../core/i18n');
|
|
66
|
+
const { buildYidaI18n, normalizeYidaLocale, resolveContentLocale } = require('../core/yida-i18n');
|
|
66
67
|
const { banner, step, label, success, fail, warn, info, error, result, usage, hint, listItem } = require('../core/chalk');
|
|
67
68
|
const { parseOpenOption, withBrowserHandoff } = require('../core/browser-handoff');
|
|
68
69
|
|
|
@@ -99,13 +100,14 @@ function parseArgs() {
|
|
|
99
100
|
layout: 'single', // 布局:single/double/card/section
|
|
100
101
|
theme: 'default', // 主题:default/compact/comfortable
|
|
101
102
|
labelAlign: 'top', // 标签对齐:top/left/right
|
|
103
|
+
contentLocale: null,
|
|
102
104
|
browserOpenMode: openOption.mode,
|
|
103
105
|
};
|
|
104
106
|
|
|
105
107
|
// 复制一份 args 用于解析(避免修改原始数组影响后续处理)
|
|
106
108
|
const args = [...rawArgs];
|
|
107
109
|
|
|
108
|
-
// 解析 --layout, --theme, --label-align, --force 参数
|
|
110
|
+
// 解析 --layout, --theme, --label-align, --locale, --force 参数
|
|
109
111
|
for (let i = 0; i < args.length; i++) {
|
|
110
112
|
if (args[i] === '--layout' && i + 1 < args.length) {
|
|
111
113
|
options.layout = args[i + 1];
|
|
@@ -119,6 +121,14 @@ function parseArgs() {
|
|
|
119
121
|
options.labelAlign = args[i + 1];
|
|
120
122
|
args.splice(i, 2);
|
|
121
123
|
i--;
|
|
124
|
+
} else if ((args[i] === '--locale' || args[i] === '--content-locale' || args[i] === '--lang') && i + 1 < args.length) {
|
|
125
|
+
options.contentLocale = args[i + 1];
|
|
126
|
+
if (!normalizeYidaLocale(options.contentLocale)) {
|
|
127
|
+
error(`Unsupported locale: ${options.contentLocale}`);
|
|
128
|
+
}
|
|
129
|
+
process.env.OPENYIDA_CONTENT_LOCALE = normalizeYidaLocale(options.contentLocale);
|
|
130
|
+
args.splice(i, 2);
|
|
131
|
+
i--;
|
|
122
132
|
} else if (args[i] === '--force') {
|
|
123
133
|
options.force = true;
|
|
124
134
|
args.splice(i, 1);
|
|
@@ -292,21 +302,24 @@ function generateFieldId(componentName) {
|
|
|
292
302
|
|
|
293
303
|
// ── i18n 辅助 ────────────────────────────────────────
|
|
294
304
|
|
|
295
|
-
function i18n(text, enText) {
|
|
296
|
-
return
|
|
305
|
+
function i18n(text, enText, jaText) {
|
|
306
|
+
return buildYidaI18n(text, {
|
|
307
|
+
en_US: enText || text,
|
|
308
|
+
ja_JP: jaText || text,
|
|
309
|
+
});
|
|
297
310
|
}
|
|
298
311
|
|
|
299
312
|
// ── 默认占位符 ───────────────────────────────────────
|
|
300
313
|
|
|
301
|
-
const PLACEHOLDER_INPUT = i18n('请输入', 'Please enter');
|
|
302
|
-
const PLACEHOLDER_SELECT = i18n('请选择', '
|
|
314
|
+
const PLACEHOLDER_INPUT = i18n('请输入', 'Please enter', '入力してください');
|
|
315
|
+
const PLACEHOLDER_SELECT = i18n('请选择', 'Please select', '選択してください');
|
|
303
316
|
|
|
304
317
|
// ── 生成选项数据源 ───────────────────────────────────
|
|
305
318
|
|
|
306
319
|
function buildOptionDataSource(options) {
|
|
307
320
|
return options.map(function (optionText, optionIndex) {
|
|
308
321
|
return {
|
|
309
|
-
text:
|
|
322
|
+
text: i18n(optionText, optionText, optionText),
|
|
310
323
|
value: optionText,
|
|
311
324
|
sid: 'serial_' + Date.now().toString(36) + optionIndex,
|
|
312
325
|
disable: false,
|
|
@@ -400,7 +413,7 @@ function buildFieldComponent(field) {
|
|
|
400
413
|
props.complexValue = {
|
|
401
414
|
complexType: 'custom',
|
|
402
415
|
formula: '',
|
|
403
|
-
value:
|
|
416
|
+
value: i18n('', '', ''),
|
|
404
417
|
};
|
|
405
418
|
props.variable = '';
|
|
406
419
|
props.formula = '';
|
|
@@ -416,7 +429,7 @@ function buildFieldComponent(field) {
|
|
|
416
429
|
// 数字字段
|
|
417
430
|
if (componentName === 'NumberField') {
|
|
418
431
|
props.hasClear = true;
|
|
419
|
-
props.placeholder = field.placeholder ? i18n(field.placeholder) : i18n('请输入数字', 'Please enter a number');
|
|
432
|
+
props.placeholder = field.placeholder ? i18n(field.placeholder) : i18n('请输入数字', 'Please enter a number', '数値を入力してください');
|
|
420
433
|
props.valueType = 'custom';
|
|
421
434
|
props.__gridSpan = 1;
|
|
422
435
|
props.tips = i18n('', '');
|
|
@@ -489,7 +502,7 @@ function buildFieldComponent(field) {
|
|
|
489
502
|
} else if (typeof rawDataSource[0] === 'object' && rawDataSource[0].value !== undefined) {
|
|
490
503
|
dataSource = rawDataSource.map(function (item, idx) {
|
|
491
504
|
return {
|
|
492
|
-
text: item.text ||
|
|
505
|
+
text: item.text || i18n(String(item.value), String(item.value), String(item.value)),
|
|
493
506
|
value: item.value,
|
|
494
507
|
sid: item.sid || 'serial_' + Date.now().toString(36) + idx,
|
|
495
508
|
disable: item.disable || false,
|
|
@@ -536,7 +549,7 @@ function buildFieldComponent(field) {
|
|
|
536
549
|
props.isUseDataSourceColor = false;
|
|
537
550
|
props.dataSourceLinkage = '';
|
|
538
551
|
props.filterLocal = true;
|
|
539
|
-
props.notFoundContent = i18n('无数据', '
|
|
552
|
+
props.notFoundContent = i18n('无数据', 'No data', 'データがありません');
|
|
540
553
|
props.searchConfig = {
|
|
541
554
|
dataType: 'jsonp',
|
|
542
555
|
url: '',
|
|
@@ -586,7 +599,7 @@ function buildFieldComponent(field) {
|
|
|
586
599
|
|
|
587
600
|
// 部门字段
|
|
588
601
|
if (componentName === 'DepartmentSelectField') {
|
|
589
|
-
props.placeholder = i18n('请输入关键字进行搜索', 'Please enter keyword');
|
|
602
|
+
props.placeholder = i18n('请输入关键字进行搜索', 'Please enter keyword', 'キーワードを入力してください');
|
|
590
603
|
props.__gridSpan = 1;
|
|
591
604
|
props.tips = i18n('', '');
|
|
592
605
|
props.multiple = field.multiple || false;
|
|
@@ -637,8 +650,8 @@ function buildFieldComponent(field) {
|
|
|
637
650
|
props.countryMode = 'default';
|
|
638
651
|
props.countryScope = 1;
|
|
639
652
|
props.addressType = 'ADDRESS';
|
|
640
|
-
props.subLabel = i18n('详细地址', 'Detailed Address');
|
|
641
|
-
props.detailPlaceholder = i18n('请输入详细地址', 'Please input detailed address');
|
|
653
|
+
props.subLabel = i18n('详细地址', 'Detailed Address', '詳細住所');
|
|
654
|
+
props.detailPlaceholder = i18n('请输入详细地址', 'Please input detailed address', '詳細住所を入力してください');
|
|
642
655
|
props.hasClear = true;
|
|
643
656
|
props.enableLocation = true;
|
|
644
657
|
props.value = {};
|
|
@@ -659,7 +672,7 @@ function buildFieldComponent(field) {
|
|
|
659
672
|
};
|
|
660
673
|
props.type = 'normal';
|
|
661
674
|
props.listType = 'text';
|
|
662
|
-
props.buttonText = i18n('上传文件', 'Upload');
|
|
675
|
+
props.buttonText = i18n('上传文件', 'Upload file', 'ファイルをアップロード');
|
|
663
676
|
props.buttonSize = 'medium';
|
|
664
677
|
props.buttonType = 'normal';
|
|
665
678
|
props.multiple = true;
|
|
@@ -691,7 +704,7 @@ function buildFieldComponent(field) {
|
|
|
691
704
|
props.normalListType = 'image';
|
|
692
705
|
props.cardListType = 'card';
|
|
693
706
|
props.listType = 'image';
|
|
694
|
-
props.buttonText = i18n('图片上传', 'Upload');
|
|
707
|
+
props.buttonText = i18n('图片上传', 'Upload image', '画像をアップロード');
|
|
695
708
|
props.buttonSize = 'medium';
|
|
696
709
|
props.buttonType = 'normal';
|
|
697
710
|
props.enableCameraDate = true;
|
|
@@ -718,26 +731,26 @@ function buildFieldComponent(field) {
|
|
|
718
731
|
props.linkage = '';
|
|
719
732
|
props.tips = i18n('', '');
|
|
720
733
|
props.showIndex = true;
|
|
721
|
-
props.copyButtonText = i18n('复制', 'Copy');
|
|
734
|
+
props.copyButtonText = i18n('复制', 'Copy', 'コピー');
|
|
722
735
|
props.addButtonBehavior = 'NORMAL';
|
|
723
736
|
props.pageSize = 20;
|
|
724
|
-
props.addButtonText = i18n('新增一项', 'Add item');
|
|
737
|
+
props.addButtonText = i18n('新增一项', 'Add item', '項目を追加');
|
|
725
738
|
props.enableExport = true;
|
|
726
739
|
props.addButtonPosition = 'bottom';
|
|
727
740
|
props.actionsColumnWidth = 70;
|
|
728
741
|
props.theme = 'split';
|
|
729
|
-
props.delButtonText = i18n('删除', 'Remove');
|
|
742
|
+
props.delButtonText = i18n('删除', 'Remove', '削除');
|
|
730
743
|
props.useCustomColumnsWidth = false;
|
|
731
744
|
props.showSortable = false;
|
|
732
|
-
props.moveUp = i18n('上移', 'Up');
|
|
745
|
+
props.moveUp = i18n('上移', 'Up', '上へ');
|
|
733
746
|
props.maxItems = 500;
|
|
734
747
|
props.tableLayout = 'fixed';
|
|
735
748
|
props.showActions = true;
|
|
736
|
-
props.indexName = i18n('项目', 'Line');
|
|
749
|
+
props.indexName = i18n('项目', 'Line', '項目');
|
|
737
750
|
props.showCopyAction = false;
|
|
738
751
|
props.showDelAction = true;
|
|
739
752
|
props.showTableHead = true;
|
|
740
|
-
props.moveDown = i18n('下移', 'Down');
|
|
753
|
+
props.moveDown = i18n('下移', 'Down', '下へ');
|
|
741
754
|
props.pcFreezeColumnStartCounts = '0';
|
|
742
755
|
props.layout = 'TABLE';
|
|
743
756
|
props.showDeleteConfirm = true;
|
|
@@ -763,7 +776,7 @@ function buildFieldComponent(field) {
|
|
|
763
776
|
props.__gridSpan = 1;
|
|
764
777
|
props.tips = i18n('', '');
|
|
765
778
|
props.placeholder = PLACEHOLDER_SELECT;
|
|
766
|
-
props.notFoundContent = i18n('无数据', '
|
|
779
|
+
props.notFoundContent = i18n('无数据', 'No data', 'データがありません');
|
|
767
780
|
props.hasClear = true;
|
|
768
781
|
props.multiple = field.multiple || false;
|
|
769
782
|
props.dataEntryMode = false;
|
|
@@ -1313,8 +1326,8 @@ function buildFormSchema(formTitle, fields, formUuid, corpId, appType, layout, t
|
|
|
1313
1326
|
contentMarginMobile: '0',
|
|
1314
1327
|
className: 'page_' + Date.now().toString(36),
|
|
1315
1328
|
contentBgColorMobile: 'white',
|
|
1316
|
-
titleName: i18n('标题名称', 'title'),
|
|
1317
|
-
titleDesc: i18n('标题描述', '
|
|
1329
|
+
titleName: i18n('标题名称', 'title', 'タイトル'),
|
|
1330
|
+
titleDesc: i18n('标题描述', 'description', '説明'),
|
|
1318
1331
|
titleColor: 'light',
|
|
1319
1332
|
titleBg: 'https://img.alicdn.com/imgextra/i2/O1CN0143ATPP1wIa9TrVvzN_!!6000000006285-2-tps-3360-400.png_.webp',
|
|
1320
1333
|
backgroundColorCustom: '#f1f2f3',
|
|
@@ -1390,9 +1403,9 @@ function buildFormSchema(formTitle, fields, formUuid, corpId, appType, layout, t
|
|
|
1390
1403
|
formLabelVisible: true,
|
|
1391
1404
|
columns: columns,
|
|
1392
1405
|
labelAlign: labelAlign || 'top',
|
|
1393
|
-
submitText: i18n('提交', 'Submit'),
|
|
1394
|
-
stageText: i18n('暂存', '
|
|
1395
|
-
submitAndNewText: i18n('提交并继续', 'Submit and New'),
|
|
1406
|
+
submitText: i18n('提交', 'Submit', '送信'),
|
|
1407
|
+
stageText: i18n('暂存', 'Save draft', '下書き保存'),
|
|
1408
|
+
submitAndNewText: i18n('提交并继续', 'Submit and New', '送信して続ける'),
|
|
1396
1409
|
fieldId: 'formContainer_' + Date.now().toString(36) + 'a',
|
|
1397
1410
|
aiFormConfig: { systemPrompt: '', model: 'qwen' },
|
|
1398
1411
|
beforeSubmit: false,
|
|
@@ -1642,15 +1655,15 @@ function buildEmptyFormSchema() {
|
|
|
1642
1655
|
id: nextNodeId(),
|
|
1643
1656
|
props: {
|
|
1644
1657
|
beforeSubmit: false,
|
|
1645
|
-
'submitProps.text': i18n('提交', 'Submit'),
|
|
1646
|
-
submitText: i18n('提交', 'Submit'),
|
|
1647
|
-
submitProps: { text: i18n('提交', 'Submit') },
|
|
1658
|
+
'submitProps.text': i18n('提交', 'Submit', '送信'),
|
|
1659
|
+
submitText: i18n('提交', 'Submit', '送信'),
|
|
1660
|
+
submitProps: { text: i18n('提交', 'Submit', '送信') },
|
|
1648
1661
|
labelAlign: 'top',
|
|
1649
1662
|
columns: 1,
|
|
1650
1663
|
afterSubmit: false,
|
|
1651
1664
|
fieldId: 'formContainer_' + Date.now().toString(36) + 'b',
|
|
1652
|
-
stageText: i18n('暂存', '
|
|
1653
|
-
submitAndNewText: i18n('提交并继续', 'Submit and New'),
|
|
1665
|
+
stageText: i18n('暂存', 'Save draft', '下書き保存'),
|
|
1666
|
+
submitAndNewText: i18n('提交并继续', 'Submit and New', '送信して続ける'),
|
|
1654
1667
|
onProcessActionValidate: false,
|
|
1655
1668
|
afterFormDataInit: false,
|
|
1656
1669
|
},
|
|
@@ -1702,10 +1715,7 @@ function extractLabelText(component) {
|
|
|
1702
1715
|
if (typeof label === 'string') {
|
|
1703
1716
|
return label;
|
|
1704
1717
|
}
|
|
1705
|
-
|
|
1706
|
-
return label.zh_CN;
|
|
1707
|
-
}
|
|
1708
|
-
return '';
|
|
1718
|
+
return label.zh_CN || label.ja_JP || label.en_US || label.pureEn_US || '';
|
|
1709
1719
|
}
|
|
1710
1720
|
|
|
1711
1721
|
function findFormContainer(node) {
|
|
@@ -2480,7 +2490,7 @@ async function mainAddOption(parsedArgs, csrfToken, cookies, baseUrl, cookieData
|
|
|
2480
2490
|
warn('选项已存在,跳过: ' + optionText);
|
|
2481
2491
|
} else {
|
|
2482
2492
|
const newItem = {
|
|
2483
|
-
text:
|
|
2493
|
+
text: i18n(optionText, optionText, optionText),
|
|
2484
2494
|
value: optionText,
|
|
2485
2495
|
sid: 'serial_' + Date.now().toString(36) + existingDataSource.length + addedOptions.length,
|
|
2486
2496
|
disable: false,
|
|
@@ -2720,6 +2730,7 @@ async function main() {
|
|
|
2720
2730
|
const { csrf_token: csrfToken, cookies } = cookieData;
|
|
2721
2731
|
const baseUrl = resolveBaseUrl(cookieData);
|
|
2722
2732
|
success(t('common.login_ready', baseUrl));
|
|
2733
|
+
label('Locale:', resolveContentLocale({ locale: parsedArgs.contentLocale, baseUrl: baseUrl }));
|
|
2723
2734
|
|
|
2724
2735
|
if (parsedArgs.mode === 'update') {
|
|
2725
2736
|
await mainUpdate(parsedArgs, csrfToken, cookies, baseUrl, cookieData);
|
package/lib/app/create-page.js
CHANGED
|
@@ -15,12 +15,14 @@ const {
|
|
|
15
15
|
requestWithAutoLogin,
|
|
16
16
|
} = require('../core/utils');
|
|
17
17
|
const { t } = require('../core/i18n');
|
|
18
|
+
const { buildYidaI18n, buildYidaTitleI18n, normalizeYidaLocale, resolveContentLocale } = require('../core/yida-i18n');
|
|
18
19
|
const { parseOpenOption, withBrowserHandoff } = require('../core/browser-handoff');
|
|
19
20
|
|
|
20
21
|
function parseArgs(args) {
|
|
21
22
|
const openOption = parseOpenOption(args);
|
|
22
23
|
const filteredArgs = [];
|
|
23
24
|
let mode = 'default';
|
|
25
|
+
let locale = null;
|
|
24
26
|
|
|
25
27
|
for (let i = 0; i < openOption.args.length; i++) {
|
|
26
28
|
const arg = openOption.args[i];
|
|
@@ -28,6 +30,13 @@ function parseArgs(args) {
|
|
|
28
30
|
mode = openOption.args[++i];
|
|
29
31
|
continue;
|
|
30
32
|
}
|
|
33
|
+
if ((arg === '--locale' || arg === '--content-locale' || arg === '--lang') && openOption.args[i + 1]) {
|
|
34
|
+
locale = openOption.args[++i];
|
|
35
|
+
if (!normalizeYidaLocale(locale)) {
|
|
36
|
+
throw new Error(`Unsupported locale: ${locale}`);
|
|
37
|
+
}
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
31
40
|
filteredArgs.push(arg);
|
|
32
41
|
}
|
|
33
42
|
|
|
@@ -36,20 +45,16 @@ function parseArgs(args) {
|
|
|
36
45
|
appType: filteredArgs[0],
|
|
37
46
|
pageName: filteredArgs[1],
|
|
38
47
|
mode,
|
|
48
|
+
locale,
|
|
39
49
|
openMode: openOption.mode,
|
|
40
50
|
};
|
|
41
51
|
}
|
|
42
52
|
|
|
43
53
|
function buildPageInfoPostData(csrfToken, formUuid, pageName, isRenderNav) {
|
|
44
|
-
const titleJson = JSON.stringify({
|
|
45
|
-
pureEn_US: pageName,
|
|
54
|
+
const titleJson = JSON.stringify(buildYidaTitleI18n(pageName, {
|
|
46
55
|
en_US: pageName,
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
type: 'i18n',
|
|
50
|
-
ja_JP: null,
|
|
51
|
-
key: null,
|
|
52
|
-
});
|
|
56
|
+
ja_JP: pageName,
|
|
57
|
+
}));
|
|
53
58
|
|
|
54
59
|
return querystring.stringify({
|
|
55
60
|
_api: 'Form.updateFormSchemaInfo',
|
|
@@ -94,7 +99,13 @@ async function configureDashboardMode(authRef, appType, pageId, pageName) {
|
|
|
94
99
|
}
|
|
95
100
|
|
|
96
101
|
async function run(args) {
|
|
97
|
-
|
|
102
|
+
let options;
|
|
103
|
+
try {
|
|
104
|
+
options = parseArgs(args || []);
|
|
105
|
+
} catch (err) {
|
|
106
|
+
const { error: chalkError } = require('../core/chalk');
|
|
107
|
+
chalkError(err.message, { hint: t('create_page.usage') });
|
|
108
|
+
}
|
|
98
109
|
|
|
99
110
|
if (options.args.length < 2) {
|
|
100
111
|
const { error: chalkError } = require('../core/chalk');
|
|
@@ -132,6 +143,9 @@ async function run(args) {
|
|
|
132
143
|
};
|
|
133
144
|
chalkSuccess(t('common.login_ready', authRef.baseUrl));
|
|
134
145
|
|
|
146
|
+
const contentLocale = resolveContentLocale({ locale: options.locale, baseUrl: authRef.baseUrl });
|
|
147
|
+
label('Locale', contentLocale);
|
|
148
|
+
|
|
135
149
|
// Step 2: 创建自定义页面
|
|
136
150
|
step(2, t('create_page.step_create'));
|
|
137
151
|
info(t('create_page.sending'));
|
|
@@ -140,7 +154,7 @@ async function run(args) {
|
|
|
140
154
|
const postData = querystring.stringify({
|
|
141
155
|
_csrf_token: auth.csrfToken,
|
|
142
156
|
formType: 'display',
|
|
143
|
-
title: JSON.stringify(
|
|
157
|
+
title: JSON.stringify(buildYidaI18n(pageName, { en_US: pageName, ja_JP: pageName })),
|
|
144
158
|
});
|
|
145
159
|
return httpPost(
|
|
146
160
|
auth.baseUrl,
|
package/lib/app/import-app.js
CHANGED
|
@@ -19,19 +19,21 @@ const {
|
|
|
19
19
|
requestWithAutoLogin,
|
|
20
20
|
} = require('../core/utils');
|
|
21
21
|
const { t } = require('../core/i18n');
|
|
22
|
-
const {
|
|
22
|
+
const { buildYidaI18n, resolveContentLocale } = require('../core/yida-i18n');
|
|
23
|
+
const { banner, step, label, success, fail, warn, info, error, result, usage, hint } = require('../core/chalk');
|
|
23
24
|
|
|
24
25
|
// ── 创建新应用 ────────────────────────────────────────
|
|
25
26
|
|
|
26
27
|
async function createApp(appName, authRef) {
|
|
28
|
+
const contentLocale = resolveContentLocale({ baseUrl: authRef.baseUrl });
|
|
27
29
|
const postData = querystring.stringify({
|
|
28
30
|
_csrf_token: authRef.csrfToken,
|
|
29
|
-
appName: JSON.stringify(
|
|
30
|
-
description: JSON.stringify(
|
|
31
|
+
appName: JSON.stringify(buildYidaI18n(appName, { en_US: appName, ja_JP: appName })),
|
|
32
|
+
description: JSON.stringify(buildYidaI18n(appName, { en_US: appName, ja_JP: appName })),
|
|
31
33
|
icon: 'xian-yingyong%%#0089FF',
|
|
32
34
|
iconUrl: 'xian-yingyong%%#0089FF',
|
|
33
35
|
colour: 'blue',
|
|
34
|
-
defaultLanguage:
|
|
36
|
+
defaultLanguage: contentLocale,
|
|
35
37
|
openExclusive: 'n',
|
|
36
38
|
openPhysicColumn: 'n',
|
|
37
39
|
openIsolationDatabase: 'n',
|
|
@@ -56,7 +58,7 @@ async function createBlankForm(appType, formTitle, authRef) {
|
|
|
56
58
|
const postData = querystring.stringify({
|
|
57
59
|
_csrf_token: authRef.csrfToken,
|
|
58
60
|
formType: 'receipt',
|
|
59
|
-
title: JSON.stringify(
|
|
61
|
+
title: JSON.stringify(buildYidaI18n(formTitle, { en_US: formTitle, ja_JP: formTitle })),
|
|
60
62
|
});
|
|
61
63
|
|
|
62
64
|
const result = await requestWithAutoLogin((auth) => {
|
package/lib/app/page-linter.js
CHANGED
|
@@ -306,7 +306,7 @@ function lintYidaSource(sourceCode, filePath) {
|
|
|
306
306
|
|
|
307
307
|
const computedMatch = line.match(/\{\s*\[/);
|
|
308
308
|
if (computedMatch && !isInCommentOrString(line, computedMatch.index)) {
|
|
309
|
-
pushIssue(
|
|
309
|
+
pushIssue(errors, lineNumber, 'computed-property', t('publish.lint_computed_property'), disableMap);
|
|
310
310
|
}
|
|
311
311
|
|
|
312
312
|
const padMatch = line.match(/\.(padStart|padEnd)\s*\(/);
|
|
@@ -329,6 +329,11 @@ function lintYidaSource(sourceCode, filePath) {
|
|
|
329
329
|
pushIssue(errors, lineNumber, 'controlled-input', t('publish.lint_controlled_input'), disableMap);
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
+
const nativeSelectMatch = line.match(/<select\b/);
|
|
333
|
+
if (nativeSelectMatch && !isInCommentOrString(line, nativeSelectMatch.index)) {
|
|
334
|
+
pushIssue(warnings, lineNumber, 'native-select-ui', t('publish.lint_native_select_ui'), disableMap);
|
|
335
|
+
}
|
|
336
|
+
|
|
332
337
|
const pageSizeMatch = line.match(/\bpageSize\s*:\s*(\d+)/);
|
|
333
338
|
if (pageSizeMatch && Number(pageSizeMatch[1]) > 100 && !isInCommentOrString(line, pageSizeMatch.index)) {
|
|
334
339
|
pushIssue(errors, lineNumber, 'page-size-limit', t('publish.lint_page_size_limit', pageSizeMatch[1]), disableMap);
|
package/lib/app/update-app.js
CHANGED
|
@@ -15,6 +15,7 @@ const {
|
|
|
15
15
|
requestWithAutoLogin,
|
|
16
16
|
} = require('../core/utils');
|
|
17
17
|
const { t } = require('../core/i18n');
|
|
18
|
+
const { buildYidaI18n } = require('../core/yida-i18n');
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* 解析命令行参数
|
|
@@ -135,21 +136,18 @@ async function run(args) {
|
|
|
135
136
|
|
|
136
137
|
// 应用名称(支持国际化)
|
|
137
138
|
if (params.name) {
|
|
138
|
-
postDataObj.appName = JSON.stringify({
|
|
139
|
-
zh_CN: params.name,
|
|
139
|
+
postDataObj.appName = JSON.stringify(buildYidaI18n(params.name, {
|
|
140
140
|
en_US: params.name,
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
});
|
|
141
|
+
ja_JP: params.name,
|
|
142
|
+
}, { includePureEn: true }));
|
|
144
143
|
}
|
|
145
144
|
|
|
146
145
|
// 应用描述
|
|
147
146
|
if (params.desc) {
|
|
148
|
-
postDataObj.description = JSON.stringify({
|
|
149
|
-
zh_CN: params.desc,
|
|
147
|
+
postDataObj.description = JSON.stringify(buildYidaI18n(params.desc, {
|
|
150
148
|
en_US: params.desc,
|
|
151
|
-
|
|
152
|
-
});
|
|
149
|
+
ja_JP: params.desc,
|
|
150
|
+
}));
|
|
153
151
|
}
|
|
154
152
|
|
|
155
153
|
// 图标
|