节点在线、应用在线、配置在线使用令牌查询
|
# 多è¯è¨€æœ¬åœ°åŒ–
> 基线:NewLife.Core 12 编译期è¯è¡¨ï½œè¿è¡Œæ—¶ 31 ç§è¯è¨€ï½œè‹±æ–‡ä¸ºæºæ–‡ä¸Žå…œåº•
## 1. è¯è¨€åˆ†å±‚
本库全部è¿è¡Œæ—¶ç»„件统一维护 NewLife.Core çš„ 31 ç§è¯è¨€è¯è¡¨ã€‚缺失译文在è¿è¡Œæ—¶è‡ªåŠ¨å›žé€€è‹±æ–‡ï¼Œæ–°å¢žè¯è¨€ä¸éœ€è¦æ”¹ä»£ç 。
è¯è¡¨æ–‡ä»¶ï¼ˆ31 ä¸ªï¼Œå¦æœ‰è‹±æ–‡æºæ–‡å…œåº•):
- zh-Hans
- zh-Hant
- es
- de
- ja
- fr
- pt
- ru
- ko
- ar
- bn
- cs
- da
- el
- fa
- fi
- he
- hi
- hu
- id
- it
- ms
- nb
- nl
- pl
- ro
- sv
- th
- tr
- uk
- vi
## 2. è¯è¡¨æ–‡ä»¶
- `Stardust/Locales`
- `Stardust.Extensions/Locales`
- `Stardust.Server/Locales`
- `Stardust.Web/Locales`
- `Stardust.WebApi/Locales`
- `Stardust.Data/Locales`
- `StarAgent/Locales`
- `StarAgent.Mobile/Locales`
- `StarGateway/Locales`
- `Plugins/AgentExpansion/Locales`
- `Plugins/NetworkDetect/Locales`
- `DeployAgent/Locales`
- 文件åå³ BCP 47 è¯è¨€ä»£ç ï¼›è‹±æ–‡æ˜¯æºæ–‡ä¸Žå…œåº•,**ä¸éœ€è¦** `en.yml`。
- 键为**英文原文**,必须与代ç å—符串é€å—一致(å«ç©ºæ ¼ä¸Žæ ‡ç‚¹ï¼‰ï¼›å ä½ç¬¦ `{0}` ä¸Žæ ¼å¼è¯´æ˜Žç¬¦ `{1:n0}` 必须与英文一致。
- è¯è¡¨ç”± NewLife.Core 包内的æºç”Ÿæˆå™¨åœ¨ç¼–译期读å–并生æˆåŽ‹ç¼©è¯è¨€è¡¨ï¼ŒNativeAOT ä¸‹åŒæ ·å¯ç”¨ã€‚
- 全部è¿è¡Œæ—¶ç»„件(SDKã€æœåŠ¡ç«¯ã€ç®¡ç†ç«¯ã€ä»£ç†ã€ç½‘å…³ã€æ’ä»¶ã€å‘å¸ƒå·¥å…·ï¼‰çš„å¼‚å¸¸ã€æ—¥å¿—ã€æŽ§åˆ¶å°ã€äº‹ä»¶ã€å‘½ä»¤è¿”回结果ã€åކå²å¤‡æ³¨ã€åŸ‹ç‚¹æ ‡ç¾ä¸Žé…置项æè¿°å·²è‹±æ–‡åŒ–ï¼Œå¹¶è¡¥é½ 31 ç§è¯è¨€è¯‘文(缺失或åŒå½¢æ¡ç›®å›žé€€è‹±æ–‡æºæ–‡ï¼‰ã€‚告è¦é€šçŸ¥ï¼ˆ`AlarmService`ã€`RobotHelper` ä¸Žä¸‹çº¿å‘Šè¦æ–‡æ¡ˆï¼‰ä¸Ž Cube 界é¢é™æ€æ–‡æ¡ˆä»ä¸ºä¸æ–‡æºæ–‡ï¼Œè§ç¬¬ 7 节。
日志è¯è¨€ä¸Žç•Œé¢è¯è¨€ç›¸äº’独立:`Localization.Culture` æŽ§åˆ¶å¼‚å¸¸ä¸Žç•Œé¢æ–‡æ¡ˆï¼Œ`Localization.LogCulture` 控制日志与é…置注释。日志直接写英文模æ¿ï¼Œç”±æ—¥å¿—器按日志è¯è¨€ç¿»è¯‘ï¼›éœ€è¦æ‰‹åŠ¨ç”Ÿæˆæ—¥å¿—文本时用 `L.TLog`,异常文案用 `L.T`。带动æ€å‰ç¼€çš„æ—¥å¿—使用 `InfoPrefixed`/`ErrorPrefixed`,å‰ç¼€åœ¨ç¿»è¯‘ä¹‹åŽæ‹¼æŽ¥ï¼Œé¿å…å‰ç¼€å¯¼è‡´æ•´ä¸²æŸ¥è¡¨å¤±è´¥è€Œå›žé€€è‹±æ–‡ã€‚
日志与异常一律使用**æ ¼å¼å—符串 + 傿•°**(`Log?.Info("Deleted {0}", path)`),ä¸è¦ç”¨å—符串æ’值(`$"Deleted {path}"`):æ’å€¼åŽæ¨¡æ¿å·²å±•å¼€ï¼Œæ— æ³•å†æŒ‰åŽŸæ–‡æŸ¥è¡¨ã€‚æ¨¡æ¿æŒ‰æ•´å¥åŒ¹é…ï¼Œå‚æ•°æœ¬èº«ä¸ç¿»è¯‘ï¼›ä¸è¦æŠŠè‹±æ–‡çжæ€è¯å¡žè¿› `{n}` 傿•°ï¼Œåº”按è¯ä¹‰æ‹†æˆå®Œæ•´å¥å。
## 3. æ–°å¢žä¸€æ¡æ¶ˆæ¯
1. 代ç ä¸å†™è‹±æ–‡æºæ–‡ï¼š`throw new XException("Session {0} is closed.", sessionId);`,日志直接写英文模æ¿ï¼ˆ`Logger.Format` 自动翻译)。
2. 在全部è¯è¨€æ–‡ä»¶çš„åŒä¸€ä½ç½®è¡¥é½è¯¥é”®ï¼›æš‚未翻译的è¯è¨€å…ˆå†™è‹±æ–‡åŽŸæ–‡å…œåº•ï¼Œé—æ¼åªä¼šå›žé€€è‹±æ–‡ï¼Œä¸ä¼šç¼–译失败。
3. è¿è¡Œæ ¡éªŒè„šæœ¬ç¡®è®¤é”®é›†åˆã€å ä½ç¬¦ä¸Žæ ¼å¼è¯´æ˜Žç¬¦ä¸€è‡´ã€‚
## 4. æ ¡éªŒ
```powershell
# è¿è¡Œæ—¶æ¶ˆæ¯ä¸å¾—å«ä¸æ–‡ï¼ˆå¼‚å¸¸ã€æ—¥å¿—ã€æŽ§åˆ¶å°ã€ç•Œé¢æç¤ºã€åŸ‹ç‚¹æ ‡ç¾ï¼‰
pwsh -File check-english-exceptions.ps1 -Root . -Dir Stardust
pwsh -File check-english-exceptions.ps1 -Root . -Dir Stardust.Extensions
pwsh -File check-english-exceptions.ps1 -Root . -Dir Stardust.Server
pwsh -File check-english-exceptions.ps1 -Root . -Dir Stardust.Web
pwsh -File check-english-exceptions.ps1 -Root . -Dir Stardust.WebApi
pwsh -File check-english-exceptions.ps1 -Root . -Dir Stardust.Data
pwsh -File check-english-exceptions.ps1 -Root . -Dir StarAgent
pwsh -File check-english-exceptions.ps1 -Root . -Dir StarGateway
pwsh -File check-english-exceptions.ps1 -Root . -Dir Plugins/AgentExpansion
pwsh -File check-english-exceptions.ps1 -Root . -Dir Plugins/NetworkDetect
pwsh -File check-english-exceptions.ps1 -Root . -Dir DeployAgent
pwsh -File check-english-exceptions.ps1 -Root . -Dir StarAgent.Mobile
# è¯è¡¨ä¸€è‡´æ€§ã€‚Web/WebApi 通过 Compile Include å¤ç”¨ Server æºç ,å¤å„¿é”®æ‰«æéœ€åŒæ—¶è¦†ç›–
pwsh -File check-locales.ps1 -LocaleDir Stardust/Locales -SourceDir Stardust
pwsh -File check-locales.ps1 -LocaleDir Stardust.Extensions/Locales -SourceDir Stardust.Extensions
pwsh -File check-locales.ps1 -LocaleDir Stardust.Server/Locales -SourceDir Stardust.Server
pwsh -File check-locales.ps1 -LocaleDir Stardust.Web/Locales -SourceDir "Stardust.Web,Stardust.Server"
pwsh -File check-locales.ps1 -LocaleDir Stardust.WebApi/Locales -SourceDir "Stardust.WebApi,Stardust.Server"
pwsh -File check-locales.ps1 -LocaleDir Stardust.Data/Locales -SourceDir Stardust.Data
pwsh -File check-locales.ps1 -LocaleDir StarAgent/Locales -SourceDir StarAgent
pwsh -File check-locales.ps1 -LocaleDir StarGateway/Locales -SourceDir StarGateway
pwsh -File check-locales.ps1 -LocaleDir Plugins/AgentExpansion/Locales -SourceDir Plugins/AgentExpansion
pwsh -File check-locales.ps1 -LocaleDir Plugins/NetworkDetect/Locales -SourceDir Plugins/NetworkDetect
pwsh -File check-locales.ps1 -LocaleDir DeployAgent/Locales -SourceDir DeployAgent
pwsh -File check-locales.ps1 -LocaleDir StarAgent.Mobile/Locales -SourceDir StarAgent.Mobile
```
CI çš„ localization job 会执行åŒä¸€ç»„è„šæœ¬ã€‚æ£€æŸ¥è„šæœ¬è¦†ç›–æŠ›å¼‚å¸¸ã€æŽ§åˆ¶å°ä¸Žæ—¥å¿—å™¨ï¼ˆå« `log?.Warn` 这类å¯ç©ºè°ƒç”¨ï¼‰ã€`XTrace`ã€äº‹ä»¶ä¸ŠæŠ¥ï¼ˆ`WriteInfoEvent`/`WriteEvent`)ã€è‡ªå®šä¹‰æ—¥å¿—包装(`WriteError`/`WriteInfo`ï¼‰ä»¥åŠ `return` 命令结果;åªå¯¹å—符串å—é¢é‡åˆ¤ä¸æ–‡ï¼Œä¸æ–‡æ ‡è¯†ç¬¦ï¼ˆå¦‚ `CommandStatus.错误`)ä¸è¯¯æŠ¥ã€‚å¤å„¿é”®æ‰«æåŒæ—¶è¯»å– `.cs` 与 `.csproj`,程åºé›†æ ‡é¢˜ä¸Žæè¿°ä½œä¸ºè¯è¡¨ key æ—¶ä¸ä¼šè¯¯æŠ¥ã€‚
覆盖率报告由 `report-locales.ps1` 生æˆåˆ° `Doc/多è¯è¨€è¦†ç›–.md`,按è¯è¨€ä¸Žç»„件统计已翻译æ¡ç›®ä¸Žå”¯ä¸€æºæ–‡æ€»æ•°ï¼š
```powershell
pwsh -File report-locales.ps1
```
`Stardust.Tests` ä¸çš„ `LocaleConsistencyTests` 在 `dotnet test` 阶段执行åŒä¸€å¥—键集ã€å ä½ç¬¦ä¸Žæ ¼å¼è¯´æ˜Žç¬¦æ ¡éªŒï¼Œå¹¶æ–言简体与ç¹ä½“è¯è¡¨å…¨éƒ¨æä¾›è¯‘文。`StarPanelLocaleTests` æ ¡éªŒä»£ç†é¢æ¿å‰ç«¯è¯è¡¨ï¼š31 份 `i18n/*.json` 键集一致,且 `index.html` é‡Œçš„ä¸æ–‡æºæ–‡ç»è¯è¡¨æ›¿æ¢åŽä¸å†æ®‹ç•™ä¸æ–‡ï¼Œæ–°å¢žé¢æ¿æ–‡æ¡ˆå¿˜è®°è¡¥è¯è¡¨ä¼šç›´æŽ¥æµ‹è¯•失败。`AgentPanelLocalizationTests` 验è¯ä»£ç†é¢æ¿çš„æœåŠ¡ç«¯é…置文案ã€è¿è¡Œæ—¶é•¿æ¨¡æ¿ä¸Žé‰´æƒæ¶ˆæ¯åœ¨è¿è¡ŒæœŸç¡®å®žèƒ½å–åˆ°è¯‘æ–‡ï¼Œå¹¶æ ¡éªŒæœåŠ¡ç«¯æœ¬åœ°åŒ–ä¸æ”¹å†™å†…è”脚本(é¿å…译文撇å·ç ´å脚本)。
## 5. è¿è¡ŒçŽ¯å¢ƒä¸Žè¯è¨€é€‰æ‹©
ç›®æ ‡è¯è¨€æ¥è‡ª `CultureInfo.CurrentUICulture`(`Localization.Culture` ä¸ºç©ºæ—¶ï¼‰ï¼Œå³æ“作系统的界é¢è¯è¨€ï¼šWindows å–系统显示è¯è¨€ï¼ŒLinux å– `LANG`/`LC_MESSAGES`。`zh-CN` å‘½ä¸ `zh-Hans`,`zh-TW` å‘½ä¸ `zh-Hant`;没有对应è¯è¡¨çš„è¯è¨€å›žé€€è‹±æ–‡ã€‚
### 5.1 é…置项固定è¯è¨€
`Star.Language`(客户端 SDK,Star.json)与 `StarServer.Language`(æœåŠ¡ç«¯ï¼Œæ•°æ®åº“é…置)å¯å›ºå®šè¿›ç¨‹çš„界é¢ä¸Žæ—¥å¿—è¯è¨€ï¼Œå–值如 `zh-Hans`ã€`zh-Hant`ã€`en`ã€`ja`。留空时跟éšç³»ç»Ÿè¯è¨€ã€‚星尘代ç†ä¸Žä»£ç†é¢æ¿ç»Ÿä¸€ä½¿ç”¨ Core 全局é…ç½® `NewLife.Setting.Language`(`Config/Core.config`),ä¸å†è¯»å– `StarAgent.Language`ï¼›é¢æ¿å³ä¸Šè§’的选择器通过 `/api/SetLanguage` 写入该é…置,留空æ¢å¤è‡ªåŠ¨è¯†åˆ«ã€‚Core çš„è¯è¨€ç 是 `zh-CN`ã€`en-US` 这类地区ç ï¼Œé¢æ¿è¯è¡¨æŒ‰ `zh-Hans`ã€`en` 命å,`i18n.js` 负责归一化åŽåŠ è½½å¯¹åº”è¯è¡¨ã€‚
`StarLocalization.NormalizeCulture` 负责把 `zh-CN`ã€`de-AT`ã€`no`ã€`iw` ç‰å†™æ³•归一化到è¯è¡¨è¯è¨€ï¼›`StarLocalization.UseLanguage` 供客户端 SDK 与æœåŠ¡ç«¯å†™å…¥ `Localization.Culture` 与 `Localization.LogCulture`,代ç†ä¸Žé¢æ¿åˆ™èµ° Core çš„ `Localization.SetLanguage`(由 `Setting.Save()` 自动应用)。
### 5.2 Web 宿主按请求识别è¯è¨€
`StarServer.Language` 留空时,StarServerã€StarWeb 与 StarWebApi 通过 `UseStardustLocalization` 注册请求è¯è¨€ä¸é—´ä»¶ï¼Œä¼˜å…ˆçº§ä¸ºæŸ¥è¯¢ä¸² `?lang=`ã€Cookie `.AspNet.Culture`ã€è¯·æ±‚头 `Accept-Language`。ä¸é—´ä»¶åªä¿®æ”¹ `CurrentUICulture`,ä¸å½±å“åè®®ä¸çš„æ•°å—ä¸Žæ—¥æœŸæ ¼å¼ï¼›è¯·æ±‚结æŸåŽè¿˜åŽŸã€‚
识别期间日志与é…置文件注释固定为宿主å¯åЍè¯è¨€ï¼Œé¿å…åŒä¸€æ—¥å¿—文件éšè¯·æ±‚è¯è¨€æ··æ‚。
.NET 6+ 在 Linux 上ä¾èµ– ICU(Debian/Ubuntu 包å通常为 `libicu`,Alpine 为 `icu-libs`)。缺少 ICU 时进程会在å¯åŠ¨é˜¶æ®µå¤±è´¥ï¼Œæç¤ºè®¾ç½® `System.Globalization.Invariant`ã€‚æ— ICU çš„è€æ—§/嵌入å¼ç³»ç»Ÿå¯ç”¨çŽ¯å¢ƒå˜é‡é™çº§ï¼š
```bash
# 直接è¿è¡Œ
export DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
# systemd æœåŠ¡
sudo systemctl edit staragent
# [Service]
# Environment=DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
sudo systemctl daemon-reload && sudo systemctl restart staragent
```
é™çº§åŽè¿›ç¨‹ç…§å¸¸è¿è¡Œï¼Œä½†åªæœ‰è‹±æ–‡ï¼Œä¸”ä¸å¾—显å¼è®¾ç½® `Localization.Culture`(invariant 模å¼ä¸‹åˆ›å»ºå…·ä½“区域性会抛 `CultureNotFoundException`)。环境å˜é‡ä¼˜å…ˆçº§é«˜äºŽ `runtimeconfig.json`,å‡çº§éƒ¨ç½²æ—¶æ— éœ€é‡æ–°å‘布å³å¯åˆ‡æ¢ã€‚
## 6. 批é‡ç¿»è¯‘工作æµ
è¯‘æ–‡ä»¥è‹±æ–‡æºæ–‡ä¸ºé”®æ‰¹é‡å†™å…¥ï¼Œ`apply-locales.js` è´Ÿè´£è½åˆ°å„组件è¯è¡¨ï¼š
```powershell
# entries.json: { "English key": { "ja": "译文", "de": "译文" }, ... }
node apply-locales.js entries.json Stardust/Locales Stardust.Server/Locales StarAgent/Locales ...
# 统计æŸè¯è¨€ä»ä¸ºè‹±æ–‡å ä½çš„é”®
node apply-locales.js --missing es
```
脚本先按å„组件的 `zh-Hans.yml` 键集剔除多余键,å†å°±åœ°æ›´æ–°è¯‘æ–‡ï¼›è¯æ¡æœªè¦†ç›–çš„è¯è¨€ä¿æŒè‹±æ–‡å ä½ã€‚写入åŽä¾æ¬¡æ‰§è¡Œ `check-locales.ps1` 与 `report-locales.ps1` æ ¡éªŒå¹¶åˆ·æ–°è¦†ç›–çŽ‡ã€‚
界é¢å…ƒæ•°æ®ï¼ˆèœå•åã€å®žä½“å—æ®µæè¿°ï¼Œé”®ä¸ºä¸æ–‡æºæ–‡ï¼‰èµ°åŒä¸€è„šæœ¬çš„å…ƒæ•°æ®æ¨¡å¼ï¼š
```powershell
# 1) 收集键清å•(模型 XML + 控制器/视图ä¸çš„ DisplayName/Description)
pwsh -File collect-metadata-keys.ps1 -Model Stardust.Data/Nodes/Model.xml -Output Stardust.Data/Locales/metadata-keys.json
pwsh -File collect-metadata-keys.ps1 -SourceDir Stardust.Web -Output Stardust.Web/Locales/metadata-keys.json
# 2) 写入译文(元数æ®é”®å…于 zh-Hans 键集è£å‰ªï¼Œå•独æˆèŠ‚ï¼‰
node apply-locales.js --metadata Stardust.Data/Locales/metadata-keys.json --section "# 界é¢å…ƒæ•°æ®è¯æ¡" entries.json Stardust.Data/Locales
# 3) æ ¡éªŒï¼ˆ`-MetadataKeys` å…许元数æ®é”®åªå‡ºçŽ°åœ¨éƒ¨åˆ†è¯è¨€ï¼‰
pwsh -File check-locales.ps1 -LocaleDir Stardust.Data/Locales -SourceDir Stardust.Data -MetadataKeys Stardust.Data/Locales/metadata-keys.json
```
### 6.1 与英文åŒå½¢çš„è¯æ¡
å°‘æ•°æ¡ç›®åœ¨ç›®æ ‡è¯è¨€ä¸æœ¬å°±ä¸Žè‹±æ–‡åŒå½¢ï¼Œå±žæ£ç¡®è¯‘æ–‡è€Œéžæ¼ç¿»ï¼Œ`report-locales.ps1` 会将其计为未翻译:Hashã€Commitã€Statusã€Serverã€Siteã€Repositoryã€Branchã€OOM score ç‰æŠ€æœ¯é€šç”¨è¯ï¼Œä»¥åŠ NAT proxyã€Process ID è¿™ç±»ç¼©å†™åŠ å€Ÿè¯çš„组åˆã€‚当å‰åªæœ‰å¾·è¯ 8 æ¡ã€æ„大利è¯/è‘¡è„牙è¯/罗马尼亚è¯å„ 4 æ¡ã€è¥¿ç牙è¯/土耳其è¯å„ 2 æ¡ã€æ·å…‹è¯/希腊è¯/匈牙利è¯/å°å°¼è¯/马æ¥è¯/è·å…°è¯å„ 1 æ¡å±žäºŽæ¤åˆ—。æ¤å¤–,第 8 节的程åºé›†æ ‡é¢˜ä¸Žæè¿°å·²è¡¥é½å„è¯è¨€è¯‘æ–‡ï¼Œå› æ¤å„è¯è¨€æ±‡æ€»è¦†ç›–率约 99.5%,德è¯å› åŒå½¢è¯æ¡ä¸º 98.8%ï¼›ç®€ä½“ä¸æ–‡ä¸º 87.9%,是界é¢å…ƒæ•°æ®é”®ä»¥ä¸æ–‡ä¸ºé”®ã€å€¼ä¸Žé”®ç›¸åŒå¯¼è‡´æŒ‰å£å¾„ä¸è®¡å…¥å·²ç¿»è¯‘,属统计å£å¾„è€Œéžæ¼ç¿»ã€‚
## 7. 管ç†ç«¯ç•Œé¢æ–‡æ¡ˆ
ç•Œé¢æ–‡æ¡ˆåˆ†ä¸¤ç±»ï¼Œå¤„ç†æ–¹å¼ä¸åŒï¼š
- **æœåŠ¡ç«¯è¿”å›žçš„æ¶ˆæ¯**(异常ã€API `message`ã€äº‹ä»¶ã€å‘½ä»¤è¿”回结果ã€åކå²å¤‡æ³¨ï¼‰ï¼šå·²æ˜¯è‹±æ–‡æºæ–‡å¹¶èµ° `L.T` 或日志翻译,éšè¯·æ±‚è¯è¨€è¾“出,已覆盖。代ç†é¢æ¿çš„é…置项分组ã€å—段åã€è¯´æ˜Žã€è¿è¡Œæ—¶é•¿ï¼ˆ`FormatUptime`)与鉴æƒ/æœåŠ¡ç®¡ç†å™¨é”™è¯¯åŒæ ·ç”± `StarApi` 用 `L.T` 输出,è¯è¨€ç”± Core 全局é…置决定。
- **界é¢é™æ€æ–‡æ¡ˆä¸Ž Cube æ“作结果**(控制器 `df.DisplayName`ã€å®žä½“ `[DisplayName]`ã€åŒºåŸŸèœå•ã€`JsonRefresh`/`Alert` æç¤ºï¼‰ï¼šä»æ˜¯ä¸æ–‡æºæ–‡ï¼Œä¸Žç¬¬äºŒç±»ä¸€å¹¶å¤„ç†ã€‚
ç¬¬äºŒç±»æš‚ä¸æ”¹è‹±æ–‡æºæ–‡ï¼šå½“å‰å¼•用的 NewLife.Cube 版本(6.15.2026.1018-beta1727)ä¸è°ƒç”¨ `Localization`,`[DisplayName]`ã€`[Description]` 与 `ListField.DisplayName` åŽŸæ ·æ¸²æŸ“ã€‚æ¤æ—¶æŠŠä¸æ–‡æ ‡ç¾æ”¹æˆè‹±æ–‡æºæ–‡ï¼Œä¼šè®©ä¸æ–‡ç•Œé¢ç›´æŽ¥æ˜¾ç¤ºè‹±æ–‡ï¼Œå±žäºŽåŠŸèƒ½å›žé€€ï¼›Cube 主线已在 `ConfigController` 与 `CubeLocalization` ä¸å®žçް `L.T` 翻译,待å‘布åŽå†ç»Ÿä¸€åˆ‡æ¢å¹¶åŒæ¥è¯è¡¨ã€‚
2026-10-21 实测确认该结论:临时站点分别以 `zh-CN`ã€`en-US`ã€`ja-JP` 请求 `/Nodes/Node`ã€`/Configs/AppConfig`ã€`/Platform/StarServer`ï¼Œé¡µé¢æ ‡é¢˜ã€è¡¨å¤´ä¸ŽæŒ‰é’®å…¨éƒ¨ç›¸åŒä¸”ä¸ºä¸æ–‡ï¼›äºŒè¿›åˆ¶æ£€æŸ¥ `NewLife.Cube.dll`/`NewLife.Cube.Core.dll` ä¸ `Localization` 引用数为 0。Cube 仓库的 i18n æäº¤ï¼ˆ`4a5d0f44` MVC 视图外壳按请求è¯è¨€æ¸²æŸ“ã€`0fa0ce42` æœåŠ¡ç«¯æ¶ˆæ¯ä¸Žå®žä½“å…ƒæ•°æ®æŽ¥å…¥ç¼–è¯‘æœŸè¯è¡¨ï¼‰å‡æ™šäºŽè¯¥ beta 包,NuGet å°šæ— æ›´æ–°ç‰ˆæœ¬ã€‚
为缩çŸå‘版åŽçš„åˆ‡æ¢æ—¶é—´ï¼ŒStardust ä¾§è¯è¡¨å·²æå‰å¤‡å¥½ï¼ˆå‡çº§ Cube åŽæ— éœ€å†æ”¹ä»£ç å³å¯ç”Ÿæ•ˆï¼‰ï¼š
- `Stardust.Data/Locales/metadata-keys.json`:76 个实体元数æ®é”®ï¼ˆè¡¨/列æè¿°ï¼‰ï¼Œå¯¹åº” 30 ç§è¯è¨€å…± 2280 æ¡è¯‘文。
- `Stardust.Web/Locales/metadata-keys.json`:67 个èœå•与列表列å键(58 ä¸ªä¸æ–‡æºæ–‡éœ€ç¿»è¯‘,其余为 `{Name}` 这类å ä½ç¬¦æ¨¡æ¿ï¼‰ï¼Œå¯¹åº” 30 ç§è¯è¨€å…± 1740 æ¡è¯‘文。程åºé›†æ ‡é¢˜ä¸Žæè¿°ï¼ˆcsproj çš„ `AssemblyTitle`/`Description`)ä¸è¿›å…¥è¯¥æ¸…å•ï¼Œä»æŒ‰ç¬¬ 8 èŠ‚çš„ã€Œè‹±æ–‡æºæ–‡ + Locales è¯æ¡ã€çº¦å®šå¤„ç†ã€‚
- 元数æ®é”®ä»¥**䏿–‡ä¸º key**(与 Cube `DataField.Localize`ã€`MenuHelper.GetDisplayName` 的约定一致),写在éžä¸æ–‡è¯è¨€çš„ `*.yml` ä¸ï¼›ç®€ä½“䏿–‡ç›´æŽ¥è¿”å›žæºæ–‡ï¼Œå› æ¤ä¸è¿›å…¥ `zh-Hans.yml`。
- 工具链:`collect-metadata-keys.ps1` æ”¶é›†é”®ï¼ˆæ”¯æŒ `metadata-extra.json` 增é‡è¡¥ `L.T` æ ‡ç¾ï¼‰ã€`check-locales.ps1 -MetadataKeys` æ ¡éªŒï¼ˆå…ƒæ•°æ®é”®å¯éƒ¨åˆ†ç¼ºå¤±ã€å…å¤å„¿é”®æ£€æŸ¥ï¼‰ã€`apply-locales.js --metadata --section "# 界é¢å…ƒæ•°æ®è¯æ¡"` 写入ã€`report-locales.ps1` 第 3 节统计元数æ®è¦†ç›–率。
- 生效æ¡ä»¶ï¼šå‡çº§åˆ°å« `UseCubeLocalization()` çš„ NewLife.Cube 版本;届时用 E2E å¢žåŠ `Accept-Language` æ–言回归。
æµè§ˆå™¨æ ‡é¢˜åŽç¼€ä»ä¸ºè‹±æ–‡ï¼ˆ`Stardust management portal`):Cube çš„ `_Layout.cshtml` 直接输出 `SysConfig.Current.DisplayName`,未ç»è¿‡ `L.T`,属上游待办;E2E æ–言已兼容ä¸è‹±æ–‡ä¸¤ç§ç«™ç‚¹å。
告è¦é€šçŸ¥ï¼ˆ`AlarmService`ã€`RobotHelper` 与节点/应用下线告è¦ï¼‰é¢å‘䏿–‡è¿ç»´ç¾¤ï¼Œä¿æŒä¸æ–‡æºæ–‡ï¼Œä¸å‚与è¿è¡Œæ—¶æ¶ˆæ¯æ£€æŸ¥ã€‚
### 7.1 代ç†é¢æ¿å‰ç«¯è¯è¡¨
`StarAgent/WebPanel/wwwroot/index.html` ä¿æŒä¸æ–‡æºæ–‡ï¼Œ`StarPanel` é‡å†™ `WriteIndex`,在输出 HTML å‰ç”¨ `UiLocalization.GetDictionary` çš„è¯è¡¨æŒ‰å½“å‰è¯è¨€å¯¹æºæ–‡åšæœ€é•¿åŒ¹é…æ›¿æ¢ï¼Œç®€ä½“䏿–‡ç›´æŽ¥è¿”å›žæºæ–‡ï¼›å› æ¤ç™»å½•页ç‰é™æ€æ–‡æ¡ˆåœ¨æµè§ˆå™¨æ”¶åˆ° HTML æ—¶å·²æ˜¯ç›®æ ‡è¯è¨€ã€‚替æ¢åªä½œç”¨äºŽè„šæœ¬ä¸Žæ ·å¼ä¹‹å¤–çš„æ ‡è®°â€”â€”åŸºç±» `UiLocalization` 会连内è”脚本一起替æ¢ï¼Œæ³•è¯ã€ä¹Œå…‹å…°è¯è¯‘æ–‡ä¸çš„æ’‡å·ä¼šç ´åå•引å·å—ç¬¦ä¸²å¯¼è‡´è„šæœ¬è¯æ³•é”™è¯¯ã€‚è„šæœ¬å†…çš„ä¸æ–‡ç”±åŒç›®å½•çš„ `i18n.js` 负责:它把 DOM 文本节点ã€`placeholder`ã€`title` 与 `aria-label` 按è¯è¡¨æ›¿æ¢ï¼Œ`MutationObserver` è¦†ç›–åŠ¨æ€æ¸²æŸ“。è¯è¡¨æ˜¯ `wwwroot/i18n/<è¯è¨€>.json`(31 ä»½ï¼Œé”®ä¸ºä¸æ–‡æºæ–‡ï¼‰ï¼Œéš wwwroot ä½œä¸ºåµŒå…¥èµ„æºæ‰“进程åºé›†ï¼Œç”±å†…åµŒé™æ€æ–‡ä»¶æœåŠ¡æŒ‰ `/i18n/<è¯è¨€>.json` æä¾›ï¼ŒæœåŠ¡ç«¯ä¸Žæµè§ˆå™¨ç«¯å…±ç”¨åŒä¸€ä»½è¯è¡¨ã€‚
`i18n.js` å¯åŠ¨æ—¶è¯»å– `/api/Languages` 拿到è¯è¨€æ¸…å•与è¯è¨€çжæ€ï¼šæœ‰å›ºå®šè¯è¨€ï¼ˆ`current`)时界é¢è·Ÿéšå®ƒï¼Œæœªå›ºå®šæ—¶è·ŸéšæœåŠ¡ç«¯å®žé™…ç”Ÿæ•ˆè¯è¨€ï¼ˆ`effective`),接å£ä¸å¯ç”¨æ—¶æ‰é€€å›ž `navigator.language`,并å¤ç”¨åŒä¸€å¥—归一化规则(`zh-CN` æ˜ å°„åˆ° `zh-Hans`)。下拉项显示æœåŠ¡ç«¯è¿”å›žå称括å·å†…的本地å(`å¾·è¯ (Deutsch)` → `Deutsch`),é¿å…䏿–‡å‰ç¼€æ··å…¥å…¶ä»–è¯è¨€çš„界é¢ã€‚颿¿å³ä¸Šè§’çš„è¯è¨€é€‰æ‹©å™¨è°ƒç”¨ `/api/SetLanguage` 把选择写入 Core 全局é…ç½® `NewLife.Setting.Language`(留空æ¢å¤è‡ªåŠ¨è¯†åˆ«ï¼‰ï¼Œå†™å…¥åŽé‡è½½é¡µé¢ï¼›é˜¿æ‹‰ä¼¯è¯ã€æ³¢æ–¯è¯ã€å¸Œä¼¯æ¥è¯åŒæ—¶æŠŠæ ¹å…ƒç´ æ–¹å‘设为 `rtl`。`/api/SetLanguage` 需è¦ç™»å½•ï¼Œå› æ¤ç™»å½•é¡µå›ºå®šè·ŸéšæœåŠ¡ç«¯è¯è¨€ï¼Œç™»å½•åŽæ‰èƒ½åˆ‡æ¢ã€‚
匹é…规则:文本节点整串命ä¸è¯è¡¨ä¼˜å…ˆï¼›å¦åˆ™æŒ‰æœ€é•¿çŸè¯å¤šè½®æ›¿æ¢ï¼Œç›´åˆ°æ²¡æœ‰å¯å‘½ä¸çš„è¯æ¡ï¼Œå› æ¤ `确定è¦é‡å¯æœåŠ¡å—?é‡å¯åŽæœåŠ¡å°†è‡ªåŠ¨æ¢å¤ã€‚` 这类拼å¥ä¼šè¢«æ•´ä½“译出。å•å—è¯æ¡åªåœ¨ä¸¤ä¾§éƒ½ä¸æ˜¯æ±‰å—时替æ¢ï¼Œé¿å…误伤。`placeholder`ã€`title` 与 `aria-label` åŒæ ·èµ°è¯è¡¨ç¿»è¯‘ã€‚éœ€è¦æ‹¼æŽ¥åЍæ€å€¼æˆ– HTML çš„å¥å用 `StarI18N.t()` 显å¼ç¿»è¯‘åŽå†æ’入,å ä½ç¬¦å†™æˆ `{0}`ï¼Œä¾‹å¦‚åˆ é™¤ç¡®è®¤å¼¹çª—çš„ `确定è¦åˆ é™¤åæœåŠ¡ {0} å—?`。
接å£è¯·æ±‚ä¸å†æºå¸¦è¯è¨€å‚数:固定è¯è¨€å†™å…¥ `NewLife.Setting.Language` åŽç”± `Localization` 进程级生效,æœåŠ¡ç«¯æ¶ˆæ¯ã€æŽ¥å£é”™è¯¯ä¸Žæ—¥å¿—ä¿æŒä¸€è‡´ã€‚颿¿ä¸æŒ‰è¯·æ±‚头识别è¯è¨€ï¼Œæœªå›ºå®šæ—¶æ‰€æœ‰è¯·æ±‚都使用进程的界é¢è¯è¨€ï¼Œå› æ¤ `i18n.js` 以 `/api/Languages` çš„ `effective` ä¸ºå‡†ï¼Œè€Œä¸æ˜¯æµè§ˆå™¨è¯è¨€ï¼Œé¿å…界é¢ä¸ŽæœåŠ¡ç«¯æ¶ˆæ¯ä¸ä¸€è‡´ã€‚`StarApi` ä¸å†è‡ªè¡Œè§£æžè¯è¨€ã€‚
æ–°å¢žé¢æ¿æ–‡æ¡ˆæ—¶å…ˆæ”¹ `index.html` çš„ä¸æ–‡æºæ–‡ï¼Œå†æŠŠè¯¥æºæ–‡è¡¥è¿› 31 份è¯è¡¨ JSONï¼›`Stardust.Tests` çš„ `StarPanelLocaleTests` ä¼šæ ¡éªŒé—æ¼ã€‚颿¿è¯è¡¨ä¸Ž `StarAgent/Locales` çš„åŽç«¯è¯è¡¨äº’相独立:åŽè€…åªç”¨äºŽæœåŠ¡ç«¯ç”Ÿæˆçš„æ¶ˆæ¯ï¼ˆé…置项文案ã€è¿è¡Œæ—¶é•¿ç‰ï¼‰ï¼Œä¸å‚与 `index.html` 䏿–‡æºæ–‡çš„æ›¿æ¢ã€‚
ä¸è¦åœ¨ csproj é‡Œå†™æ» `System.Globalization.Invariant`:它会让该项目所有平å°ã€æ‰€æœ‰ç›®æ ‡æ¡†æž¶çš„æž„建永久失去多è¯è¨€èƒ½åŠ›ï¼ˆStarAgent 2020 年为 linux-arm æ— ICU çŽ¯å¢ƒåŠ çš„å¼€å…³å³å±žæ¤ç±»ï¼Œ2026 年已移除)。
## 8. 程åºé›†ä¸ŽåŒ…元数æ®
è·Ÿéš NewLife.Core v12 规范,程åºé›†æ ‡é¢˜ä¸Žæè¿°é‡‡ç”¨ã€Œè‹±æ–‡æºæ–‡ + è¯è¡¨ + 展示层翻译ã€ï¼š
- csproj çš„ `AssemblyTitle` 与 `Description` 使用**è‹±æ–‡æºæ–‡**,åŒä¸€å—符串作为 key 写入本程åºé›† `Locales/*.yml`ï¼šä¸æ–‡ç»™è¯‘文,其余è¯è¨€åŒæ ·å†™å‡ºè¯‘æ–‡ï¼Œç¼ºå¤±æ—¶å›žé€€è‹±æ–‡æºæ–‡ã€‚
- 展示点用 `L.T(value)` 翻译:NewLife.Agent çš„ `DisplayTitle`/`DisplayDescription` å·²å†…ç½®ï¼ˆæŽ§åˆ¶å°æ ‡é¢˜ã€`-status` 输出ã€é¢æ¿ã€Windows SCM/systemd 注册),`DeployAgent` æŽ§åˆ¶å°æè¿°è‡ªè¡Œè°ƒç”¨ã€‚æœåŠ¡åä¸ç¿»è¯‘,用户é…置的 `DisplayName` åŒæ ·èµ° `L.T`。
- `check-locales.ps1` çš„å¤å„¿é”®æ‰«æå·²è¦†ç›– `.csproj`ï¼Œå…ƒæ•°æ® key ä¸ä¼šè¢«è¯¯åˆ¤ã€‚
- NuGet åŒ…å¦æœ‰ `<PackageDescription>` 承接ä¸è‹±åŒè¯æè¿°ï¼›`Description` ä¿æŒçº¯è‹±æ–‡ä¾›å射使用,`PackageTags` å¯ä¿ç•™ä¸è‹±å…³é”®è¯ã€‚
`StarAgent/MyService` 与 `DeployAgent/DeployService` çš„ `DisplayName`/`Description` 与å„自 csproj ä¿æŒåŒä¸€è‹±æ–‡ä¸²ï¼Œå®‰è£…æ³¨å†Œå’Œé¢æ¿æ˜¾ç¤ºéšè¯è¨€åˆ‡æ¢ã€‚
æ’件与测试的 `[DisplayName]`/`[Description]` 属表结构或æ’件元数æ®ï¼Œä¿æŒé¡¹ç›®æ¯è¯ï¼Œä¸è¿›å…¥è¿è¡Œæ—¶æ¶ˆæ¯æ£€æŸ¥ï¼›ç¡®éœ€ç¿»è¯‘时按 Core è§„èŒƒçš„å…ƒæ•°æ® key 机制å•独登记。
é…置项 `[Description]` å·²æ”¹ä¸ºè‹±æ–‡æºæ–‡ï¼ŒåŒä¸€å—符串作为 key 写入å„组件 `Locales/*.yml` å¹¶è¡¥é½ 31 ç§è¯è¨€è¯‘文;Core 在读å–与ä¿å˜é…置时按 `Localization.LogCulture` æŸ¥è¡¨è¾“å‡ºï¼Œç¼ºå¤±å›žé€€è‹±æ–‡æºæ–‡ã€‚实体与控制器元数æ®ï¼ˆèœå•åã€å—段æè¿°ï¼‰ä»ä¿æŒä¸æ–‡æºæ–‡ï¼Œèµ°ç¬¬ 7 节的界é¢å…ƒæ•°æ®è¯æ¡ï¼š`Stardust.Data/Locales/metadata-keys.json` 与 `Stardust.Web/Locales/metadata-keys.json` ç™»è®°ä¸æ–‡æºæ–‡é”®ï¼Œ30 ç§è¯è¨€çš„译文写在åŒä¸€ `Locales` 目录的 `*.yml` ä¸ã€‚
`Installer`ã€`StarAgentTool`ã€`StarAgent.Android`ã€`StarAgent.iOS`ã€`MySqlAgent` 未纳入解决方案的è¯è¡¨ä½“系,暂ä¿ç•™ä¸æ–‡ `AssemblyTitle`/`Description`;它们ä¸å‚与æœåŠ¡æ³¨å†Œä¸Žé¢æ¿å±•示。
|