多语言布局:自动缩字、按语言放宽钱包表与手册列表、缩短过长标签

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ZHU MINCHAOandClaude Sonnet 5.5 committed 2026-11-04 14:55:11 +08:00
1 parent f5de68aea9
commit fa8f416545
11 files changed
+70 -23

No files matched your search

+1
View File
@@ -5,6 +5,7 @@
### 第九阶段:剧情与收尾
- 多语言:新增 de fr es pl ru ja 六种语言表(连同 zh en 共 8 种,对应游戏主菜单的语言)。英文表是源文,其他语言由 Gemini 分块译出(术语表 `docs/i18n-glossary.md` 在运营仓库,游戏已有的词用官方译名),未经人工校对,文件头有说明。键集合、占位符、`§ ¶ \\n` 标记由单元测试逐语言核对,非中日语言里不得出现汉字或假名。
- 多语言布局:表格单元、按钮文字放不下时自动缩小字号(最小 68%)再省略,商品名限定两行内缩放;de fr es pl ru 的钱包表、手册章节列表、硬件钱包粘贴键按语言放宽;ru de 等少数过长标签改短(永续“加保证金”键、收益池列头)。
- 中文“匿名币”改称“隐私币”(英文 privacy coin),手册、黑市标签同步。
- 英文手册里写死的“六天”“7 天”改为占位符 `{BmBulkDays}`、`{AmlWindowDays}`,和中文一致。
- 手册里剩下的写死数字也进了 `Tuning`:出金审查污点系数 `AmlTaintMult` 与超额权重 `AmlExcessWeight`、出售速度界线 `BmSellFastMult` / `BmSellDay1Mult`;剧情联动的期限和涨幅改用已有的 `Story*` / `Tumbler*` 常量占位。占位符新增 `{Name:+%}`(倍数显示成涨幅)。
+6
View File
@@ -14,6 +14,12 @@ namespace Decentralized
public static class L
{
public static string T(string key, params object[] args) => ModHelper.GetLocalized(ModInfo.Id, key, args);
/// <summary>当前游戏语言代码(en zh de fr es pl ru ja)。</summary>
public static string Code => LocHelper.GetCurrentLocaleCode() ?? "en";
/// <summary>译文普遍比中英文长的语言:表格列宽、操作键区按此放宽。</summary>
public static bool Wide { get { var c = Code; return c == "de" || c == "fr" || c == "es" || c == "pl" || c == "ru"; } }
}
public static class Log
+4 -2
View File
@@ -11,7 +11,9 @@ namespace Decentralized
{
public const string Id = "pd_handbook";
private const float WinW = 828f, WinH = 680f, ScreenW = 768f, ScreenH = 578f, Bezel = 6f;
private const float ListW = 190f, TextW = 540f;
private static float ListW => L.Wide ? 276f : 190f;
private static float RowW => L.Wide ? ListW - 52f : ListW - 12f;
private static float TextW => L.Wide ? 456f : 540f;
private static int _chapter;
private static readonly List<GameObject> _picked = new List<GameObject>();
@@ -61,7 +63,7 @@ namespace Decentralized
for (int i = 0; i < chapters.Length; i++)
{
int k = i;
PledgerSkin.TableRow(b, ListW - 12f, 30f, new[] { ListW - 12f }, new[] { TextAnchor.MiddleLeft },
PledgerSkin.TableRow(b, RowW, 30f, new[] { RowW }, new[] { TextAnchor.MiddleLeft },
new[] { Title(chapters[i]) }, i == _chapter, () => { _chapter = k; Open(); }, _rows, _clear);
}
b.End();
@@ -498,7 +498,7 @@ perp.open,Eröffnen
perp.margin,Margin (CRC)
perp.preview,Nominal {0} Ausführung {1}
perp.do.open,Eröffnen
perp.do.add,Margin hinzufügen
perp.do.add,+ Margin
perp.do.close,Schließen
perp.opened,"{0} {1} eröffnet: Margin {2} CRC, {3}×."
perp.added,{0} CRC Margin hinzugefügt.
@@ -683,11 +683,11 @@ air.err.wallet,Mit der freigebenden Wallet signieren.
pool.h,Drittanbieter-Renditepools (CRC)
pool.none,Derzeit keine vorhanden.
pool.col.name,Pool
pool.col.rate,Tageszins
pool.col.rate,Zins
pool.col.reserve,Reserve
pool.col.lock,Timelock
pool.col.lock,Sperre
pool.col.audit,Audit
pool.col.mine,Eigene
pool.col.mine,Mein
pool.yes,ja
pool.no,nein
pool.pending,in Auszahlung
1 # Probably Decentralized localization table: German (de)
498 perp.margin
499 perp.preview
500 perp.do.open
501 perp.do.add
502 perp.do.close
503 perp.opened
504 perp.added
683 pool.h
684 pool.none
685 pool.col.name
686 pool.col.rate
687 pool.col.reserve
688 pool.col.lock
689 pool.col.audit
690 pool.col.mine
691 pool.yes
692 pool.no
693 pool.pending
@@ -498,7 +498,7 @@ perp.open,Abrir
perp.margin,Margen (CRC)
perp.preview,Nocional {0} ejecución {1}
perp.do.open,Abrir
perp.do.add,Agregar margen
perp.do.add,+ Margen
perp.do.close,Cerrar
perp.opened,"Se abrió {0} {1}: margen {2} CRC, {3}×."
perp.added,Se agregaron {0} CRC de margen.
@@ -683,11 +683,11 @@ air.err.wallet,Firmar con la billetera que aprobó.
pool.h,Fondos de rendimiento de terceros (CRC)
pool.none,Ninguno por ahora.
pool.col.name,Fondo
pool.col.rate,Tasa diaria
pool.col.rate,Tasa/día
pool.col.reserve,Reserva
pool.col.lock,Bloqueo
pool.col.audit,Auditoría
pool.col.mine,Mi depósito
pool.col.mine,Mío
pool.yes,sí
pool.no,no
pool.pending,retirando
1 # Probably Decentralized localization table: Spanish (es)
498 perp.margin
499 perp.preview
500 perp.do.open
501 perp.do.add
502 perp.do.close
503 perp.opened
504 perp.added
683 pool.h
684 pool.none
685 pool.col.name
686 pool.col.rate
687 pool.col.reserve
688 pool.col.lock
689 pool.col.audit
690 pool.col.mine
691 pool.yes
692 pool.no
693 pool.pending
@@ -498,7 +498,7 @@ perp.open,Ouvrir
perp.margin,Marge (CRC)
perp.preview,Notionnel {0} exécution {1}
perp.do.open,Ouvrir
perp.do.add,Ajouter de la marge
perp.do.add,+ Marge
perp.do.close,Fermer
perp.opened,"Position {0} {1} ouverte : marge {2} CRC, {3}×."
perp.added,{0} CRC de marge ajoutés.
@@ -687,7 +687,7 @@ pool.col.rate,Taux/j
pool.col.reserve,Réserve
pool.col.lock,Blocage
pool.col.audit,Audit
pool.col.mine,Dépôt
pool.col.mine,Mon dépôt
pool.yes,oui
pool.no,non
pool.pending,retrait en cours
1 # Probably Decentralized localization table: French (fr)
498 perp.margin
499 perp.preview
500 perp.do.open
501 perp.do.add
502 perp.do.close
503 perp.opened
504 perp.added
687 pool.col.reserve
688 pool.col.lock
689 pool.col.audit
690 pool.col.mine
691 pool.yes
692 pool.no
693 pool.pending
@@ -406,7 +406,7 @@ poin.out.done,Wypłacono {0} {1} na {2}.
wallets.count,Portfele {0}/{1}
wallets.col.name,Nazwa
wallets.col.addr,Adres
wallets.col.taint,Skażenie
wallets.col.taint,Skażone
wallets.open,Otwórz
wallets.use,Połącz
wallets.current,Połączono
@@ -498,7 +498,7 @@ perp.open,Otwórz
perp.margin,Depozyt zabezpieczający (CRC)
perp.preview,Nominał {0} realizacja {1}
perp.do.open,Otwórz
perp.do.add,Dodaj depozyt
perp.do.add,+ Depozyt
perp.do.close,Zamknij
perp.opened,"Otwarto {0} {1}: depozyt {2} CRC, {3}×."
perp.added,Dodano {0} CRC depozytu.
1 # Probably Decentralized localization table: Polish (pl)
406 wallets.count
407 wallets.col.name
408 wallets.col.addr
409 wallets.col.taint
410 wallets.open
411 wallets.use
412 wallets.current
498 perp.margin
499 perp.preview
500 perp.do.open
501 perp.do.add
502 perp.do.close
503 perp.opened
504 perp.added
@@ -154,7 +154,7 @@ avg.short,ср. покупка {0}
avg.none,—
col.asset,Актив
col.amount,Сумма
col.value,Стоимость
col.value,Оценка
col.avg,Ср. покупка
bank.cash.k,Кредиты
bank.rent.k,Аренда
@@ -406,7 +406,7 @@ poin.out.done,{0} {1} выведено на {2}.
wallets.count,Кошельки {0}/{1}
wallets.col.name,Название
wallets.col.addr,Адрес
wallets.col.taint,Загрязнение
wallets.col.taint,Грязные
wallets.open,Открыть
wallets.use,Подключить
wallets.current,Подключено
@@ -425,7 +425,7 @@ wallets.destroy.sure,Подтвердить
wallets.destroy.notempty,В {0} ещё есть монеты. Сначала выведите их.
wallets.destroyed,Уничтожен {0}: {1}
hw.balance.value,≈ {0}
wallets.rename,Переименовать
wallets.rename,Назвать
wallets.rename.h,Новое имя для {0}
wallets.rename.ok,ОК
hw.fee.slow,Медленно
@@ -498,7 +498,7 @@ perp.open,Открыть
perp.margin,Маржа (CRC)
perp.preview,Номинал {0} цена исполнения {1}
perp.do.open,Открыть
perp.do.add,Добавить маржу
perp.do.add,+ Маржа
perp.do.close,Закрыть
perp.opened,"Открыто {0} {1}: маржа {2} CRC, {3}×."
perp.added,Добавлено {0} CRC маржи.
@@ -685,7 +685,7 @@ pool.none,Сейчас пулов нет.
pool.col.name,Пул
pool.col.rate,Ставка
pool.col.reserve,Резерв
pool.col.lock,Таймлок
pool.col.lock,Лок
pool.col.audit,Аудит
pool.col.mine,Вклад
pool.yes,да
1 # Probably Decentralized localization table: Russian (ru)
154 avg.none
155 col.asset
156 col.amount
157 col.value
158 col.avg
159 bank.cash.k
160 bank.rent.k
406 wallets.count
407 wallets.col.name
408 wallets.col.addr
409 wallets.col.taint
410 wallets.open
411 wallets.use
412 wallets.current
425 wallets.destroy.notempty
426 wallets.destroyed
427 hw.balance.value
428 wallets.rename
429 wallets.rename.h
430 wallets.rename.ok
431 hw.fee.slow
498 perp.margin
499 perp.preview
500 perp.do.open
501 perp.do.add
502 perp.do.close
503 perp.opened
504 perp.added
685 pool.col.name
686 pool.col.rate
687 pool.col.reserve
688 pool.col.lock
689 pool.col.audit
690 pool.col.mine
691 pool.yes
+36
View File
@@ -123,6 +123,7 @@ namespace Decentralized
b.BeginPanel(w, height - 6f, align, 0f, 0f);
if (clear != null) clear.Add(b.Last.image);
b.AddLabel(i < texts.Length ? texts[i] : "");
FitCurrent(b);
b.End();
}
b.End();
@@ -142,12 +143,46 @@ namespace Decentralized
b.BeginPanel(w, height - 6f, align, 0f, 0f);
if (clear != null) clear.Add(b.Last.image);
b.AddLabel(i < headers.Length ? "<b>" + headers[i] + "</b>" : "");
FitCurrent(b);
b.End();
}
b.End();
b.End();
}
/// <summary>单行固定宽度的文字放不下时先缩小字号(最小 68%),再省略:各语言译文长度不同,不靠逐语言调列宽。</summary>
public static void Fit(TMP_Text t)
{
if (t == null) return;
t.enableWordWrapping = false;
t.enableAutoSizing = true;
t.fontSizeMax = t.fontSize;
t.fontSizeMin = Mathf.Round(t.fontSize * 0.68f);
t.overflowMode = TextOverflowModes.Ellipsis;
}
/// <summary>刚加的标签限定在固定高度内(可折行),放不下时缩小字号:商品名等多语言下长度差很大的标题用。</summary>
public static void FitBox(CustomUIBuilder b, float height)
{
var c = b.CurrentContainer;
if (c == null || c.childCount == 0) return;
var t = c.GetChild(c.childCount - 1).GetComponentInChildren<TextMeshProUGUI>(true);
if (t == null) return;
var le = t.gameObject.GetComponent<LayoutElement>() ?? t.gameObject.AddComponent<LayoutElement>();
le.minHeight = height; le.preferredHeight = height; le.flexibleHeight = 0f;
t.enableAutoSizing = true;
t.fontSizeMax = t.fontSize;
t.fontSizeMin = Mathf.Round(t.fontSize * 0.7f);
t.overflowMode = TextOverflowModes.Ellipsis;
}
/// <summary>对当前容器(刚加完标签的单元面板)里的文字做 Fit。</summary>
public static void FitCurrent(CustomUIBuilder b)
{
if (b.CurrentContainer == null) return;
foreach (var t in b.CurrentContainer.GetComponentsInChildren<TextMeshProUGUI>(true)) Fit(t);
}
/// <summary>应用整套 PLEDGER 皮肤:机身、边框、墨水屏、刻字、文字颜色、触控键、滚动条与数据表行。</summary>
public static void Apply(CustomUIWindow win, RectTransform screen, Image bezel, Image paper, ICollection<GameObject> picked, ICollection<Image> clear, IEnumerable<RowDef> rows = null)
{
@@ -230,6 +265,7 @@ namespace Decentralized
}
foreach (var t in btn.GetComponentsInChildren<TextMeshProUGUI>(true))
{
Fit(t);
t.color = on ? Ink : KeyText;
if (on) t.fontStyle |= FontStyles.Bold;
}
+6 -4
View File
@@ -119,6 +119,7 @@ namespace Decentralized
b.BeginPanel(w, 26f, align, 0f, 0f);
_clear.Add(b.Last.image);
b.AddLabel(header ? "<b>" + text + "</b>" : text);
PledgerSkin.FitCurrent(b);
b.End();
}
@@ -984,7 +985,7 @@ namespace Decentralized
{
var d = Session.Data;
b.AddLabel(L.T("wallets.count", d.Wallets.Count, Tuning.WalletMax), "wallets_count_dim");
var cols = new[] { 100f, 110f, 85f, 70f, 375f };
var cols = L.Wide ? new[] { 100f, 92f, 62f, 62f, 372f } : new[] { 100f, 110f, 85f, 70f, 375f };
var aligns = new[] { TextAnchor.MiddleLeft, TextAnchor.MiddleLeft, TextAnchor.MiddleRight, TextAnchor.MiddleRight, TextAnchor.MiddleLeft };
TableHeader(b, 740f, cols, aligns, L.T("wallets.col.name"), L.T("wallets.col.addr"), L.T("col.value"), L.T("wallets.col.taint"), "");
for (int i = 0; i < d.Wallets.Count; i++)
@@ -1009,7 +1010,7 @@ namespace Decentralized
Cell(b, cols[3], Fmt.Credits(w.Taint), false, TextAnchor.MiddleRight);
b.End();
b.End();
b.BeginPanel(cols[4] - 150f, 32f, TextAnchor.MiddleLeft, 0f, 0f);
b.BeginPanel(cols[4] - (L.Wide ? 112f : 150f), 32f, TextAnchor.MiddleLeft, 0f, 0f);
_clear.Add(b.Last.image);
b.BeginRow(4f, TextAnchor.MiddleLeft);
RowKey(b, L.T("wallets.open"), () => WalletWindow.Open(w));
@@ -1018,11 +1019,11 @@ namespace Decentralized
if (idx > 0) RowKey(b, _destroyPick == w.Address ? L.T("wallets.destroy.sure") : L.T("wallets.destroy"), () => DestroyWallet(w));
b.End();
b.End();
b.BeginPanel(84f, 32f, TextAnchor.MiddleRight, 0f, 0f);
b.BeginPanel(L.Wide ? 92f : 84f, 32f, TextAnchor.MiddleRight, 0f, 0f);
_clear.Add(b.Last.image);
b.BeginRow(0f, TextAnchor.MiddleRight);
if (idx != d.Current) RowKey(b, L.T("wallets.use"), () => { d.Current = idx; Session.Save(); Open(); });
else b.AddLabel(L.T("wallets.current"), "wallets_cur_dim");
else { b.AddLabel(L.T("wallets.current"), "wallets_cur_dim"); PledgerSkin.FitCurrent(b); }
b.End();
b.End();
b.End();
@@ -1754,6 +1755,7 @@ namespace Decentralized
var sprite = Icon(g.Item);
if (sprite != null) c.AddImage(sprite, 64f, 64f); else c.AddSpace(64f);
c.AddLabel("<b>" + ItemName(g.Item) + "</b>");
PledgerSkin.FitBox(c, 52f);
bool asic = g.Kind == GoodKind.Gpu && g.Model != null && Hardware.Specs.TryGetValue(g.Model, out var gs) && gs.Asic;
c.AddLabel(asic ? (g.Health >= 100 ? L.T("black.kind.asicnew") : L.T("black.kind.asic", (int)Math.Round(g.Health))) : g.Kind == GoodKind.Gpu ? (g.Health >= 100 ? L.T("black.kind.gpunew") : L.T("black.kind.gpu", (int)Math.Round(g.Health))) : L.T("black.kind." + g.Kind + (g.Kind == GoodKind.Contraband ? "." + g.Level : "")), "good_kind_dim");
if (!string.IsNullOrEmpty(line2)) c.AddLabel(line2, "good_line2_dim");
+1 -1
View File
@@ -265,7 +265,7 @@ namespace Decentralized
b.AddHeader(L.T("hw.send"));
b.AddLabel(L.T("hw.to"));
b.BeginRow(6f, TextAnchor.MiddleLeft);
b.BeginPanel(RowW - 70f, -1f, TextAnchor.MiddleLeft, 0f, 0f);
b.BeginPanel(RowW - (L.Wide ? 100f : 70f), -1f, TextAnchor.MiddleLeft, 0f, 0f);
_clear.Add(b.Last.image);
b.AddInput("0x", dev.To, s => dev.To = s, "to", s => dev.To = s);
b.End();