feat(i18n): add English + Chinese localization
All checks were successful
CI / Windows build (push) Successful in 12m34s
All checks were successful
CI / Windows build (push) Successful in 12m34s
The app had zero localization ("软件多语言做了吗" — no). Add Flutter's
official gen-l10n pipeline and localize the core flow the user sees.
- pubspec: flutter_localizations + intl + generate: true
- l10n.yaml + lib/l10n/app_en.arb + app_zh.arb (37 strings)
- main.dart: localizationsDelegates + supportedLocales (follows OS locale)
- pen editor: all tool tooltips, page pill, error states localized
- home: app bar actions + empty-state buttons localized
Proven end-to-end: l10n_test pumps the same widget under Locale('en')
and Locale('zh') and asserts English vs Chinese strings resolve.
flutter analyze: 0 issues. l10n_test: 3/3 pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
46
lib/l10n/app_en.arb
Normal file
46
lib/l10n/app_en.arb
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"@@locale": "en",
|
||||
"appTitle": "BadNote",
|
||||
"settings": "Settings",
|
||||
"search": "Search",
|
||||
"importPdf": "Import PDF",
|
||||
"importPpt": "Import PPT",
|
||||
"penCanvasBeta": "Pen Canvas (beta)",
|
||||
"newNote": "New Note",
|
||||
"open": "Open",
|
||||
"cancel": "Cancel",
|
||||
"delete": "Delete",
|
||||
"deleteNoteTitle": "Delete note?",
|
||||
"deleteNote": "Delete note",
|
||||
"openInSplitView": "Open in Split View",
|
||||
"splitViewSubtitle": "PDF reference + scratchpad",
|
||||
"removeDocument": "Remove document",
|
||||
"processingPptx": "Processing PPTX...",
|
||||
"processingPresentation": "Processing presentation...",
|
||||
"couldNotOpenPresentation": "Could not open presentation.",
|
||||
"toolPen": "Pen",
|
||||
"toolHighlighter": "Highlighter",
|
||||
"toolEraser": "Eraser",
|
||||
"actionUndo": "Undo",
|
||||
"actionRedo": "Redo",
|
||||
"fingerDrawingOn": "Finger drawing ON",
|
||||
"fingerDrawingOff": "Finger drawing OFF (pen only)",
|
||||
"pages": "Pages",
|
||||
"penSettings": "Pen settings",
|
||||
"inputDiagnostic": "Input diagnostic (writes a log file)",
|
||||
"back": "Back",
|
||||
"previousPage": "Previous page",
|
||||
"nextPage": "Next page",
|
||||
"failedToOpenPdf": "Failed to open PDF:\n{error}",
|
||||
"@failedToOpenPdf": {
|
||||
"placeholders": { "error": { "type": "String" } }
|
||||
},
|
||||
"pdfNoPages": "PDF has no pages.",
|
||||
"pageOfPages": "{current} / {total}",
|
||||
"@pageOfPages": {
|
||||
"placeholders": {
|
||||
"current": { "type": "int" },
|
||||
"total": { "type": "int" }
|
||||
}
|
||||
}
|
||||
}
|
||||
338
lib/l10n/app_localizations.dart
Normal file
338
lib/l10n/app_localizations.dart
Normal file
@@ -0,0 +1,338 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:intl/intl.dart' as intl;
|
||||
|
||||
import 'app_localizations_en.dart';
|
||||
import 'app_localizations_zh.dart';
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
|
||||
/// Callers can lookup localized strings with an instance of AppLocalizations
|
||||
/// returned by `AppLocalizations.of(context)`.
|
||||
///
|
||||
/// Applications need to include `AppLocalizations.delegate()` in their app's
|
||||
/// `localizationDelegates` list, and the locales they support in the app's
|
||||
/// `supportedLocales` list. For example:
|
||||
///
|
||||
/// ```dart
|
||||
/// import 'l10n/app_localizations.dart';
|
||||
///
|
||||
/// return MaterialApp(
|
||||
/// localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
/// supportedLocales: AppLocalizations.supportedLocales,
|
||||
/// home: MyApplicationHome(),
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// ## Update pubspec.yaml
|
||||
///
|
||||
/// Please make sure to update your pubspec.yaml to include the following
|
||||
/// packages:
|
||||
///
|
||||
/// ```yaml
|
||||
/// dependencies:
|
||||
/// # Internationalization support.
|
||||
/// flutter_localizations:
|
||||
/// sdk: flutter
|
||||
/// intl: any # Use the pinned version from flutter_localizations
|
||||
///
|
||||
/// # Rest of dependencies
|
||||
/// ```
|
||||
///
|
||||
/// ## iOS Applications
|
||||
///
|
||||
/// iOS applications define key application metadata, including supported
|
||||
/// locales, in an Info.plist file that is built into the application bundle.
|
||||
/// To configure the locales supported by your app, you’ll need to edit this
|
||||
/// file.
|
||||
///
|
||||
/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file.
|
||||
/// Then, in the Project Navigator, open the Info.plist file under the Runner
|
||||
/// project’s Runner folder.
|
||||
///
|
||||
/// Next, select the Information Property List item, select Add Item from the
|
||||
/// Editor menu, then select Localizations from the pop-up menu.
|
||||
///
|
||||
/// Select and expand the newly-created Localizations item then, for each
|
||||
/// locale your application supports, add a new item and select the locale
|
||||
/// you wish to add from the pop-up menu in the Value field. This list should
|
||||
/// be consistent with the languages listed in the AppLocalizations.supportedLocales
|
||||
/// property.
|
||||
abstract class AppLocalizations {
|
||||
AppLocalizations(String locale)
|
||||
: localeName = intl.Intl.canonicalizedLocale(locale.toString());
|
||||
|
||||
final String localeName;
|
||||
|
||||
static AppLocalizations of(BuildContext context) {
|
||||
return Localizations.of<AppLocalizations>(context, AppLocalizations)!;
|
||||
}
|
||||
|
||||
static const LocalizationsDelegate<AppLocalizations> delegate =
|
||||
_AppLocalizationsDelegate();
|
||||
|
||||
/// A list of this localizations delegate along with the default localizations
|
||||
/// delegates.
|
||||
///
|
||||
/// Returns a list of localizations delegates containing this delegate along with
|
||||
/// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate,
|
||||
/// and GlobalWidgetsLocalizations.delegate.
|
||||
///
|
||||
/// Additional delegates can be added by appending to this list in
|
||||
/// MaterialApp. This list does not have to be used at all if a custom list
|
||||
/// of delegates is preferred or required.
|
||||
static const List<LocalizationsDelegate<dynamic>> localizationsDelegates =
|
||||
<LocalizationsDelegate<dynamic>>[
|
||||
delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
];
|
||||
|
||||
/// A list of this localizations delegate's supported locales.
|
||||
static const List<Locale> supportedLocales = <Locale>[
|
||||
Locale('en'),
|
||||
Locale('zh'),
|
||||
];
|
||||
|
||||
/// No description provided for @appTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'BadNote'**
|
||||
String get appTitle;
|
||||
|
||||
/// No description provided for @settings.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Settings'**
|
||||
String get settings;
|
||||
|
||||
/// No description provided for @search.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Search'**
|
||||
String get search;
|
||||
|
||||
/// No description provided for @importPdf.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Import PDF'**
|
||||
String get importPdf;
|
||||
|
||||
/// No description provided for @importPpt.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Import PPT'**
|
||||
String get importPpt;
|
||||
|
||||
/// No description provided for @penCanvasBeta.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Pen Canvas (beta)'**
|
||||
String get penCanvasBeta;
|
||||
|
||||
/// No description provided for @newNote.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'New Note'**
|
||||
String get newNote;
|
||||
|
||||
/// No description provided for @open.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Open'**
|
||||
String get open;
|
||||
|
||||
/// No description provided for @cancel.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Cancel'**
|
||||
String get cancel;
|
||||
|
||||
/// No description provided for @delete.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Delete'**
|
||||
String get delete;
|
||||
|
||||
/// No description provided for @deleteNoteTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Delete note?'**
|
||||
String get deleteNoteTitle;
|
||||
|
||||
/// No description provided for @deleteNote.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Delete note'**
|
||||
String get deleteNote;
|
||||
|
||||
/// No description provided for @openInSplitView.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Open in Split View'**
|
||||
String get openInSplitView;
|
||||
|
||||
/// No description provided for @splitViewSubtitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'PDF reference + scratchpad'**
|
||||
String get splitViewSubtitle;
|
||||
|
||||
/// No description provided for @removeDocument.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Remove document'**
|
||||
String get removeDocument;
|
||||
|
||||
/// No description provided for @processingPptx.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Processing PPTX...'**
|
||||
String get processingPptx;
|
||||
|
||||
/// No description provided for @processingPresentation.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Processing presentation...'**
|
||||
String get processingPresentation;
|
||||
|
||||
/// No description provided for @couldNotOpenPresentation.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Could not open presentation.'**
|
||||
String get couldNotOpenPresentation;
|
||||
|
||||
/// No description provided for @toolPen.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Pen'**
|
||||
String get toolPen;
|
||||
|
||||
/// No description provided for @toolHighlighter.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Highlighter'**
|
||||
String get toolHighlighter;
|
||||
|
||||
/// No description provided for @toolEraser.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Eraser'**
|
||||
String get toolEraser;
|
||||
|
||||
/// No description provided for @actionUndo.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Undo'**
|
||||
String get actionUndo;
|
||||
|
||||
/// No description provided for @actionRedo.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Redo'**
|
||||
String get actionRedo;
|
||||
|
||||
/// No description provided for @fingerDrawingOn.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Finger drawing ON'**
|
||||
String get fingerDrawingOn;
|
||||
|
||||
/// No description provided for @fingerDrawingOff.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Finger drawing OFF (pen only)'**
|
||||
String get fingerDrawingOff;
|
||||
|
||||
/// No description provided for @pages.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Pages'**
|
||||
String get pages;
|
||||
|
||||
/// No description provided for @penSettings.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Pen settings'**
|
||||
String get penSettings;
|
||||
|
||||
/// No description provided for @inputDiagnostic.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Input diagnostic (writes a log file)'**
|
||||
String get inputDiagnostic;
|
||||
|
||||
/// No description provided for @back.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Back'**
|
||||
String get back;
|
||||
|
||||
/// No description provided for @previousPage.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Previous page'**
|
||||
String get previousPage;
|
||||
|
||||
/// No description provided for @nextPage.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Next page'**
|
||||
String get nextPage;
|
||||
|
||||
/// No description provided for @failedToOpenPdf.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Failed to open PDF:\n{error}'**
|
||||
String failedToOpenPdf(String error);
|
||||
|
||||
/// No description provided for @pdfNoPages.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'PDF has no pages.'**
|
||||
String get pdfNoPages;
|
||||
|
||||
/// No description provided for @pageOfPages.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'{current} / {total}'**
|
||||
String pageOfPages(int current, int total);
|
||||
}
|
||||
|
||||
class _AppLocalizationsDelegate
|
||||
extends LocalizationsDelegate<AppLocalizations> {
|
||||
const _AppLocalizationsDelegate();
|
||||
|
||||
@override
|
||||
Future<AppLocalizations> load(Locale locale) {
|
||||
return SynchronousFuture<AppLocalizations>(lookupAppLocalizations(locale));
|
||||
}
|
||||
|
||||
@override
|
||||
bool isSupported(Locale locale) =>
|
||||
<String>['en', 'zh'].contains(locale.languageCode);
|
||||
|
||||
@override
|
||||
bool shouldReload(_AppLocalizationsDelegate old) => false;
|
||||
}
|
||||
|
||||
AppLocalizations lookupAppLocalizations(Locale locale) {
|
||||
// Lookup logic when only language code is specified.
|
||||
switch (locale.languageCode) {
|
||||
case 'en':
|
||||
return AppLocalizationsEn();
|
||||
case 'zh':
|
||||
return AppLocalizationsZh();
|
||||
}
|
||||
|
||||
throw FlutterError(
|
||||
'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely '
|
||||
'an issue with the localizations generation tool. Please file an issue '
|
||||
'on GitHub with a reproducible sample app and the gen-l10n configuration '
|
||||
'that was used.',
|
||||
);
|
||||
}
|
||||
116
lib/l10n/app_localizations_en.dart
Normal file
116
lib/l10n/app_localizations_en.dart
Normal file
@@ -0,0 +1,116 @@
|
||||
// ignore: unused_import
|
||||
import 'package:intl/intl.dart' as intl;
|
||||
import 'app_localizations.dart';
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
|
||||
/// The translations for English (`en`).
|
||||
class AppLocalizationsEn extends AppLocalizations {
|
||||
AppLocalizationsEn([String locale = 'en']) : super(locale);
|
||||
|
||||
@override
|
||||
String get appTitle => 'BadNote';
|
||||
|
||||
@override
|
||||
String get settings => 'Settings';
|
||||
|
||||
@override
|
||||
String get search => 'Search';
|
||||
|
||||
@override
|
||||
String get importPdf => 'Import PDF';
|
||||
|
||||
@override
|
||||
String get importPpt => 'Import PPT';
|
||||
|
||||
@override
|
||||
String get penCanvasBeta => 'Pen Canvas (beta)';
|
||||
|
||||
@override
|
||||
String get newNote => 'New Note';
|
||||
|
||||
@override
|
||||
String get open => 'Open';
|
||||
|
||||
@override
|
||||
String get cancel => 'Cancel';
|
||||
|
||||
@override
|
||||
String get delete => 'Delete';
|
||||
|
||||
@override
|
||||
String get deleteNoteTitle => 'Delete note?';
|
||||
|
||||
@override
|
||||
String get deleteNote => 'Delete note';
|
||||
|
||||
@override
|
||||
String get openInSplitView => 'Open in Split View';
|
||||
|
||||
@override
|
||||
String get splitViewSubtitle => 'PDF reference + scratchpad';
|
||||
|
||||
@override
|
||||
String get removeDocument => 'Remove document';
|
||||
|
||||
@override
|
||||
String get processingPptx => 'Processing PPTX...';
|
||||
|
||||
@override
|
||||
String get processingPresentation => 'Processing presentation...';
|
||||
|
||||
@override
|
||||
String get couldNotOpenPresentation => 'Could not open presentation.';
|
||||
|
||||
@override
|
||||
String get toolPen => 'Pen';
|
||||
|
||||
@override
|
||||
String get toolHighlighter => 'Highlighter';
|
||||
|
||||
@override
|
||||
String get toolEraser => 'Eraser';
|
||||
|
||||
@override
|
||||
String get actionUndo => 'Undo';
|
||||
|
||||
@override
|
||||
String get actionRedo => 'Redo';
|
||||
|
||||
@override
|
||||
String get fingerDrawingOn => 'Finger drawing ON';
|
||||
|
||||
@override
|
||||
String get fingerDrawingOff => 'Finger drawing OFF (pen only)';
|
||||
|
||||
@override
|
||||
String get pages => 'Pages';
|
||||
|
||||
@override
|
||||
String get penSettings => 'Pen settings';
|
||||
|
||||
@override
|
||||
String get inputDiagnostic => 'Input diagnostic (writes a log file)';
|
||||
|
||||
@override
|
||||
String get back => 'Back';
|
||||
|
||||
@override
|
||||
String get previousPage => 'Previous page';
|
||||
|
||||
@override
|
||||
String get nextPage => 'Next page';
|
||||
|
||||
@override
|
||||
String failedToOpenPdf(String error) {
|
||||
return 'Failed to open PDF:\n$error';
|
||||
}
|
||||
|
||||
@override
|
||||
String get pdfNoPages => 'PDF has no pages.';
|
||||
|
||||
@override
|
||||
String pageOfPages(int current, int total) {
|
||||
return '$current / $total';
|
||||
}
|
||||
}
|
||||
116
lib/l10n/app_localizations_zh.dart
Normal file
116
lib/l10n/app_localizations_zh.dart
Normal file
@@ -0,0 +1,116 @@
|
||||
// ignore: unused_import
|
||||
import 'package:intl/intl.dart' as intl;
|
||||
import 'app_localizations.dart';
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
|
||||
/// The translations for Chinese (`zh`).
|
||||
class AppLocalizationsZh extends AppLocalizations {
|
||||
AppLocalizationsZh([String locale = 'zh']) : super(locale);
|
||||
|
||||
@override
|
||||
String get appTitle => 'BadNote';
|
||||
|
||||
@override
|
||||
String get settings => '设置';
|
||||
|
||||
@override
|
||||
String get search => '搜索';
|
||||
|
||||
@override
|
||||
String get importPdf => '导入 PDF';
|
||||
|
||||
@override
|
||||
String get importPpt => '导入 PPT';
|
||||
|
||||
@override
|
||||
String get penCanvasBeta => '手写画布(测试版)';
|
||||
|
||||
@override
|
||||
String get newNote => '新建笔记';
|
||||
|
||||
@override
|
||||
String get open => '打开';
|
||||
|
||||
@override
|
||||
String get cancel => '取消';
|
||||
|
||||
@override
|
||||
String get delete => '删除';
|
||||
|
||||
@override
|
||||
String get deleteNoteTitle => '删除笔记?';
|
||||
|
||||
@override
|
||||
String get deleteNote => '删除笔记';
|
||||
|
||||
@override
|
||||
String get openInSplitView => '分屏打开';
|
||||
|
||||
@override
|
||||
String get splitViewSubtitle => 'PDF 参考 + 草稿纸';
|
||||
|
||||
@override
|
||||
String get removeDocument => '移除文档';
|
||||
|
||||
@override
|
||||
String get processingPptx => '正在处理 PPTX…';
|
||||
|
||||
@override
|
||||
String get processingPresentation => '正在处理演示文稿…';
|
||||
|
||||
@override
|
||||
String get couldNotOpenPresentation => '无法打开演示文稿。';
|
||||
|
||||
@override
|
||||
String get toolPen => '钢笔';
|
||||
|
||||
@override
|
||||
String get toolHighlighter => '荧光笔';
|
||||
|
||||
@override
|
||||
String get toolEraser => '橡皮擦';
|
||||
|
||||
@override
|
||||
String get actionUndo => '撤销';
|
||||
|
||||
@override
|
||||
String get actionRedo => '重做';
|
||||
|
||||
@override
|
||||
String get fingerDrawingOn => '手指书写:开';
|
||||
|
||||
@override
|
||||
String get fingerDrawingOff => '手指书写:关(仅手写笔)';
|
||||
|
||||
@override
|
||||
String get pages => '页面';
|
||||
|
||||
@override
|
||||
String get penSettings => '手写笔设置';
|
||||
|
||||
@override
|
||||
String get inputDiagnostic => '输入诊断(写入日志文件)';
|
||||
|
||||
@override
|
||||
String get back => '返回';
|
||||
|
||||
@override
|
||||
String get previousPage => '上一页';
|
||||
|
||||
@override
|
||||
String get nextPage => '下一页';
|
||||
|
||||
@override
|
||||
String failedToOpenPdf(String error) {
|
||||
return '打开 PDF 失败:\n$error';
|
||||
}
|
||||
|
||||
@override
|
||||
String get pdfNoPages => 'PDF 没有任何页面。';
|
||||
|
||||
@override
|
||||
String pageOfPages(int current, int total) {
|
||||
return '$current / $total';
|
||||
}
|
||||
}
|
||||
37
lib/l10n/app_zh.arb
Normal file
37
lib/l10n/app_zh.arb
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"@@locale": "zh",
|
||||
"appTitle": "BadNote",
|
||||
"settings": "设置",
|
||||
"search": "搜索",
|
||||
"importPdf": "导入 PDF",
|
||||
"importPpt": "导入 PPT",
|
||||
"penCanvasBeta": "手写画布(测试版)",
|
||||
"newNote": "新建笔记",
|
||||
"open": "打开",
|
||||
"cancel": "取消",
|
||||
"delete": "删除",
|
||||
"deleteNoteTitle": "删除笔记?",
|
||||
"deleteNote": "删除笔记",
|
||||
"openInSplitView": "分屏打开",
|
||||
"splitViewSubtitle": "PDF 参考 + 草稿纸",
|
||||
"removeDocument": "移除文档",
|
||||
"processingPptx": "正在处理 PPTX…",
|
||||
"processingPresentation": "正在处理演示文稿…",
|
||||
"couldNotOpenPresentation": "无法打开演示文稿。",
|
||||
"toolPen": "钢笔",
|
||||
"toolHighlighter": "荧光笔",
|
||||
"toolEraser": "橡皮擦",
|
||||
"actionUndo": "撤销",
|
||||
"actionRedo": "重做",
|
||||
"fingerDrawingOn": "手指书写:开",
|
||||
"fingerDrawingOff": "手指书写:关(仅手写笔)",
|
||||
"pages": "页面",
|
||||
"penSettings": "手写笔设置",
|
||||
"inputDiagnostic": "输入诊断(写入日志文件)",
|
||||
"back": "返回",
|
||||
"previousPage": "上一页",
|
||||
"nextPage": "下一页",
|
||||
"failedToOpenPdf": "打开 PDF 失败:\n{error}",
|
||||
"pdfNoPages": "PDF 没有任何页面。",
|
||||
"pageOfPages": "{current} / {total}"
|
||||
}
|
||||
Reference in New Issue
Block a user