backdraw 0.0.0-stage → 2.1.1
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/LICENSE +21 -0
- package/README.md +635 -2
- package/README.tr.md +653 -0
- package/bin/backdraw.js +60 -0
- package/cli.js +105 -0
- package/doctor.js +174 -0
- package/extension/_locales/en/messages.json +3 -0
- package/extension/_locales/tr/messages.json +3 -0
- package/extension/aspect.js +25 -0
- package/extension/background.js +1015 -0
- package/extension/collection.html +45 -0
- package/extension/collection.js +62 -0
- package/extension/collections.js +409 -0
- package/extension/i18n.js +359 -0
- package/extension/icons/icon128.png +0 -0
- package/extension/icons/icon16.png +0 -0
- package/extension/icons/icon32.png +0 -0
- package/extension/icons/icon48.png +0 -0
- package/extension/icons/mascot-192.png +0 -0
- package/extension/icons/mascot-96.png +0 -0
- package/extension/inpage.js +624 -0
- package/extension/manifest.json +20 -0
- package/extension/menus.js +50 -0
- package/extension/popup.css +90 -0
- package/extension/popup.html +63 -0
- package/extension/popup.js +376 -0
- package/extension/settings.js +35 -0
- package/extension/setup.js +57 -0
- package/extension/sidepanel.css +113 -0
- package/extension/sidepanel.html +31 -0
- package/extension/sidepanel.js +433 -0
- package/extension/theme.css +22 -0
- package/host.js +664 -0
- package/identity.js +21 -0
- package/install.js +427 -0
- package/lib/args.js +30 -0
- package/lib/aspect.js +85 -0
- package/lib/browsers.js +74 -0
- package/lib/collection-store.js +288 -0
- package/lib/collections.js +192 -0
- package/lib/doctor.js +143 -0
- package/lib/extension-calls.js +59 -0
- package/lib/gallery.js +63 -0
- package/lib/limits.js +18 -0
- package/lib/mcp-clients.js +178 -0
- package/lib/naming.js +25 -0
- package/lib/png.js +24 -0
- package/lib/projects.js +56 -0
- package/lib/prompt.js +25 -0
- package/lib/protocol.js +50 -0
- package/lib/results.js +170 -0
- package/lib/term.js +22 -0
- package/lib/toml.js +248 -0
- package/mcp-server.js +437 -0
- package/package.json +52 -4
- package/platform/index.js +14 -0
- package/platform/win32.js +268 -0
- package/setup.js +213 -0
- package/shared.js +118 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
// UI texts of the extension pages, the in-page note, the right-click menu and the service worker, by
|
|
2
|
+
// key: English or Turkish, as picked in the popup (chrome.storage.local "language": 'auto' | 'en' |
|
|
3
|
+
// 'tr'; 'auto' follows the browser's language). A key without a Turkish text falls back to English,
|
|
4
|
+
// then to the key itself. _locales/ only holds the manifest's description.
|
|
5
|
+
(() => {
|
|
6
|
+
const STRINGS = {
|
|
7
|
+
en: {
|
|
8
|
+
bridge: 'Bridge',
|
|
9
|
+
project: 'Project',
|
|
10
|
+
job: 'Job',
|
|
11
|
+
connected: 'Connected',
|
|
12
|
+
notConnected: 'Not connected',
|
|
13
|
+
noHost: 'The local bridge was not found{error}. Make sure the setup script (install.js) has been run.',
|
|
14
|
+
notSelected: 'Not selected',
|
|
15
|
+
idle: 'Idle',
|
|
16
|
+
useProject: 'Use the “{name}” project',
|
|
17
|
+
selectFailed: 'Could not select it.',
|
|
18
|
+
version: 'Version',
|
|
19
|
+
footerNote: 'Images are made in a background tab; your active tab does not change.',
|
|
20
|
+
collections: 'Collections',
|
|
21
|
+
noCollections: 'No collections yet. Right-click an image on any page and choose "Add to Backdraw".',
|
|
22
|
+
imageCountOne: '1 image',
|
|
23
|
+
imageCount: '{count} images',
|
|
24
|
+
back: '‹ Back',
|
|
25
|
+
rename: 'Rename',
|
|
26
|
+
save: 'Save',
|
|
27
|
+
cancel: 'Cancel',
|
|
28
|
+
deleteCollection: 'Delete collection',
|
|
29
|
+
confirmDelete: 'Delete "{name}" and its {images} from disk?',
|
|
30
|
+
delete: 'Delete',
|
|
31
|
+
removeImage: 'Delete this image',
|
|
32
|
+
confirmRemoveImage: 'Click again to delete',
|
|
33
|
+
loading: 'Loading…',
|
|
34
|
+
noThumb: 'no preview',
|
|
35
|
+
leftover: 'Not everything could be deleted. Left in the folder: {files}',
|
|
36
|
+
newTitle: 'New collection',
|
|
37
|
+
nameLabel: 'Collection name',
|
|
38
|
+
namePlaceholder: 'e.g. Character references',
|
|
39
|
+
nameExists: 'A collection with this name exists; the image is added to it.',
|
|
40
|
+
nameInvalid: 'Use 1 to 64 characters.',
|
|
41
|
+
add: 'Add',
|
|
42
|
+
preparing: 'Preparing the image…',
|
|
43
|
+
saving: 'Saving…',
|
|
44
|
+
toastAdding: 'Adding to {name}…',
|
|
45
|
+
toastAdded: 'Added to {name} · {images}',
|
|
46
|
+
toastDuplicate: 'Already in {name} · {images}',
|
|
47
|
+
errNotConnected: 'Backdraw is not connected. Is it installed (node install.js)?',
|
|
48
|
+
errNotImage: 'This is not an image Backdraw can save.',
|
|
49
|
+
errTooLarge: 'The image is larger than {mb} MB.',
|
|
50
|
+
errDownload: 'The image could not be downloaded ({reason}).',
|
|
51
|
+
errNoSource: 'Chrome did not pass on this image\'s address (it does not for very large embedded images), so it cannot be added.',
|
|
52
|
+
errExpired: 'This window has expired; right-click the image again.',
|
|
53
|
+
errTimeout: 'Backdraw did not answer in time.',
|
|
54
|
+
menuAdd: 'Add to Backdraw',
|
|
55
|
+
menuNew: 'New collection…',
|
|
56
|
+
menuOffline: 'Backdraw is not connected',
|
|
57
|
+
settings: 'Settings',
|
|
58
|
+
language: 'Language',
|
|
59
|
+
theme: 'Theme',
|
|
60
|
+
optAuto: 'Auto',
|
|
61
|
+
langEnglish: 'English',
|
|
62
|
+
langTurkish: 'Türkçe',
|
|
63
|
+
themeLight: 'Light',
|
|
64
|
+
themeDark: 'Dark',
|
|
65
|
+
setupTitle: 'Set up Backdraw',
|
|
66
|
+
setupChecklist: 'Setup',
|
|
67
|
+
setupCount: '{done} of {total} done',
|
|
68
|
+
stepBridge: 'Local bridge connected',
|
|
69
|
+
bridgeOk: 'Chrome is talking to Backdraw on this computer.',
|
|
70
|
+
bridgeMissing: 'Chrome cannot reach the Backdraw bridge.',
|
|
71
|
+
chromeSays: 'Chrome says: {error}',
|
|
72
|
+
bridgeFix: 'Run this in the Backdraw folder, then reopen this popup:',
|
|
73
|
+
bridgeReload: 'If you just installed: chrome://extensions → Backdraw → Reload.',
|
|
74
|
+
retry: 'Try again',
|
|
75
|
+
copy: 'Copy',
|
|
76
|
+
copied: 'Copied',
|
|
77
|
+
stepChatgpt: 'Signed in to ChatGPT',
|
|
78
|
+
chatgptIn: 'You are signed in to ChatGPT in Chrome.',
|
|
79
|
+
chatgptOut: 'You are not signed in to ChatGPT in Chrome.',
|
|
80
|
+
chatgptChallenge: 'ChatGPT is showing a human verification page. Open ChatGPT and complete it.',
|
|
81
|
+
chatgptUnknown: 'Not checked yet. The check opens chatgpt.com in a background tab for a moment.',
|
|
82
|
+
chatgptChecking: 'Checking in a background tab…',
|
|
83
|
+
openChatgptSignIn: 'Open ChatGPT to sign in',
|
|
84
|
+
openChatgpt: 'Open ChatGPT',
|
|
85
|
+
checkNow: 'Check now',
|
|
86
|
+
checkAgain: 'Check again',
|
|
87
|
+
errChatgptCheck: 'ChatGPT did not finish loading; try again.',
|
|
88
|
+
stepProject: 'Project selected',
|
|
89
|
+
projectUsing: 'Images go to the “{name}” project.',
|
|
90
|
+
projectGuide: 'In ChatGPT, create a Project for your images (e.g. "Backdraw") and open it. Then click the Backdraw icon again: a "Use the … project" button appears here.',
|
|
91
|
+
projectNeedsBridge: 'Needs the bridge (step 1) first.',
|
|
92
|
+
chooseProject: 'Choose from my projects',
|
|
93
|
+
loadingProjects: 'Reading your projects in a background tab…',
|
|
94
|
+
noProjects: 'No projects were found in ChatGPT.',
|
|
95
|
+
stepReady: 'Ready',
|
|
96
|
+
readyIntro: 'Ask Claude, for example:',
|
|
97
|
+
examplePrompt: 'Generate an image with ChatGPT: a cozy reading nook, 16:9',
|
|
98
|
+
readyCollections: 'Collect references: right-click any image → "Add to Backdraw".',
|
|
99
|
+
readyWaiting: 'Finish the steps above first.',
|
|
100
|
+
gotIt: 'Done',
|
|
101
|
+
chatgpt: 'ChatGPT',
|
|
102
|
+
signedIn: 'Signed in',
|
|
103
|
+
signedOut: 'Not signed in',
|
|
104
|
+
notChecked: 'Not checked',
|
|
105
|
+
verification: 'Verification needed',
|
|
106
|
+
openGallery: 'Open gallery',
|
|
107
|
+
jobPercent: '{step} ({pct}%)',
|
|
108
|
+
jobStarting: 'Starting',
|
|
109
|
+
jobOpening: 'Opening ChatGPT',
|
|
110
|
+
jobAttaching: 'Attaching the reference images',
|
|
111
|
+
jobTyping: 'Writing the prompt',
|
|
112
|
+
jobSending: 'Sending the prompt',
|
|
113
|
+
jobGenerating: 'ChatGPT is generating the image',
|
|
114
|
+
jobReattaching: 'Attaching the image again',
|
|
115
|
+
jobVerifying: 'Checking the final version',
|
|
116
|
+
jobDownloading: 'Downloading the image',
|
|
117
|
+
jobNeedsHuman: 'Waiting for you in the ChatGPT tab (sign-in or verification)',
|
|
118
|
+
jobResuming: 'Continuing',
|
|
119
|
+
galleryTitle: 'Gallery',
|
|
120
|
+
refresh: 'Refresh',
|
|
121
|
+
loadMore: 'Load more',
|
|
122
|
+
galleryEmpty: 'No images yet. Images Claude makes with Backdraw show up here.',
|
|
123
|
+
galleryFetched: 'Fetched from a ChatGPT chat',
|
|
124
|
+
elapsed: '{time} elapsed',
|
|
125
|
+
openChat: 'Open chat',
|
|
126
|
+
noChatLink: 'No chat link',
|
|
127
|
+
showInFolder: 'Show in folder',
|
|
128
|
+
copyPath: 'Copy path',
|
|
129
|
+
pathCopied: 'Path copied',
|
|
130
|
+
copyFailed: 'Could not copy the path.',
|
|
131
|
+
addToCollection: 'Add to collection',
|
|
132
|
+
newCollectionItem: 'New collection…',
|
|
133
|
+
fileMissing: 'The file was moved or deleted.',
|
|
134
|
+
stepQueued: 'Waiting for its turn',
|
|
135
|
+
stepStarting: 'Starting',
|
|
136
|
+
stepOpening: 'Opening ChatGPT',
|
|
137
|
+
stepAttaching: 'Attaching reference images',
|
|
138
|
+
stepTyping: 'Writing the prompt',
|
|
139
|
+
stepSending: 'Sending the prompt',
|
|
140
|
+
stepGenerating: 'ChatGPT is generating',
|
|
141
|
+
stepVerifying: 'Checking the final image',
|
|
142
|
+
stepDownloading: 'Downloading the image',
|
|
143
|
+
stepReattaching: 'Attaching the image again',
|
|
144
|
+
stepNeedsHuman: 'Waiting for you: open the ChatGPT tab in Chrome and finish the login or verification',
|
|
145
|
+
stepResuming: 'Continuing',
|
|
146
|
+
menuGallery: 'Open Backdraw gallery',
|
|
147
|
+
},
|
|
148
|
+
tr: {
|
|
149
|
+
bridge: 'Köprü',
|
|
150
|
+
project: 'Proje',
|
|
151
|
+
job: 'İş',
|
|
152
|
+
connected: 'Bağlı',
|
|
153
|
+
notConnected: 'Bağlı değil',
|
|
154
|
+
noHost: 'Yerel köprü bulunamadı{error}. Kurulum betiğini (install.js) çalıştırdığından emin ol.',
|
|
155
|
+
notSelected: 'Seçilmedi',
|
|
156
|
+
idle: 'Boşta',
|
|
157
|
+
useProject: '“{name}” projesini kullan',
|
|
158
|
+
selectFailed: 'Seçilemedi.',
|
|
159
|
+
version: 'Sürüm',
|
|
160
|
+
footerNote: 'Görseller arka planda üretilir; aktif sekmen değişmez.',
|
|
161
|
+
collections: 'Koleksiyonlar',
|
|
162
|
+
noCollections: 'Henüz koleksiyon yok. Herhangi bir sayfada bir görsele sağ tıklayıp "Backdraw\'a ekle"yi seç.',
|
|
163
|
+
imageCountOne: '1 görsel',
|
|
164
|
+
imageCount: '{count} görsel',
|
|
165
|
+
back: '‹ Geri',
|
|
166
|
+
rename: 'Yeniden adlandır',
|
|
167
|
+
save: 'Kaydet',
|
|
168
|
+
cancel: 'Vazgeç',
|
|
169
|
+
deleteCollection: 'Koleksiyonu sil',
|
|
170
|
+
confirmDelete: '"{name}" ve içindeki {images} diskten silinsin mi?',
|
|
171
|
+
delete: 'Sil',
|
|
172
|
+
removeImage: 'Bu görseli sil',
|
|
173
|
+
confirmRemoveImage: 'Silmek için tekrar tıkla',
|
|
174
|
+
loading: 'Yükleniyor…',
|
|
175
|
+
noThumb: 'önizleme yok',
|
|
176
|
+
leftover: 'Her şey silinemedi. Klasörde kalanlar: {files}',
|
|
177
|
+
newTitle: 'Yeni koleksiyon',
|
|
178
|
+
nameLabel: 'Koleksiyon adı',
|
|
179
|
+
namePlaceholder: 'örn. Karakter referansları',
|
|
180
|
+
nameExists: 'Bu adda bir koleksiyon var; görsel ona eklenir.',
|
|
181
|
+
nameInvalid: '1 ile 64 karakter arasında bir ad yaz.',
|
|
182
|
+
add: 'Ekle',
|
|
183
|
+
preparing: 'Görsel hazırlanıyor…',
|
|
184
|
+
saving: 'Kaydediliyor…',
|
|
185
|
+
toastAdding: '{name} koleksiyonuna ekleniyor…',
|
|
186
|
+
toastAdded: '{name} koleksiyonuna eklendi · {images}',
|
|
187
|
+
toastDuplicate: 'Zaten {name} içinde · {images}',
|
|
188
|
+
errNotConnected: 'Backdraw bağlı değil. Kurulu mu (node install.js)?',
|
|
189
|
+
errNotImage: 'Bu, Backdraw\'ın kaydedebileceği bir görsel değil.',
|
|
190
|
+
errTooLarge: 'Görsel {mb} MB\'tan büyük.',
|
|
191
|
+
errDownload: 'Görsel indirilemedi ({reason}).',
|
|
192
|
+
errNoSource: 'Chrome bu görselin adresini iletmedi (çok büyük gömülü görsellerde iletmiyor), bu yüzden eklenemiyor.',
|
|
193
|
+
errExpired: 'Bu pencerenin süresi doldu; görsele yeniden sağ tıkla.',
|
|
194
|
+
errTimeout: 'Backdraw zamanında yanıt vermedi.',
|
|
195
|
+
menuAdd: 'Backdraw\'a ekle',
|
|
196
|
+
menuNew: 'Yeni koleksiyon…',
|
|
197
|
+
menuOffline: 'Backdraw bağlı değil',
|
|
198
|
+
settings: 'Ayarlar',
|
|
199
|
+
language: 'Dil',
|
|
200
|
+
theme: 'Tema',
|
|
201
|
+
optAuto: 'Otomatik',
|
|
202
|
+
langEnglish: 'English',
|
|
203
|
+
langTurkish: 'Türkçe',
|
|
204
|
+
themeLight: 'Açık',
|
|
205
|
+
themeDark: 'Koyu',
|
|
206
|
+
setupTitle: 'Backdraw kurulumu',
|
|
207
|
+
setupChecklist: 'Kurulum',
|
|
208
|
+
setupCount: '{done}/{total} tamam',
|
|
209
|
+
stepBridge: 'Yerel köprü bağlı',
|
|
210
|
+
bridgeOk: 'Chrome bu bilgisayardaki Backdraw ile konuşuyor.',
|
|
211
|
+
bridgeMissing: 'Chrome, Backdraw köprüsüne ulaşamıyor.',
|
|
212
|
+
chromeSays: 'Chrome\'un mesajı: {error}',
|
|
213
|
+
bridgeFix: 'Backdraw klasöründe bunu çalıştır, sonra bu pencereyi yeniden aç:',
|
|
214
|
+
bridgeReload: 'Yeni kurduysan: chrome://extensions → Backdraw → Yeniden yükle.',
|
|
215
|
+
retry: 'Tekrar dene',
|
|
216
|
+
copy: 'Kopyala',
|
|
217
|
+
copied: 'Kopyalandı',
|
|
218
|
+
stepChatgpt: 'ChatGPT oturumu açık',
|
|
219
|
+
chatgptIn: 'Chrome\'da ChatGPT oturumu açık.',
|
|
220
|
+
chatgptOut: 'Chrome\'da ChatGPT oturumu açık değil.',
|
|
221
|
+
chatgptChallenge: 'ChatGPT bir insan doğrulaması gösteriyor. ChatGPT\'yi açıp doğrulamayı tamamla.',
|
|
222
|
+
chatgptUnknown: 'Henüz kontrol edilmedi. Kontrol, chatgpt.com\'u kısa bir süre arka planda bir sekmede açar.',
|
|
223
|
+
chatgptChecking: 'Arka planda bir sekmede kontrol ediliyor…',
|
|
224
|
+
openChatgptSignIn: 'Oturum açmak için ChatGPT\'yi aç',
|
|
225
|
+
openChatgpt: 'ChatGPT\'yi aç',
|
|
226
|
+
checkNow: 'Şimdi kontrol et',
|
|
227
|
+
checkAgain: 'Tekrar kontrol et',
|
|
228
|
+
errChatgptCheck: 'ChatGPT yüklenmeyi bitirmedi; tekrar dene.',
|
|
229
|
+
stepProject: 'Proje seçili',
|
|
230
|
+
projectUsing: 'Görseller “{name}” projesine gidiyor.',
|
|
231
|
+
projectGuide: 'ChatGPT\'de görsellerin için bir Proje oluştur (ör. "Backdraw") ve onu aç. Sonra Backdraw simgesine yeniden tıkla: burada "… projesini kullan" düğmesi çıkar.',
|
|
232
|
+
projectNeedsBridge: 'Önce köprü gerekli (1. adım).',
|
|
233
|
+
chooseProject: 'Projelerimden seç',
|
|
234
|
+
loadingProjects: 'Projelerin arka planda bir sekmede okunuyor…',
|
|
235
|
+
noProjects: 'ChatGPT\'de proje bulunamadı.',
|
|
236
|
+
stepReady: 'Hazır',
|
|
237
|
+
readyIntro: 'Claude\'a örneğin şunu yaz:',
|
|
238
|
+
examplePrompt: 'ChatGPT ile bir görsel üret: sıcacık bir okuma köşesi, 16:9',
|
|
239
|
+
readyCollections: 'Referans topla: herhangi bir görsele sağ tıkla → "Backdraw\'a ekle".',
|
|
240
|
+
readyWaiting: 'Önce yukarıdaki adımları bitir.',
|
|
241
|
+
gotIt: 'Tamam',
|
|
242
|
+
chatgpt: 'ChatGPT',
|
|
243
|
+
signedIn: 'Oturum açık',
|
|
244
|
+
signedOut: 'Oturum kapalı',
|
|
245
|
+
notChecked: 'Kontrol edilmedi',
|
|
246
|
+
verification: 'Doğrulama gerekli',
|
|
247
|
+
openGallery: 'Galeriyi aç',
|
|
248
|
+
jobPercent: '{step} (%{pct})',
|
|
249
|
+
jobStarting: 'Başlıyor',
|
|
250
|
+
jobOpening: 'ChatGPT açılıyor',
|
|
251
|
+
jobAttaching: 'Referans görseller ekleniyor',
|
|
252
|
+
jobTyping: 'İstem yazılıyor',
|
|
253
|
+
jobSending: 'İstem gönderiliyor',
|
|
254
|
+
jobGenerating: 'ChatGPT görseli üretiyor',
|
|
255
|
+
jobReattaching: 'Görsel yeniden ekleniyor',
|
|
256
|
+
jobVerifying: 'Son hâli kontrol ediliyor',
|
|
257
|
+
jobDownloading: 'Görsel indiriliyor',
|
|
258
|
+
jobNeedsHuman: 'ChatGPT sekmesinde seni bekliyor (oturum açma ya da doğrulama)',
|
|
259
|
+
jobResuming: 'Devam ediyor',
|
|
260
|
+
galleryTitle: 'Galeri',
|
|
261
|
+
refresh: 'Yenile',
|
|
262
|
+
loadMore: 'Daha fazla yükle',
|
|
263
|
+
galleryEmpty: 'Henüz görsel yok. Claude\'un Backdraw ile ürettiği görseller burada görünür.',
|
|
264
|
+
galleryFetched: 'Bir ChatGPT sohbetinden alındı',
|
|
265
|
+
elapsed: '{time} geçti',
|
|
266
|
+
openChat: 'Sohbeti aç',
|
|
267
|
+
noChatLink: 'Sohbet bağlantısı yok',
|
|
268
|
+
showInFolder: 'Klasörde göster',
|
|
269
|
+
copyPath: 'Yolu kopyala',
|
|
270
|
+
pathCopied: 'Yol kopyalandı',
|
|
271
|
+
copyFailed: 'Yol kopyalanamadı.',
|
|
272
|
+
addToCollection: 'Koleksiyona ekle',
|
|
273
|
+
newCollectionItem: 'Yeni koleksiyon…',
|
|
274
|
+
fileMissing: 'Dosya taşınmış ya da silinmiş.',
|
|
275
|
+
stepQueued: 'Sırasını bekliyor',
|
|
276
|
+
stepStarting: 'Başlıyor',
|
|
277
|
+
stepOpening: 'ChatGPT açılıyor',
|
|
278
|
+
stepAttaching: 'Referans görseller ekleniyor',
|
|
279
|
+
stepTyping: 'İstem yazılıyor',
|
|
280
|
+
stepSending: 'İstem gönderiliyor',
|
|
281
|
+
stepGenerating: 'ChatGPT üretiyor',
|
|
282
|
+
stepVerifying: 'Son görsel kontrol ediliyor',
|
|
283
|
+
stepDownloading: 'Görsel indiriliyor',
|
|
284
|
+
stepReattaching: 'Görsel yeniden ekleniyor',
|
|
285
|
+
stepNeedsHuman: 'Seni bekliyor: Chrome\'da ChatGPT sekmesini açıp girişi ya da doğrulamayı tamamla',
|
|
286
|
+
stepResuming: 'Devam ediliyor',
|
|
287
|
+
menuGallery: 'Backdraw galerisini aç',
|
|
288
|
+
},
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
const PREFERENCES = ['auto', 'en', 'tr'];
|
|
292
|
+
const LANGUAGE_KEY = 'language'; // in chrome.storage.local
|
|
293
|
+
let preference = 'auto';
|
|
294
|
+
|
|
295
|
+
// The language a preference means; 'auto' takes Turkish for a Turkish browser, else English.
|
|
296
|
+
function resolve(pref, uiLanguage) {
|
|
297
|
+
if (pref === 'en' || pref === 'tr') return pref;
|
|
298
|
+
return /^tr\b/i.test(String(uiLanguage || '')) ? 'tr' : 'en';
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Chrome's own UI language, as _locales uses; navigator.language follows the websites' language list.
|
|
302
|
+
function uiLanguage() {
|
|
303
|
+
try { return chrome.i18n.getUILanguage(); } catch {}
|
|
304
|
+
try { return navigator.language; } catch { return ''; }
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const lang = () => resolve(preference, uiLanguage());
|
|
308
|
+
|
|
309
|
+
function setPreference(pref) {
|
|
310
|
+
preference = PREFERENCES.includes(pref) ? pref : 'auto';
|
|
311
|
+
return lang();
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Reads the user's choice; pages and the worker call it once before they show texts.
|
|
315
|
+
async function load() {
|
|
316
|
+
try { setPreference((await chrome.storage.local.get(LANGUAGE_KEY))[LANGUAGE_KEY]); } catch {}
|
|
317
|
+
return lang();
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// onChange(lang) runs whenever the choice changes (in the popup, maybe in another window).
|
|
321
|
+
function watch(onChange) {
|
|
322
|
+
try {
|
|
323
|
+
chrome.storage.onChanged.addListener((changes, area) => {
|
|
324
|
+
if (area !== 'local' || !changes[LANGUAGE_KEY]) return;
|
|
325
|
+
setPreference(changes[LANGUAGE_KEY].newValue);
|
|
326
|
+
onChange(lang());
|
|
327
|
+
});
|
|
328
|
+
} catch {}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// A path or message as the extension shows it: the user's home folder (it holds their name) becomes
|
|
332
|
+
// "~". home comes from the host; without it, user folders are recognised by their usual shape.
|
|
333
|
+
function shortPath(text, home) {
|
|
334
|
+
const s = String(text ?? '');
|
|
335
|
+
const h = String(home || '').replace(/[\\/]+$/, '');
|
|
336
|
+
if (h) return s.replace(new RegExp(`${h.replace(/[.*+?^$()|[\]{}\\]/g, '\\$&')}(?!\\w)`, 'gi'), '~');
|
|
337
|
+
return s.replace(/(^|[\s"'(])[A-Za-z]:\\Users\\[^\\/"'\r\n]+/g, '$1~').replace(/(^|[\s"'(])\/(?:Users|home)\/[^/\s"']+/g, '$1~');
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// {name} placeholders are filled from vars; an unknown key shows as itself.
|
|
341
|
+
function translate(language, key, vars) {
|
|
342
|
+
const s = (STRINGS[language] && STRINGS[language][key]) || STRINGS.en[key] || key;
|
|
343
|
+
return vars ? s.replace(/\{(\w+)\}/g, (m, k) => (k in vars ? String(vars[k]) : m)) : s;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const t = (key, vars) => translate(lang(), key, vars);
|
|
347
|
+
const images = n => t(n === 1 ? 'imageCountOne' : 'imageCount', { count: n });
|
|
348
|
+
|
|
349
|
+
// A page names its texts with data-i18n (text), data-i18n-title and data-i18n-placeholder.
|
|
350
|
+
function apply(root = document) {
|
|
351
|
+
for (const el of root.querySelectorAll('[data-i18n]')) el.textContent = t(el.dataset.i18n);
|
|
352
|
+
for (const el of root.querySelectorAll('[data-i18n-title]')) el.title = t(el.dataset.i18nTitle);
|
|
353
|
+
for (const el of root.querySelectorAll('[data-i18n-placeholder]')) el.placeholder = t(el.dataset.i18nPlaceholder);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const api = { STRINGS, PREFERENCES, LANGUAGE_KEY, lang, resolve, setPreference, load, watch, translate, t, images, apply, shortPath };
|
|
357
|
+
if (typeof module === 'object' && module.exports) module.exports = api;
|
|
358
|
+
else globalThis.i18n = api;
|
|
359
|
+
})();
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|