feat(i18n): ④ l10n 漂移闸 + codegen 改纯 node 确定性输出

- gen_l10n_dart.mjs 去掉 dart format:其输出依赖包解析状态、跨环境不稳(首次 4 空格、
  重跑 8 空格),会让漂移闸误报。改为确定性终态格式(list 单行 const [])。
  analyze 不校验格式,不影响;gen 现零 dart 依赖、纯 node 确定。
- ci/check-codegen-drift.sh 增 l10n 段:重生成 strings_*.dart → git diff 非空即 fail
  (与 token 段同法,纯 node)。
- 验证:往返无损、重生成零 diff(闸稳定)、flutter analyze 0 issue、244 测试全绿。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A79VtQA1BwTuQN1ThpvYpo
This commit is contained in:
wangjia
2026-07-27 08:55:21 +08:00
parent f76e3cdffd
commit 13e0ba6d47
8 changed files with 110 additions and 263 deletions
+27 -66
View File
@@ -61,8 +61,7 @@ class StringsRu extends AppText {
@override
String get watchAdMore => 'Смотрите рекламу, чтобы добавить время';
@override
String get quotaExhaustedNotice =>
'Дневной бесплатный лимит исчерпан. Оформите подписку, чтобы продолжить подключение.';
String get quotaExhaustedNotice => 'Дневной бесплатный лимит исчерпан. Оформите подписку, чтобы продолжить подключение.';
@override
String get adPlaying => 'Реклама воспроизводится…';
@override
@@ -70,8 +69,7 @@ class StringsRu extends AppText {
@override
String get quotaDesktopTitle => 'Дневной бесплатный лимит исчерпан';
@override
String get quotaDesktopBody =>
'Смотрите рекламу в мобильном приложении, чтобы добавить время, или оформите подписку для безлимитного доступа.';
String get quotaDesktopBody => 'Смотрите рекламу в мобильном приложении, чтобы добавить время, или оформите подписку для безлимитного доступа.';
@override
String get gotIt => 'Понятно';
@override
@@ -81,16 +79,13 @@ class StringsRu extends AppText {
@override
String get smartSelect => 'Умный выбор';
@override
String get nodeSwitchBody =>
'Текущее соединение будет разорвано и восстановлено через этот узел.';
String get nodeSwitchBody => 'Текущее соединение будет разорвано и восстановлено через этот узел.';
@override
String get nodeSwitchConfirm => 'Переключить';
@override
String get nodeUnhealthySwitched =>
'Текущий узел недоступен — переключение выполнено автоматически';
String get nodeUnhealthySwitched => 'Текущий узел недоступен — переключение выполнено автоматически';
@override
String get nodeUnhealthyError =>
'Текущий узел недоступен — переподключитесь или смените узел';
String get nodeUnhealthyError => 'Текущий узел недоступен — переподключитесь или смените узел';
@override
String get nodeReconnecting => 'Сеть нестабильна, переподключение…';
@override
@@ -148,8 +143,7 @@ class StringsRu extends AppText {
@override
String get deviceLimitTitle => 'Достигнут лимит устройств';
@override
String get deviceLimitDesc =>
'Ваш тариф допускает до %s устройств. Удалите одно, чтобы продолжить.';
String get deviceLimitDesc => 'Ваш тариф допускает до %s устройств. Удалите одно, чтобы продолжить.';
@override
String get deviceLimitRemoveOldest => 'Удалить давно неиспользуемое';
@override
@@ -179,18 +173,15 @@ class StringsRu extends AppText {
@override
String get devSave => 'Сохранить';
@override
String get devForceLogoutConfirm =>
'Это завершит сеанс входа устройства — оно будет отключено и потребует повторного входа. Устройство останется в списке.';
String get devForceLogoutConfirm => 'Это завершит сеанс входа устройства — оно будет отключено и потребует повторного входа. Устройство останется в списке.';
@override
String get devClearLoginConfirm =>
'Это полностью удалит устройство из списка и отзовёт его сеанс входа и учётные данные передачи данных. Чтобы снова использовать устройство, нужно войти заново.';
String get devClearLoginConfirm => 'Это полностью удалит устройство из списка и отзовёт его сеанс входа и учётные данные передачи данных. Чтобы снова использовать устройство, нужно войти заново.';
@override
String get devCancel => 'Отмена';
@override
String get sessionRevokedTitle => 'Это устройство было отключено';
@override
String get sessionRevokedBody =>
'Вход с этого устройства завершён с другого устройства вашего аккаунта. Пожалуйста, войдите снова.';
String get sessionRevokedBody => 'Вход с этого устройства завершён с другого устройства вашего аккаунта. Пожалуйста, войдите снова.';
@override
String get sessionRevokedOk => 'Войти снова';
@override
@@ -222,8 +213,7 @@ class StringsRu extends AppText {
@override
String get smartRoute => 'Умная маршрутизация';
@override
String get smartRouteSub =>
'Ускорение для зарубежных ресурсов, прямой доступ для локальных';
String get smartRouteSub => 'Ускорение для зарубежных ресурсов, прямой доступ для локальных';
@override
String get killSwitch => 'Kill Switch';
@override
@@ -233,8 +223,7 @@ class StringsRu extends AppText {
@override
String get webUserCenter => 'Личный кабинет (веб)';
@override
String get updateNotesFallback =>
'Это обновление включает исправления и улучшения стабильности.';
String get updateNotesFallback => 'Это обновление включает исправления и улучшения стабильности.';
@override
String get updateLater => 'Позже';
@override
@@ -242,8 +231,7 @@ class StringsRu extends AppText {
@override
String get updateUpToDate => 'У вас установлена последняя версия';
@override
String get updateCheckFailed =>
'Не удалось проверить обновления, попробуйте позже';
String get updateCheckFailed => 'Не удалось проверить обновления, попробуйте позже';
@override
String get freePlan => 'Бесплатный';
@override
@@ -273,8 +261,7 @@ class StringsRu extends AppText {
@override
String get buyTitle => 'Где купить';
@override
String get buySub =>
'Оплата внутри приложения недоступна. Получить код можно через:';
String get buySub => 'Оплата внутри приложения недоступна. Получить код можно через:';
@override
String get chStore => 'Магазин самообслуживания';
@override
@@ -282,8 +269,7 @@ class StringsRu extends AppText {
@override
String get contactTitle => 'Связаться с нами';
@override
String get contactIntro =>
'Нужна помощь? Свяжитесь с нами любым способом ниже — обычно отвечаем за считанные минуты.';
String get contactIntro => 'Нужна помощь? Свяжитесь с нами любым способом ниже — обычно отвечаем за считанные минуты.';
@override
String get contactEmail => 'Поддержка по эл. почте';
@override
@@ -325,8 +311,7 @@ class StringsRu extends AppText {
@override
String get forgotPw => 'Забыли пароль?';
@override
String get tos =>
'Продолжая, вы принимаете наши Условия и Политику конфиденциальности';
String get tos => 'Продолжая, вы принимаете наши Условия и Политику конфиденциальности';
@override
String get obSkip => 'Пропустить';
@override
@@ -338,13 +323,11 @@ class StringsRu extends AppText {
@override
String get ob1Title => 'Подключение по всему миру';
@override
String get ob1Sub =>
'80+ локаций, автоматический выбор самого быстрого маршрута, надёжная стабильность.';
String get ob1Sub => '80+ локаций, автоматический выбор самого быстрого маршрута, надёжная стабильность.';
@override
String get ob2Title => 'Разрешите настройку сети';
@override
String get ob2Sub =>
'Система запросит добавление сетевого профиля для создания зашифрованного туннеля.';
String get ob2Sub => 'Система запросит добавление сетевого профиля для создания зашифрованного туннеля.';
@override
String get ob3Title => 'Приватность по умолчанию';
@override
@@ -378,18 +361,15 @@ class StringsRu extends AppText {
@override
String get payNetworkLabel => 'Сеть';
@override
String get payExactAmountHint =>
'Отправьте точную сумму — активация произойдёт автоматически';
String get payExactAmountHint => 'Отправьте точную сумму — активация произойдёт автоматически';
@override
String get copied => 'Скопировано';
@override
String get openAlipay => 'Оплатить через Alipay';
@override
String get openAlipayHint =>
'Вернитесь после оплаты — страница обновится автоматически';
String get openAlipayHint => 'Вернитесь после оплаты — страница обновится автоматически';
@override
String get openAlipayFailed =>
'Не удалось открыть Alipay. Проверьте, установлен ли он, или повторите';
String get openAlipayFailed => 'Не удалось открыть Alipay. Проверьте, установлен ли он, или повторите';
@override
String get awaitingPayment => 'Ожидание оплаты';
@override
@@ -409,11 +389,9 @@ class StringsRu extends AppText {
@override
String get qrNotSupported => 'Скопируйте и откройте в Alipay';
@override
String get orderCreateFailed =>
'Не удалось создать заказ, повторите попытку позже';
String get orderCreateFailed => 'Не удалось создать заказ, повторите попытку позже';
@override
String get payUnsupportedRenderType =>
'Не поддерживается, смените способ оплаты';
String get payUnsupportedRenderType => 'Не поддерживается, смените способ оплаты';
@override
String get payNoActiveOrder => 'Нет активных заказов';
@override
@@ -457,8 +435,7 @@ class StringsRu extends AppText {
@override
String get inviteEntry => 'Приглашайте друзей и получайте членство';
@override
String get inviteEntrySub =>
'Друг регистрируется · оба получают дни подписки';
String get inviteEntrySub => 'Друг регистрируется · оба получают дни подписки';
@override
String get inviteTitle => 'Пригласить друзей';
@override
@@ -528,26 +505,11 @@ class StringsRu extends AppText {
@override
List<String> get days7 => const ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс'];
@override
List<String> get featsFree => const [
'Только 1 базовый узел',
'10 минут в день',
'Смотрите рекламу, чтобы начать',
'7 дней бесплатно при регистрации',
];
List<String> get featsFree => const ['Только 1 базовый узел', '10 минут в день', 'Смотрите рекламу, чтобы начать', '7 дней бесплатно при регистрации'];
@override
List<String> get featsPro => const [
'80+ локаций',
'Безлимитно · быстро',
'5 устройств',
'Оптимизация для стриминга',
];
List<String> get featsPro => const ['80+ локаций', 'Безлимитно · быстро', '5 устройств', 'Оптимизация для стриминга'];
@override
List<String> get featsTeam => const [
'Всё из Pro',
'10 мест',
'Единый счёт',
'Приоритетная поддержка',
];
List<String> get featsTeam => const ['Всё из Pro', '10 мест', 'Единый счёт', 'Приоритетная поддержка'];
@override
String adRewarded(int minutes) => '+$minutes мин добавлено';
@override
@@ -555,8 +517,7 @@ class StringsRu extends AppText {
@override
String autoConnectingTo(String name) => 'Автоподключение к $name';
@override
String updateAvailableTitle(String version) =>
'Доступна новая версия v$version';
String updateAvailableTitle(String version) => 'Доступна новая версия v$version';
@override
String codeSentTo(String email) => 'Код отправлен на $email';
}