@khorsheed/dsh-reader 0.2.0
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.en.md +90 -0
- package/README.i18n.yaml +6 -0
- package/README.md +90 -0
- package/cordis.patch.yml +13 -0
- package/lib/client.js +15119 -0
- package/lib/index.js +3660 -0
- package/lib/invariant.js +38 -0
- package/lib/typert.host.d.ts +3 -0
- package/lib/typert.host.js +1738 -0
- package/lib/typert.remote-client.d.ts +82 -0
- package/lib/typert.remote-client.js +1115 -0
- package/lib/types/arxiv.d.ts +47 -0
- package/lib/types/arxiv.js +76 -0
- package/lib/types/client/ReaderPane.d.ts +71 -0
- package/lib/types/client/ReaderPane.js +3135 -0
- package/lib/types/client/contract.d.ts +254 -0
- package/lib/types/client/contract.js +1 -0
- package/lib/types/client/definition.d.ts +27 -0
- package/lib/types/client/definition.js +32 -0
- package/lib/types/client/extract-article.d.ts +102 -0
- package/lib/types/client/extract-article.js +1024 -0
- package/lib/types/client/icons.d.ts +27 -0
- package/lib/types/client/icons.js +11 -0
- package/lib/types/client/index.d.ts +34 -0
- package/lib/types/client/index.js +222 -0
- package/lib/types/client/locales.d.ts +391 -0
- package/lib/types/client/locales.js +382 -0
- package/lib/types/client/parse-rss.d.ts +69 -0
- package/lib/types/client/parse-rss.js +656 -0
- package/lib/types/client/quote.d.ts +89 -0
- package/lib/types/client/quote.js +119 -0
- package/lib/types/client/selectors.d.ts +167 -0
- package/lib/types/client/selectors.js +413 -0
- package/lib/types/client/session.d.ts +171 -0
- package/lib/types/client/session.js +223 -0
- package/lib/types/client/store.d.ts +181 -0
- package/lib/types/client/store.js +226 -0
- package/lib/types/client/translate.d.ts +379 -0
- package/lib/types/client/translate.js +873 -0
- package/lib/types/index.d.ts +48 -0
- package/lib/types/index.js +25 -0
- package/lib/types/invariant.d.ts +29 -0
- package/lib/types/invariant.js +38 -0
- package/lib/types/link-resolvers.d.ts +107 -0
- package/lib/types/link-resolvers.js +299 -0
- package/lib/types/remote.d.ts +224 -0
- package/lib/types/remote.js +275 -0
- package/lib/types/schedule.d.ts +78 -0
- package/lib/types/schedule.js +114 -0
- package/lib/types/service.d.ts +633 -0
- package/lib/types/service.js +1697 -0
- package/lib/types/store.d.ts +224 -0
- package/lib/types/store.js +916 -0
- package/lib/types/types.d.ts +710 -0
- package/lib/types/types.js +182 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +128 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The inspiration space's dictionaries. Keys are referenced as translation ids
|
|
3
|
+
* from the pane and the tab definition, so adding a key here is what makes it
|
|
4
|
+
* usable.
|
|
5
|
+
*
|
|
6
|
+
* The namespace stays `reader`: the package name, the tab kind and the Remote
|
|
7
|
+
* namespace are stable identifiers, and renaming them would change a deployed
|
|
8
|
+
* loader row and the on-disk state root for a copy change. What the reader
|
|
9
|
+
* SEES is 「灵感空间」, which is what the surface became once saved links
|
|
10
|
+
* joined subscriptions — a wall of things you chose to keep.
|
|
11
|
+
*
|
|
12
|
+
* @module @khorsheed/dsh-reader/client/locales
|
|
13
|
+
*/
|
|
14
|
+
export const NS = 'reader';
|
|
15
|
+
export const en = {
|
|
16
|
+
'tab.label': 'Inspiration',
|
|
17
|
+
'tab.subtitle': 'Subscriptions and saved links',
|
|
18
|
+
'guide.description': 'RSS/Atom subscriptions plus any article link you save',
|
|
19
|
+
'filter.today': 'Today',
|
|
20
|
+
'filter.all': 'All',
|
|
21
|
+
'filter.unreadOnly': 'Unread only',
|
|
22
|
+
'filter.unreadOn': 'Showing unread only',
|
|
23
|
+
'filter.readState': 'Read state',
|
|
24
|
+
'filter.bySource': 'By source',
|
|
25
|
+
'action.filter': 'Filter',
|
|
26
|
+
'action.fetchBody': 'Fetch the text',
|
|
27
|
+
'action.more': 'More actions',
|
|
28
|
+
'action.addTag': 'Tags…',
|
|
29
|
+
'action.translate': 'Translate',
|
|
30
|
+
'translate.view': 'Translation view',
|
|
31
|
+
'translate.onlyTranslation': 'Translation only',
|
|
32
|
+
'translate.bilingual': 'Side by side',
|
|
33
|
+
'translate.onlyOriginal': 'Original only',
|
|
34
|
+
'translate.retry': 'Translate again',
|
|
35
|
+
'translate.tip': 'Click any sentence to see its original; click again to hide it',
|
|
36
|
+
'translate.preparing': 'Preparing the Chinese language pack',
|
|
37
|
+
'translate.working': 'Translating',
|
|
38
|
+
'translate.local': 'On-device · Chinese (Simplified)',
|
|
39
|
+
'translate.failed': 'Translation stopped: {reason}',
|
|
40
|
+
'translate.failedAll': 'No batch came back — try again from the globe menu',
|
|
41
|
+
'translate.nothing': 'Nothing in this body to translate',
|
|
42
|
+
'translate.unsupported': 'This browser cannot translate {pair} — the model for that pair is not available (see chrome://on-device-internals, "Broker State")',
|
|
43
|
+
'translate.dismiss': 'Dismiss',
|
|
44
|
+
'detail.bodyStale': 'The saved copy is out of date — fetch it again?',
|
|
45
|
+
'tag.title': 'Tags',
|
|
46
|
+
'tag.placeholder': 'Tag name, then Enter',
|
|
47
|
+
'tag.hint': 'Enter to add',
|
|
48
|
+
'tag.create': 'Create “{name}”',
|
|
49
|
+
'tag.empty': 'No tags yet — type a name to make the first one',
|
|
50
|
+
'filter.byTag': 'By tag',
|
|
51
|
+
'filter.byKind': 'By type',
|
|
52
|
+
'filter.deleteTag': 'Delete this tag',
|
|
53
|
+
'filter.hideDupes': 'Hide duplicates',
|
|
54
|
+
'dedupe.badge': '{count} duplicate sources',
|
|
55
|
+
'dedupe.title': 'The same article also came from: {sources}',
|
|
56
|
+
'filter.searchSource': 'Search sources…',
|
|
57
|
+
'filter.noSourceMatch': 'No source matches',
|
|
58
|
+
'search.placeholder': 'Search titles, authors, sources…',
|
|
59
|
+
'sort.title': 'Sort',
|
|
60
|
+
'sort.newest': 'Newest first',
|
|
61
|
+
'sort.oldest': 'Oldest first',
|
|
62
|
+
'sort.source': 'Group by source',
|
|
63
|
+
'action.refresh': 'Refresh all',
|
|
64
|
+
'action.refreshOne': 'Refresh',
|
|
65
|
+
'action.add': 'Add inspiration',
|
|
66
|
+
'action.back': 'Back',
|
|
67
|
+
'action.copyLink': 'Copy link',
|
|
68
|
+
'action.openExternal': 'Open in browser',
|
|
69
|
+
'action.quote': 'Quote',
|
|
70
|
+
'action.manage': 'Manage subscriptions',
|
|
71
|
+
'action.recent': 'Recently read',
|
|
72
|
+
'recent.title': 'Recently read',
|
|
73
|
+
'recent.help': 'What you opened, newest first. Click one to read it again.',
|
|
74
|
+
'recent.empty': 'Nothing read yet — open an article and it lands here.',
|
|
75
|
+
'recent.clearTitle': 'Clear this list',
|
|
76
|
+
'recent.sourceGone': 'Its source is gone, so this one can only be opened at its original address',
|
|
77
|
+
'action.remove': 'Remove',
|
|
78
|
+
'action.pause': 'Pause',
|
|
79
|
+
'action.resume': 'Resume',
|
|
80
|
+
'action.submit': 'Fetch',
|
|
81
|
+
'action.done': 'Done',
|
|
82
|
+
'action.cancel': 'Cancel',
|
|
83
|
+
'action.clearSearch': 'Clear',
|
|
84
|
+
'add.title': 'Add inspiration',
|
|
85
|
+
'add.help': 'Paste a feed address or any article link. What comes back decides how it is stored: a feed is subscribed and refreshed on schedule, a web page is kept as a single item.',
|
|
86
|
+
'add.placeholder': 'https://',
|
|
87
|
+
'verdict.subscribed': 'Subscribed to {label} — it will refresh on schedule.',
|
|
88
|
+
'verdict.savedLink': 'Saved this article — one item, no subscription.',
|
|
89
|
+
'verdict.savedLinkNoPreview': 'Saved the link to {label} — no preview here: {reason}',
|
|
90
|
+
'verdict.duplicate': 'That source is already in the list.',
|
|
91
|
+
'verdict.invalidUrl': 'That is not an http(s) address.',
|
|
92
|
+
'verdict.unsupportedContent': 'That address did not return a feed or an article.',
|
|
93
|
+
'verdict.fetchFailed': 'Could not fetch that address.',
|
|
94
|
+
'preview.blocked': 'this site refuses automatic fetches (it answers with a bot challenge)',
|
|
95
|
+
'preview.login': 'this address is behind a login (an institutional proxy or single sign-on)',
|
|
96
|
+
'preview.unsupportedType': 'this address is not a web page (a PDF or another file)',
|
|
97
|
+
'preview.redirected': 'this address moves to another site, and the redirect cannot be followed here',
|
|
98
|
+
'preview.empty': 'this page has no readable text',
|
|
99
|
+
'preview.unreachable': 'the site could not be reached just now',
|
|
100
|
+
'preview.http': 'the site answered with an HTTP error',
|
|
101
|
+
'preview.unreadable': 'this page is a script-rendered app behind an anti-bot wall, so there is nothing to read server-side — if it has an arXiv version, paste that link instead',
|
|
102
|
+
'detail.linkOnlyBadge': 'Link only',
|
|
103
|
+
'detail.fetchingBody': 'Fetching the full text…',
|
|
104
|
+
'detail.refetch': 'Fetch again',
|
|
105
|
+
'detail.refetching': 'Fetching…',
|
|
106
|
+
'detail.refetchTitle': 'Fetch this article again — what is on screen is the copy fetched earlier',
|
|
107
|
+
'detail.removeLink': 'Delete this link',
|
|
108
|
+
'state.loading': 'Loading…',
|
|
109
|
+
'state.emptyTitle': 'Nothing here yet',
|
|
110
|
+
'state.emptyBody': 'Collect your first inspiration: subscribe to a source, or paste a link you want to keep.',
|
|
111
|
+
'state.noMatch': 'Nothing matches “{query}”.',
|
|
112
|
+
'state.fetching': 'Fetching…',
|
|
113
|
+
'state.incomplete': 'Incomplete',
|
|
114
|
+
'state.error': 'Failed',
|
|
115
|
+
'state.stale': 'Refresh failed: {message}',
|
|
116
|
+
'state.emptyWall': 'No entries yet. Add a source, or press refresh.',
|
|
117
|
+
'state.incompleteReason': 'the payload hit the host’s 100,000-character fetch cap, so only the part that arrived is shown',
|
|
118
|
+
'state.matches': '{count} matching “{query}”',
|
|
119
|
+
'sources.title': 'Subscriptions',
|
|
120
|
+
'sources.count': '{count} sources',
|
|
121
|
+
'sources.empty': 'No subscriptions yet',
|
|
122
|
+
'sources.help': 'A subscription is fetched on schedule and its new entries land in the wall below. A saved link is one item and never refetches on its own.',
|
|
123
|
+
'sources.enabled': 'Updating',
|
|
124
|
+
'sources.disabled': 'Paused',
|
|
125
|
+
'sources.time': 'Daily refresh',
|
|
126
|
+
'sources.timeHelp': 'Local time, 24-hour. A missed window is caught up once at the next boot.',
|
|
127
|
+
'sources.failed': 'Last fetch failed',
|
|
128
|
+
'sources.items': '{count} items',
|
|
129
|
+
'sources.never': 'Not fetched yet',
|
|
130
|
+
'sources.cardHint': 'Subscription',
|
|
131
|
+
'sources.name': 'Name',
|
|
132
|
+
'sources.cache': 'Keep fetched articles',
|
|
133
|
+
'sources.cacheHelp': 'A fetched article is served from the cache until this deadline; after that, opening the entry fetches it once more. 0 means keep it until the storage budget evicts it.',
|
|
134
|
+
'sources.cacheHours': '{count} hours',
|
|
135
|
+
'sources.cacheForever': 'Until evicted',
|
|
136
|
+
'sources.storageBodies': 'Article cache',
|
|
137
|
+
'sources.storageBodiesUsage': '{entries} cached · {size}',
|
|
138
|
+
'sources.storageBodiesHelp': 'What the fetched full texts currently occupy. Their eviction follows the retention setting above and the entries cap.',
|
|
139
|
+
'sources.storageTranslations': 'Translation cache',
|
|
140
|
+
'sources.storageTranslationsUsage': '{maps} maps + {sentences} sentences · {size}',
|
|
141
|
+
'sources.storageTranslationsHelp': 'What the translations currently occupy: a global sentence memory plus one exact-fit map per entry. No expiry — only the budget below evicts, least-recently-used first.',
|
|
142
|
+
'sources.translationBudget': 'Budget',
|
|
143
|
+
'sources.translationBudgetHelp': 'How large the translation cache may grow (MB of text). Translations never expire by the clock — rebuilding one costs a gesture plus per-sentence model work — so this budget, least-recently-used, is the only eviction.',
|
|
144
|
+
'sources.clearTranslations': 'Clear translation cache',
|
|
145
|
+
'sources.clearTranslationsConfirm': 'Click again to clear both tiers',
|
|
146
|
+
'sources.blocked': 'This publisher refuses automatic fetches (its page answers with a bot challenge), so only the feed’s own text can be shown.',
|
|
147
|
+
'sources.unreachable': 'The publisher’s page could not be reached at all (the request failed before any content arrived), so only the feed’s own text can be shown.',
|
|
148
|
+
'sources.httpError': 'The publisher answered with an error, so only the feed’s own text can be shown.',
|
|
149
|
+
'sources.kindRss': 'Feed',
|
|
150
|
+
'sources.kindLink': 'Saved link',
|
|
151
|
+
'sources.kindFilter': 'Filter by type',
|
|
152
|
+
'sources.noMatch': 'Nothing of that type.',
|
|
153
|
+
'sources.sortAdded': 'Added',
|
|
154
|
+
'sources.sortName': 'Name',
|
|
155
|
+
'sources.sortFetched': 'Fetched',
|
|
156
|
+
'detail.fetchFailed': 'Could not fetch the full text — {reason}',
|
|
157
|
+
'sources.url': 'Feed address',
|
|
158
|
+
'detail.incomplete': 'Limited length, content shown in part',
|
|
159
|
+
'detail.readOriginal': 'Read the original',
|
|
160
|
+
'detail.extractFailed': 'Could not extract a body from this page — open the original instead.',
|
|
161
|
+
'detail.summaryOnly': 'This feed publishes only a summary for this entry — the full text lives on the original page.',
|
|
162
|
+
'detail.resolvedFrom': 'Resolved from the link you pasted ({url}) — click to open it',
|
|
163
|
+
'detail.alsoFrom': 'Also from this source',
|
|
164
|
+
'detail.composerLabel': 'Current conversation draft',
|
|
165
|
+
'detail.composerEmpty': '(empty)',
|
|
166
|
+
'foot.refreshedAt': 'Refreshed {when}',
|
|
167
|
+
'foot.scheduled': 'daily {time}',
|
|
168
|
+
'foot.never': 'Not refreshed yet',
|
|
169
|
+
'foot.unread': 'unread',
|
|
170
|
+
'foot.refreshing': 'Refreshing…',
|
|
171
|
+
'foot.deduped': '{count} duplicates hidden',
|
|
172
|
+
'state.backfilling': 'Filling in {done}/{total} full articles…',
|
|
173
|
+
'detail.filledIn': 'The full text was fetched automatically and cached. The card still shows the feed’s own summary — open it to read.',
|
|
174
|
+
'detail.filledInBadge': 'Full text',
|
|
175
|
+
'detail.scriptFigures': 'This page draws {count} of its figures with its own scripts, so their pictures cannot be fetched — the captions are kept below —',
|
|
176
|
+
'fetch.none': 'Fetch',
|
|
177
|
+
'fetch.fetching': 'Fetching',
|
|
178
|
+
'fetch.raw': 'Storing',
|
|
179
|
+
'fetch.ready': 'Fetched',
|
|
180
|
+
'fetch.failed': 'Fetch failed',
|
|
181
|
+
'fetch.noneTitle': 'Fetch the article now — it keeps going when you leave this page',
|
|
182
|
+
'fetch.readyTitle': 'The full text is cached — open it to read',
|
|
183
|
+
'fetch.failedTitle': 'Fetch failed: {reason} — click for details and retry',
|
|
184
|
+
'fetch.failedOpenTitle': 'Fetch failed: {reason} — click for details',
|
|
185
|
+
'fetch.failedFinalTitle': 'Fetch failed: {reason}',
|
|
186
|
+
'detail.renderFetch': 'Fetch rendered',
|
|
187
|
+
'detail.rendering': 'Rendering this page in the browser — script-drawn figures appear when it lands (the first render is slower)…',
|
|
188
|
+
'quote.copied': 'Copied',
|
|
189
|
+
'quote.copyFailed': 'Could not reach the clipboard',
|
|
190
|
+
'quote.quoted': 'Quoted {count} characters',
|
|
191
|
+
'quote.toSideChat': 'Sent to side chat',
|
|
192
|
+
'quote.sideChatUnavailable': 'No side chat in this composition',
|
|
193
|
+
'when.justNow': 'just now',
|
|
194
|
+
'when.minutes': '{count} min ago',
|
|
195
|
+
'when.hours': '{count} h ago',
|
|
196
|
+
'when.yesterday': 'yesterday',
|
|
197
|
+
'when.days': '{count} d ago',
|
|
198
|
+
};
|
|
199
|
+
export const zh = {
|
|
200
|
+
'tab.label': '灵感空间',
|
|
201
|
+
'tab.subtitle': '订阅源与保存的链接',
|
|
202
|
+
'guide.description': 'RSS/Atom 订阅,加上你保存的任意文章链接',
|
|
203
|
+
'filter.today': '今日',
|
|
204
|
+
'filter.all': '全部',
|
|
205
|
+
'filter.unreadOnly': '只看未读',
|
|
206
|
+
'filter.unreadOn': '只看未读',
|
|
207
|
+
'filter.readState': '阅读状态',
|
|
208
|
+
'filter.bySource': '按来源',
|
|
209
|
+
'action.filter': '筛选',
|
|
210
|
+
'action.fetchBody': '抓取正文',
|
|
211
|
+
'action.more': '更多操作',
|
|
212
|
+
'action.addTag': '打标签…',
|
|
213
|
+
'action.translate': '翻译',
|
|
214
|
+
'translate.view': '翻译视图',
|
|
215
|
+
'translate.onlyTranslation': '只看译文',
|
|
216
|
+
'translate.bilingual': '双语对照',
|
|
217
|
+
'translate.onlyOriginal': '只看原文',
|
|
218
|
+
'translate.retry': '重新翻译',
|
|
219
|
+
'translate.tip': '点任意一句可看原文,再点收起',
|
|
220
|
+
'translate.preparing': '正在准备中文语言包',
|
|
221
|
+
'translate.working': '翻译中',
|
|
222
|
+
'translate.local': '浏览器本地 · 中文(简体)',
|
|
223
|
+
'translate.failed': '翻译中断:{reason}',
|
|
224
|
+
'translate.failedAll': '各批次都没翻出来 —— 可从地球菜单里重试',
|
|
225
|
+
'translate.nothing': '这段正文里没有可翻译的散文',
|
|
226
|
+
'translate.unsupported': '这个浏览器无法翻译 {pair} —— 该语言对的模型不可用(可在 chrome://on-device-internals 的 Broker State 看原因)',
|
|
227
|
+
'translate.dismiss': '不再提示',
|
|
228
|
+
'detail.bodyStale': '存的正文已过期 —— 重新抓取?',
|
|
229
|
+
'tag.title': '标签',
|
|
230
|
+
'tag.placeholder': '标签名,回车确认',
|
|
231
|
+
'tag.hint': '回车确认',
|
|
232
|
+
'tag.create': '新建「{name}」',
|
|
233
|
+
'tag.empty': '还没有标签 —— 输入名字回车就能建第一个',
|
|
234
|
+
'filter.byTag': '按标签',
|
|
235
|
+
'filter.byKind': '按类型',
|
|
236
|
+
'filter.deleteTag': '删除这个标签',
|
|
237
|
+
'filter.hideDupes': '隐藏重复',
|
|
238
|
+
'dedupe.badge': '{count} 个重复来源',
|
|
239
|
+
'dedupe.title': '同一篇还来自:{sources}',
|
|
240
|
+
'filter.searchSource': '搜索来源…',
|
|
241
|
+
'filter.noSourceMatch': '没有匹配的来源',
|
|
242
|
+
'search.placeholder': '搜索标题、作者、来源…',
|
|
243
|
+
'sort.title': '排序',
|
|
244
|
+
'sort.newest': '最新在前',
|
|
245
|
+
'sort.oldest': '最早在前',
|
|
246
|
+
'sort.source': '按来源分组',
|
|
247
|
+
'action.refresh': '刷新全部',
|
|
248
|
+
'action.refreshOne': '刷新',
|
|
249
|
+
'action.add': '新增灵感',
|
|
250
|
+
'action.back': '返回',
|
|
251
|
+
'action.copyLink': '复制链接',
|
|
252
|
+
'action.openExternal': '在浏览器打开原文',
|
|
253
|
+
'action.quote': '引用',
|
|
254
|
+
'action.manage': '订阅管理',
|
|
255
|
+
'action.recent': '最近阅读',
|
|
256
|
+
'recent.title': '最近阅读',
|
|
257
|
+
'recent.help': '按打开时间倒序。点一条就能接着读。',
|
|
258
|
+
'recent.empty': '还没有读过任何一篇 —— 打开一篇文章,它就会出现在这里。',
|
|
259
|
+
'recent.clearTitle': '清空这份列表',
|
|
260
|
+
'recent.sourceGone': '它所属的源已经删掉了,这一条只能去原文地址打开',
|
|
261
|
+
'action.remove': '删除',
|
|
262
|
+
'action.pause': '暂停',
|
|
263
|
+
'action.resume': '恢复',
|
|
264
|
+
'action.submit': '抓取',
|
|
265
|
+
'action.done': '完成',
|
|
266
|
+
'action.cancel': '取消',
|
|
267
|
+
'action.clearSearch': '清空',
|
|
268
|
+
'add.title': '新增灵感',
|
|
269
|
+
'add.help': '粘贴订阅源地址或任意文章链接。抓回来是什么,就按什么处理:是 feed 就订阅、按计划刷新,是网页就只存这一篇。',
|
|
270
|
+
'add.placeholder': 'https://',
|
|
271
|
+
'verdict.subscribed': '已订阅《{label}》—— 会按计划自动刷新。',
|
|
272
|
+
'verdict.savedLink': '已保存这篇 —— 只存这一条,不建订阅源。',
|
|
273
|
+
'verdict.savedLinkNoPreview': '已保存《{label}》的链接 —— 这里看不到内容:{reason}',
|
|
274
|
+
'verdict.duplicate': '这个源已经在列表里了。',
|
|
275
|
+
'verdict.invalidUrl': '这不是 http(s) 地址。',
|
|
276
|
+
'verdict.unsupportedContent': '这个地址返回的既不是订阅源也不是网页。',
|
|
277
|
+
'verdict.fetchFailed': '抓取失败。',
|
|
278
|
+
'preview.blocked': '这个站点拒绝自动抓取(返回的是验证页)',
|
|
279
|
+
'preview.login': '这个地址需要登录(机构代理或单点登录),本插件取不到正文',
|
|
280
|
+
'preview.unsupportedType': '这个地址不是网页(是 PDF 或其它文件)',
|
|
281
|
+
'preview.redirected': '这个地址会跳到另一个站点,跨站跳转在本地无法跟随',
|
|
282
|
+
'preview.empty': '这一页没有可读的正文',
|
|
283
|
+
'preview.unreachable': '刚才连不上这个站点',
|
|
284
|
+
'preview.http': '这个站点返回了 HTTP 错误',
|
|
285
|
+
'preview.unreadable': '这个页面是 JS 应用且有反爬,服务器端读不到内容 —— 如果它有 arxiv 版,直接粘贴 arxiv 链接',
|
|
286
|
+
'detail.linkOnlyBadge': '仅链接',
|
|
287
|
+
'detail.fetchingBody': '正在抓取正文…',
|
|
288
|
+
'detail.refetch': '重新抓取',
|
|
289
|
+
'detail.refetching': '抓取中',
|
|
290
|
+
'detail.refetchTitle': '重新抓取这篇正文 —— 现在看到的是之前抓下来的版本',
|
|
291
|
+
'detail.removeLink': '删除这条链接',
|
|
292
|
+
'state.loading': '加载中…',
|
|
293
|
+
'state.emptyTitle': '还没有内容',
|
|
294
|
+
'state.emptyBody': '新增你的第一个灵感:订阅一个源,或粘贴一篇你想留住的文章链接。',
|
|
295
|
+
'state.noMatch': '没有匹配「{query}」的条目。',
|
|
296
|
+
'state.fetching': '抓取中…',
|
|
297
|
+
'state.incomplete': '未完整',
|
|
298
|
+
'state.error': '失败',
|
|
299
|
+
'state.stale': '刷新失败:{message}',
|
|
300
|
+
'state.emptyWall': '还没有条目。新增一个订阅源,或按一下刷新。',
|
|
301
|
+
'state.incompleteReason': '内容超过宿主 100,000 字符的抓取上限,只展示已经拿到的那部分',
|
|
302
|
+
'state.matches': '匹配「{query}」{count} 条',
|
|
303
|
+
'sources.title': '订阅管理',
|
|
304
|
+
'sources.count': '{count} 个源',
|
|
305
|
+
'sources.empty': '还没有订阅源',
|
|
306
|
+
'sources.help': '订阅源会按计划抓取,新条目落进下面的灵感墙;保存的链接只有这一条,不会自己重抓。',
|
|
307
|
+
'sources.enabled': '更新中',
|
|
308
|
+
'sources.disabled': '已暂停',
|
|
309
|
+
'sources.time': '每日刷新时间',
|
|
310
|
+
'sources.timeHelp': '本地时间,24 小时制。错过的窗口会在下次启动时补刷一次。',
|
|
311
|
+
'sources.failed': '上次抓取失败',
|
|
312
|
+
'sources.items': '{count} 条',
|
|
313
|
+
'sources.never': '尚未抓取',
|
|
314
|
+
'sources.cardHint': '订阅源',
|
|
315
|
+
'sources.name': '名称',
|
|
316
|
+
'sources.cache': '正文保留',
|
|
317
|
+
'sources.cacheHelp': '抓到的正文在这个期限前直接读缓存;过期后下次打开这一条会重新抓一次。0 = 一直留到存储预算淘汰它。',
|
|
318
|
+
'sources.cacheHours': '{count} 小时',
|
|
319
|
+
'sources.cacheForever': '留到被淘汰',
|
|
320
|
+
'sources.storageBodies': '原文缓存',
|
|
321
|
+
'sources.storageBodiesUsage': '{entries} 篇 · {size}',
|
|
322
|
+
'sources.storageBodiesHelp': '抓下的全文正文当前的占用。淘汰由上方的「正文保留」期限与保留条数决定。',
|
|
323
|
+
'sources.storageTranslations': '译文缓存',
|
|
324
|
+
'sources.storageTranslationsUsage': '{maps} 篇映射 + {sentences} 句 · {size}',
|
|
325
|
+
'sources.storageTranslationsHelp': '译文当前的占用:一份全局句子记忆,外加每个条目的精确映射。不按时间过期——只有下方的预算按最近使用淘汰。',
|
|
326
|
+
'sources.translationBudget': '译文预算',
|
|
327
|
+
'sources.translationBudgetHelp': '译文缓存可以涨到多大(MB 文本量)。译文不按钟点过期——重建一份要一次手势加逐句模型——所以这个预算是唯一的淘汰,按最近使用。',
|
|
328
|
+
'sources.clearTranslations': '清空译文缓存',
|
|
329
|
+
'sources.clearTranslationsConfirm': '再点一次,清空两层',
|
|
330
|
+
'sources.blocked': '这个站点拒绝自动抓取(原文地址对人以外的请求返回验证页),所以只能展示订阅源自己发布的内容。',
|
|
331
|
+
'sources.unreachable': '原文页面完全连不上(请求在拿到任何内容之前就失败了),所以只能展示订阅源自己发布的内容。',
|
|
332
|
+
'sources.httpError': '原文页面返回了错误状态,所以只能展示订阅源自己发布的内容。',
|
|
333
|
+
'sources.kindRss': '订阅源',
|
|
334
|
+
'sources.kindLink': '保存的链接',
|
|
335
|
+
'sources.kindFilter': '按类型筛选',
|
|
336
|
+
'sources.noMatch': '这一类里还没有内容。',
|
|
337
|
+
'sources.sortAdded': '按添加时间',
|
|
338
|
+
'sources.sortName': '按名称',
|
|
339
|
+
'sources.sortFetched': '按抓取时间',
|
|
340
|
+
'detail.fetchFailed': '抓取全文失败 —— {reason}',
|
|
341
|
+
'sources.url': '订阅地址',
|
|
342
|
+
'detail.incomplete': '受限篇幅,内容未完整呈现',
|
|
343
|
+
'detail.readOriginal': '阅读原文',
|
|
344
|
+
'detail.extractFailed': '这一页抽不出正文 —— 点「阅读原文」打开它。',
|
|
345
|
+
'detail.summaryOnly': '这条订阅源只发布了摘要 —— 全文在原文页面上。',
|
|
346
|
+
'detail.resolvedFrom': '这条由粘贴的链接({url})解析而来 —— 点击打开原链接',
|
|
347
|
+
'detail.alsoFrom': '同一来源',
|
|
348
|
+
'detail.composerLabel': '当前会话草稿',
|
|
349
|
+
'detail.composerEmpty': '(空)',
|
|
350
|
+
'foot.refreshedAt': '刷出于 {when}',
|
|
351
|
+
'foot.scheduled': '每日 {time}',
|
|
352
|
+
'foot.never': '尚未刷新',
|
|
353
|
+
'foot.unread': '未读',
|
|
354
|
+
'foot.refreshing': '正在刷新…',
|
|
355
|
+
'foot.deduped': '已隐藏 {count} 条重复',
|
|
356
|
+
'state.backfilling': '正在补齐全文 {done}/{total}…',
|
|
357
|
+
'detail.filledIn': '全文是自动抓取并缓存的。卡片上仍是 feed 自己的摘要 —— 点开看全文。',
|
|
358
|
+
'detail.filledInBadge': '已抓全文',
|
|
359
|
+
'detail.scriptFigures': '这一页有 {count} 张插图由页面自己的脚本绘制,抓取时拿不到画面(图注保留在正文里)——',
|
|
360
|
+
'fetch.none': '抓取',
|
|
361
|
+
'fetch.fetching': '抓取中',
|
|
362
|
+
'fetch.raw': '待解析',
|
|
363
|
+
'fetch.ready': '已抓取',
|
|
364
|
+
'fetch.failed': '抓取失败',
|
|
365
|
+
'fetch.noneTitle': '现在就把这篇抓下来 —— 离开这个页面也不会停',
|
|
366
|
+
'fetch.readyTitle': '已有全文,点开就能读',
|
|
367
|
+
'fetch.failedTitle': '抓取失败:{reason} —— 点一下看详情、可重试',
|
|
368
|
+
'fetch.failedOpenTitle': '抓取失败:{reason} —— 点一下看详情',
|
|
369
|
+
'fetch.failedFinalTitle': '抓取失败:{reason}',
|
|
370
|
+
'detail.renderFetch': '渲染抓取',
|
|
371
|
+
'detail.rendering': '正在用浏览器渲染这一页——脚本绘制的插图会在渲染后出现(首次较慢)…',
|
|
372
|
+
'quote.copied': '已复制',
|
|
373
|
+
'quote.copyFailed': '剪贴板不可用',
|
|
374
|
+
'quote.quoted': '已引用 {count} 字',
|
|
375
|
+
'quote.toSideChat': '已发送到侧边对话',
|
|
376
|
+
'quote.sideChatUnavailable': '当前组合里没有侧边对话',
|
|
377
|
+
'when.justNow': '刚刚',
|
|
378
|
+
'when.minutes': '{count} 分钟前',
|
|
379
|
+
'when.hours': '{count} 小时前',
|
|
380
|
+
'when.yesterday': '昨天',
|
|
381
|
+
'when.days': '{count} 天前',
|
|
382
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feed parsing for the browser half: RSS 2.0 / Atom → {@link ReaderEntry}.
|
|
3
|
+
*
|
|
4
|
+
* The host half never parses (its runtime has no XML parser at all), so this
|
|
5
|
+
* module is the single owner of the feed vocabulary. It runs against the raw
|
|
6
|
+
* payload the host already stored, which means re-reading a feed costs no
|
|
7
|
+
* network call.
|
|
8
|
+
*
|
|
9
|
+
* Namespaces are matched by LOCAL name, never by prefix: publishers migrated
|
|
10
|
+
* from `content:encoded` to `media:description` to a bare `content` across RSS
|
|
11
|
+
* and Atom without ever agreeing on a prefix, and matching the local name is
|
|
12
|
+
* what lets one reader handle all of them.
|
|
13
|
+
*
|
|
14
|
+
* @module @khorsheed/dsh-reader/client/parse-rss
|
|
15
|
+
*/
|
|
16
|
+
import { type ReaderEntry } from '../types.ts';
|
|
17
|
+
export type { ReaderEntry };
|
|
18
|
+
/** A parsed feed: its own metadata plus one entry per item. */
|
|
19
|
+
export interface ParsedFeed {
|
|
20
|
+
/** Feed-level title, when it declares one. */
|
|
21
|
+
readonly title?: string;
|
|
22
|
+
/** Feed-declared icon (the publisher's own asset), when present. */
|
|
23
|
+
readonly icon?: string;
|
|
24
|
+
/**
|
|
25
|
+
* True when the payload was cut off and this feed is a salvage of it: the
|
|
26
|
+
* entries below are real, but they are not all of them.
|
|
27
|
+
*/
|
|
28
|
+
readonly incomplete?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Why the payload is incomplete, in the parser's own words — the host seam's
|
|
31
|
+
* size cap, or a document that simply stops mid-tag.
|
|
32
|
+
*/
|
|
33
|
+
readonly incompleteReason?: string;
|
|
34
|
+
readonly entries: readonly ReaderEntry[];
|
|
35
|
+
}
|
|
36
|
+
/** The two ways a payload can fail to be a feed. */
|
|
37
|
+
export type ParseFeedResult = {
|
|
38
|
+
readonly ok: true;
|
|
39
|
+
readonly feed: ParsedFeed;
|
|
40
|
+
} | {
|
|
41
|
+
readonly ok: false;
|
|
42
|
+
readonly error: string;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Parse one feed payload.
|
|
46
|
+
*
|
|
47
|
+
* A malformed document, a document cut off mid-tag by a size cap, and an HTML
|
|
48
|
+
* error page all arrive here as "not a feed" and are reported as such, never
|
|
49
|
+
* thrown: the caller decides whether to surface or retry.
|
|
50
|
+
*
|
|
51
|
+
* @param raw - the raw payload the host stored for an RSS source.
|
|
52
|
+
* @param sourceId - the source these entries belong to.
|
|
53
|
+
* @returns the parsed feed, or the reason it could not be parsed.
|
|
54
|
+
*/
|
|
55
|
+
export declare function parseFeed(raw: string, sourceId: string): ParseFeedResult;
|
|
56
|
+
/**
|
|
57
|
+
* Decode the entity subset feeds actually use, in ONE left-to-right pass, then
|
|
58
|
+
* apply a second pass only when the result still looks like escaped markup.
|
|
59
|
+
*
|
|
60
|
+
* The single pass matters: `&lt;` means the literal text `<`, so a chain
|
|
61
|
+
* of blanket replaces (or repeated decoding) would turn it into `<` and then
|
|
62
|
+
* into a tag. Scanning left to right and consuming `&` first keeps `<`,
|
|
63
|
+
* which is exactly what a publisher who escaped their markup twice intended.
|
|
64
|
+
*
|
|
65
|
+
* @param input - raw element text.
|
|
66
|
+
* @returns decoded text.
|
|
67
|
+
*/
|
|
68
|
+
export declare function decodeEntities(input: string): string;
|
|
69
|
+
//# sourceMappingURL=parse-rss.d.ts.map
|