mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-14 20:33:41 +00:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0bd306773d | ||
|
|
8b02844ec3 | ||
|
|
b8e517b35b | ||
|
|
2daa9c45df | ||
|
|
27c0f81a1e | ||
|
|
1907f404ac | ||
|
|
72c5ba7d20 |
No files matched your search
@@ -872,9 +872,9 @@ func (f *Fetcher) expandConnections() {
|
|||||||
f.connMu.Unlock()
|
f.connMu.Unlock()
|
||||||
|
|
||||||
if len(newConns) == 0 {
|
if len(newConns) == 0 {
|
||||||
// No new connections could be created, stop expansion
|
// No new connections could be created, stop expansion.
|
||||||
|
// The downloadLoop will check stateSteady and call waitForCompletion.
|
||||||
f.setState(stateSteady)
|
f.setState(stateSteady)
|
||||||
go f.waitForCompletion()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -987,6 +987,14 @@ func (f *Fetcher) downloadChunkOnce(conn *connection, client *http.Client, buf [
|
|||||||
f.connMu.Unlock()
|
f.connMu.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
// For non-range downloads, reset chunk state since server will send from beginning.
|
||||||
|
// Without Range support, each new request restarts the entire transfer.
|
||||||
|
if !f.meta.Res.Range {
|
||||||
|
conn.Chunk.Begin = 0
|
||||||
|
conn.Chunk.End = 0
|
||||||
|
conn.Chunk.Downloaded = 0
|
||||||
|
conn.Downloaded = 0
|
||||||
|
}
|
||||||
rangeStart := conn.Chunk.Begin + conn.Chunk.Downloaded
|
rangeStart := conn.Chunk.Begin + conn.Chunk.Downloaded
|
||||||
rangeEnd := conn.Chunk.End
|
rangeEnd := conn.Chunk.End
|
||||||
f.connMu.Unlock()
|
f.connMu.Unlock()
|
||||||
@@ -1080,6 +1088,17 @@ func (f *Fetcher) downloadChunkOnce(conn *connection, client *http.Client, buf [
|
|||||||
n = int(remain)
|
n = int(remain)
|
||||||
finished = true
|
finished = true
|
||||||
}
|
}
|
||||||
|
} else if f.meta.Res.Size > 0 {
|
||||||
|
// For non-range downloads with known size, enforce size limit
|
||||||
|
remaining := f.meta.Res.Size - conn.Downloaded
|
||||||
|
if remaining <= 0 {
|
||||||
|
f.connMu.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if remaining < int64(n) {
|
||||||
|
n = int(remaining)
|
||||||
|
finished = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
writeOffset = conn.Chunk.Begin + conn.Chunk.Downloaded
|
writeOffset = conn.Chunk.Begin + conn.Chunk.Downloaded
|
||||||
f.connMu.Unlock()
|
f.connMu.Unlock()
|
||||||
@@ -1174,6 +1193,22 @@ func (f *Fetcher) runConnectionWithResolveResp(conn *connection) {
|
|||||||
|
|
||||||
n, err := reader.Read(buf)
|
n, err := reader.Read(buf)
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
|
// For non-range downloads with known size, enforce size limit
|
||||||
|
if !f.meta.Res.Range && f.meta.Res.Size > 0 {
|
||||||
|
f.connMu.Lock()
|
||||||
|
remaining := f.meta.Res.Size - conn.Downloaded
|
||||||
|
if remaining <= 0 {
|
||||||
|
conn.Completed = true
|
||||||
|
conn.State = connCompleted
|
||||||
|
f.connMu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if remaining < int64(n) {
|
||||||
|
n = int(remaining)
|
||||||
|
}
|
||||||
|
f.connMu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
f.fileMu.Lock()
|
f.fileMu.Lock()
|
||||||
if f.file != nil {
|
if f.file != nil {
|
||||||
_, writeErr := f.file.WriteAt(buf[:n], conn.Chunk.Downloaded)
|
_, writeErr := f.file.WriteAt(buf[:n], conn.Chunk.Downloaded)
|
||||||
@@ -1191,8 +1226,18 @@ func (f *Fetcher) runConnectionWithResolveResp(conn *connection) {
|
|||||||
}
|
}
|
||||||
f.fileMu.Unlock()
|
f.fileMu.Unlock()
|
||||||
|
|
||||||
|
f.connMu.Lock()
|
||||||
conn.Chunk.Downloaded += int64(n)
|
conn.Chunk.Downloaded += int64(n)
|
||||||
conn.Downloaded += int64(n)
|
conn.Downloaded += int64(n)
|
||||||
|
|
||||||
|
// Check if size limit reached after updating counters
|
||||||
|
if !f.meta.Res.Range && f.meta.Res.Size > 0 && conn.Downloaded >= f.meta.Res.Size {
|
||||||
|
conn.Completed = true
|
||||||
|
conn.State = connCompleted
|
||||||
|
f.connMu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f.connMu.Unlock()
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
@@ -1232,6 +1277,14 @@ func (f *Fetcher) runConnectionFallback(conn *connection) {
|
|||||||
|
|
||||||
f.connMu.Lock()
|
f.connMu.Lock()
|
||||||
conn.State = connConnecting
|
conn.State = connConnecting
|
||||||
|
// For non-range downloads, reset chunk state since server will send from beginning.
|
||||||
|
// Without Range support, each new request restarts the entire transfer.
|
||||||
|
if !f.meta.Res.Range {
|
||||||
|
conn.Chunk.Begin = 0
|
||||||
|
conn.Chunk.End = 0
|
||||||
|
conn.Chunk.Downloaded = 0
|
||||||
|
conn.Downloaded = 0
|
||||||
|
}
|
||||||
f.connMu.Unlock()
|
f.connMu.Unlock()
|
||||||
|
|
||||||
err := func() error {
|
err := func() error {
|
||||||
@@ -1276,6 +1329,17 @@ func (f *Fetcher) runConnectionFallback(conn *connection) {
|
|||||||
|
|
||||||
n, err := reader.Read(buf)
|
n, err := reader.Read(buf)
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
|
// For non-range downloads with known size, enforce size limit
|
||||||
|
if !f.meta.Res.Range && f.meta.Res.Size > 0 {
|
||||||
|
remaining := f.meta.Res.Size - conn.Downloaded
|
||||||
|
if remaining <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if remaining < int64(n) {
|
||||||
|
n = int(remaining)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
f.fileMu.Lock()
|
f.fileMu.Lock()
|
||||||
if f.file != nil {
|
if f.file != nil {
|
||||||
_, writeErr := f.file.WriteAt(buf[:n], conn.Chunk.Downloaded)
|
_, writeErr := f.file.WriteAt(buf[:n], conn.Chunk.Downloaded)
|
||||||
@@ -1475,17 +1539,31 @@ func (f *Fetcher) onDownloadComplete() {
|
|||||||
// Check if all chunks are complete (no remaining bytes)
|
// Check if all chunks are complete (no remaining bytes)
|
||||||
allChunksComplete := true
|
allChunksComplete := true
|
||||||
for _, conn := range f.connections {
|
for _, conn := range f.connections {
|
||||||
if conn.Chunk != nil && conn.Chunk.remain() > 0 && !conn.Completed && conn.State != connCompleted {
|
if !conn.Completed && conn.State != connCompleted {
|
||||||
// This connection has remaining work and isn't done
|
if f.meta.Res.Range {
|
||||||
// Check if it failed with 403 (server limit) - these can be ignored if other connections completed the work
|
// For range downloads, use chunk remain() to check completion
|
||||||
if conn.State == connFailed && conn.failed {
|
if conn.Chunk != nil && conn.Chunk.remain() > 0 {
|
||||||
if re := extractRequestError(conn.lastErr); re != nil && re.Code == 403 {
|
// This connection has remaining work and isn't done
|
||||||
// 403 is server connection limit, check if other connections will complete this chunk
|
// Check if it failed with 403 (server limit) - these can be ignored if other connections completed the work
|
||||||
continue
|
if conn.State == connFailed && conn.failed {
|
||||||
|
if re := extractRequestError(conn.lastErr); re != nil && re.Code == 403 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
allChunksComplete = false
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// For non-range downloads, completion is determined solely by EOF (conn.Completed).
|
||||||
|
// chunk.remain() is unreliable for non-range chunks (Begin=0, End=0).
|
||||||
|
if conn.State == connFailed && conn.failed {
|
||||||
|
if re := extractRequestError(conn.lastErr); re != nil && re.Code == 403 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
allChunksComplete = false
|
||||||
|
break
|
||||||
}
|
}
|
||||||
allChunksComplete = false
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -307,6 +307,58 @@ func TestFetcher_DownloadChunked(t *testing.T) {
|
|||||||
downloadNormal(listener, 2, t)
|
downloadNormal(listener, 2, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestFetcher_DownloadNoRangePauseResume targets a specific bug where pausing
|
||||||
|
// and resuming a download from a server that doesn't return Content-Length
|
||||||
|
// (and doesn't support Range) causes the downloaded file to grow beyond the
|
||||||
|
// actual file size. Root cause: on resume the server resends from byte 0, but
|
||||||
|
// the old code wrote at the previously accumulated offset, appending a second
|
||||||
|
// copy instead of overwriting. The fix resets the write offset on each non-range
|
||||||
|
// retry so data is always written from the beginning.
|
||||||
|
func TestFetcher_DownloadNoRangePauseResume(t *testing.T) {
|
||||||
|
listener := test.StartTestCustomServer()
|
||||||
|
defer listener.Close()
|
||||||
|
|
||||||
|
fetcher := downloadReady(listener, 1, t)
|
||||||
|
err := fetcher.Start()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// Let it download some data
|
||||||
|
time.Sleep(time.Millisecond * 50)
|
||||||
|
if err := fetcher.Pause(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
time.Sleep(time.Millisecond * 50)
|
||||||
|
if err := fetcher.Start(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = fetcher.Wait()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The critical assertion: the downloaded file must not be larger than
|
||||||
|
// the source file. Before the fix, each resume appended extra data,
|
||||||
|
// causing the file to exceed the actual size (the bug shown in the issue).
|
||||||
|
downloadInfo, err := os.Stat(test.DownloadFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
sourceInfo, err := os.Stat(test.BuildFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if downloadInfo.Size() > sourceInfo.Size() {
|
||||||
|
t.Errorf("Downloaded file size %d exceeds source file size %d", downloadInfo.Size(), sourceInfo.Size())
|
||||||
|
}
|
||||||
|
|
||||||
|
want := test.FileMd5(test.BuildFile)
|
||||||
|
got := test.FileMd5(test.DownloadFile)
|
||||||
|
if want != got {
|
||||||
|
t.Errorf("Download() got = %v, want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestFetcher_DownloadPost(t *testing.T) {
|
func TestFetcher_DownloadPost(t *testing.T) {
|
||||||
listener := test.StartTestPostServer()
|
listener := test.StartTestPostServer()
|
||||||
defer listener.Close()
|
defer listener.Close()
|
||||||
|
|||||||
@@ -0,0 +1,221 @@
|
|||||||
|
const caES = {
|
||||||
|
'ca_ES': {
|
||||||
|
'label': 'Català',
|
||||||
|
'error': 'Error',
|
||||||
|
'tip': 'Consell',
|
||||||
|
'confirm': 'Confirma',
|
||||||
|
'confirmDelete': 'Esteu segur que voleu suprimir-lo?',
|
||||||
|
'cancel': 'Cancel·la',
|
||||||
|
'on': 'Activat',
|
||||||
|
'off': 'Desactivat',
|
||||||
|
'selectAll': 'Selecciona-ho tot',
|
||||||
|
'select': 'Selecciona',
|
||||||
|
'task': 'Tasques',
|
||||||
|
'downloading': 's\'està baixant',
|
||||||
|
'downloaded': 's\'ha baixat',
|
||||||
|
'setting': 'Configuració',
|
||||||
|
'donate': 'Donació',
|
||||||
|
'exit': 'Surt',
|
||||||
|
'create': 'Crea una tasca',
|
||||||
|
'directDownload': 'Baixada directa',
|
||||||
|
'advancedOptions': 'Opcions avançades',
|
||||||
|
'followSettings': 'Segueix la configuració',
|
||||||
|
'downloadLink': 'Enllaç de baixada',
|
||||||
|
'downloadLinkValid': 'Introduïu l\'enllaç de baixada',
|
||||||
|
'downloadLinkHit': 'Introduïu l\'enllaç de baixada, un per línia@append',
|
||||||
|
'downloadLinkHitDesktop': 'o arrossegueu el fitxer torrent aquí',
|
||||||
|
'download': 'Baixa',
|
||||||
|
'noFileSelected': 'Seleccioneu almenys un fitxer per continuar.',
|
||||||
|
'noStoragePermission': 'Cal permís d\'emmagatzematge',
|
||||||
|
'selectFile': 'Seleccioneu el fitxer',
|
||||||
|
'rename': 'Canvia el nom',
|
||||||
|
'basic': 'Bàsic',
|
||||||
|
'advanced': 'Avançat',
|
||||||
|
'general': 'General',
|
||||||
|
'downloadDir': 'Carpeta de baixades',
|
||||||
|
'downloadDirValid': 'Seleccioneu la carpeta de baixades',
|
||||||
|
'connections': 'Connexions',
|
||||||
|
'useServerCtime': 'Utilitza l\'hora del servidor per a la creació de fitxers',
|
||||||
|
'maxRunning': 'Màxim de tasques en execució',
|
||||||
|
'defaultDirectDownload': 'Activa la baixada directa per defecte',
|
||||||
|
'autoStartTasks': 'Inicia automàticament les tasques incompletes a l\'inici',
|
||||||
|
'autoTorrentEnable': 'Crea automàticament tasques BT a partir de fitxers .torrent',
|
||||||
|
'autoTorrentDeleteAfterDownload':
|
||||||
|
'Suprimeix el fitxer .torrent després de crear la tasca BT',
|
||||||
|
'autoDeleteMissingFileTasks': 'Suprimeix automàticament les tasques dels fitxers que falten',
|
||||||
|
'items': '@count elements',
|
||||||
|
'subscribeTracker': 'Subscripció al rastrejador',
|
||||||
|
'subscribeFail':
|
||||||
|
'La subscripció ha fallat. Comproveu la xarxa o torneu-ho a provar més tard',
|
||||||
|
'update': 'Actualitza',
|
||||||
|
'updateDaily': 'Actualització diària',
|
||||||
|
'lastUpdate': 'Última actualització: @time',
|
||||||
|
'addTracker': 'Afegeix un rastrejador',
|
||||||
|
'addTrackerHit': 'Introduïu la URL del servidor del rastrejador, un per línia',
|
||||||
|
'ui': 'UI',
|
||||||
|
'theme': 'Tema',
|
||||||
|
'themeSystem': 'Sistema',
|
||||||
|
'themeLight': 'Clar',
|
||||||
|
'themeDark': 'Fosc',
|
||||||
|
'locale': 'Idioma',
|
||||||
|
'notifyWhenNewVersion': 'Notificació d\'actualitzacions',
|
||||||
|
'analyticsEnabled': 'Puja les estadístiques',
|
||||||
|
'analyticsEnabledDesc': 'Compartiu dades d\'ús anònimes per ajudar-nos a millorar',
|
||||||
|
'about': 'Quant a',
|
||||||
|
'homepage': 'Pàgina d\'inici',
|
||||||
|
'version': 'Versió',
|
||||||
|
'protocol': 'Protocol',
|
||||||
|
'port': 'Port',
|
||||||
|
'apiToken': 'Token de l\'API',
|
||||||
|
'notSet': 'Sense congigurar',
|
||||||
|
'set': 'Configurat',
|
||||||
|
'portInUse': 'El port [@port] està en ús, canvieu-lo',
|
||||||
|
'effectAfterRestart': 'Els efectes s\'aplicaran després de reiniciar',
|
||||||
|
'developer': 'Desenvolupador',
|
||||||
|
'logDirectory': 'Carpeta dels registres',
|
||||||
|
'webhook': 'Webhook',
|
||||||
|
'webhookEnable': 'Activa el Webhook',
|
||||||
|
'webhookDesc': 'Envia notificacions HTTP POST quan les tasques finalitzin o fallin',
|
||||||
|
'webhookUrlHint': 'Introduïu la URL del webhook',
|
||||||
|
'webhookTest': 'Prova',
|
||||||
|
'webhookTestSuccess': 'La prova del webhook ha estat correcta',
|
||||||
|
'webhookTestFail': 'La prova del webhook ha fallat',
|
||||||
|
'script': 'Seqüència d\'ordres',
|
||||||
|
'scriptEnable': 'Activa la seqüència d\'ordres',
|
||||||
|
'scriptDesc': 'Executa seqüències d\'ordres personalitzades quan les baixades finalitzin correctament',
|
||||||
|
'scriptPathHint': 'Introduïu la ruta del fitxer de seqüències d\'ordres (p.ex., /ruta/al/fitxer.sh)',
|
||||||
|
'urlInvalid': 'Introduïu una URL HTTP o HTTPS vàlida',
|
||||||
|
'required': 'Aquest camp és obligatori',
|
||||||
|
'show': 'Mostra',
|
||||||
|
'continue': 'Contina',
|
||||||
|
'pause': 'Pausa',
|
||||||
|
'startAll': 'Inicia-ho tot',
|
||||||
|
'pauseAll': 'Pausa-ho tot',
|
||||||
|
'deleteTask': 'Suprimeix @count tasques',
|
||||||
|
'deleteTaskTip': 'Mantén els fitxers baixats',
|
||||||
|
'delete': 'Suprimeix',
|
||||||
|
'add': 'Afegeix',
|
||||||
|
'edit': 'Edita',
|
||||||
|
'newVersionTitle': 'Descobriu la nova versió @version',
|
||||||
|
'newVersionUpdate': 'Actualitza ara',
|
||||||
|
'newVersionLater': 'Més tard',
|
||||||
|
'extensions': 'Extensions',
|
||||||
|
'extensionInstallUrl': 'URL d\'instal·lació',
|
||||||
|
'extensionInstallSuccess': 'S\'ha instal·lat amb èxit',
|
||||||
|
'extensionUpdateSuccess': 'S\'ha actualitzat amb èxit',
|
||||||
|
'extensionDelete': 'Suprimeix l\'extensió',
|
||||||
|
'extensionAlreadyLatest': 'Ja està actualitzada',
|
||||||
|
'extensionFind': 'Cerca extensions',
|
||||||
|
'extensionDevelop': 'Desenvolupa extensions',
|
||||||
|
'extensionStoreTitle': 'Botiga d\'extensions',
|
||||||
|
'extensionStoreSection': 'Extensions de la botiga',
|
||||||
|
'extensionInstalledSection': 'Extensions instal·lades',
|
||||||
|
'extensionInstalledEmpty': 'Encara no s\'ha instal·lat cap extensió',
|
||||||
|
'extensionStoreEmpty': 'No s\'han trobat extensions coincidents',
|
||||||
|
'extensionSearchHint': 'Cerca per nom, títol, descripció o autor',
|
||||||
|
'extensionSortStars': 'Més estrelles',
|
||||||
|
'extensionSortInstalls': 'Més instal·lacions',
|
||||||
|
'extensionSortUpdated': 'Actualitzada recentment',
|
||||||
|
'extensionSortToggle': 'Commuta la direcció d\'ordenació',
|
||||||
|
'extensionLoadMore': 'Carrega\'n més',
|
||||||
|
'extensionNoMore': 'No hi ha més extensions',
|
||||||
|
'extensionInstalled': 'Instal·lada',
|
||||||
|
'extensionCanUpdate': 'Actualització disponible',
|
||||||
|
'extensionInstall': 'Instal·la',
|
||||||
|
'extensionLoadLocal': 'Instal·la de d\'una carpeta local',
|
||||||
|
'extensionFilterAll': 'Totes',
|
||||||
|
'extensionFilterMarket': 'Mercat',
|
||||||
|
'extensionFilterInstalled': 'Instal·lada',
|
||||||
|
'extensionManualInstall': 'Instal·lació manual',
|
||||||
|
'extensionInstallTools': 'Opcions d\'instal·lació',
|
||||||
|
'history': 'Historial',
|
||||||
|
'clearHistory': 'Neteja l\'historial',
|
||||||
|
'noHistoryFound': 'No s\'ha trobat cap historial',
|
||||||
|
'serviceTitle': 'Servei de baixades',
|
||||||
|
'serviceText': 'S\'està executant',
|
||||||
|
'network': 'Xarxa',
|
||||||
|
'proxy': 'Servidor intermediari',
|
||||||
|
'noProxy': 'Sense servidor intermediari',
|
||||||
|
'systemProxy': 'Servidor intermediari del sistema',
|
||||||
|
'customProxy': 'Servidor intermediari personalitzat',
|
||||||
|
'server': 'Servidor',
|
||||||
|
'username': 'Nom d\'usuari',
|
||||||
|
'password': 'Contrasenya',
|
||||||
|
'thanks': 'Gràcies',
|
||||||
|
'thanksDesc':
|
||||||
|
'Gràcies a tots els col·laboradors que han ajudat a construir i desenvolupar la comunitat del Gopeed!',
|
||||||
|
'browserExtension': 'Extensió del navegador',
|
||||||
|
'launchAtStartup': 'Executa a l\'inici',
|
||||||
|
'runAsMenubarApp': 'Executa com a aplicació de barra de menú',
|
||||||
|
'runAsMenubarAppDesc': 'Amaga la icona de l\'acoblador i executa només a la barra de menú',
|
||||||
|
'seedConfig': 'Configuració de les llavors',
|
||||||
|
'seedKeep': 'Continua sembrant fins que s\'aturi manualment',
|
||||||
|
'seedRatio': 'Proporció de les llavors',
|
||||||
|
'seedTime': 'Temps de la llavor (minuts)',
|
||||||
|
'setAsDefaultBtClient': 'Estableix com a client BT per defecte',
|
||||||
|
'taskDetail': 'Detall de la tasca',
|
||||||
|
'taskName': 'Nom de la tasca',
|
||||||
|
'taskUrl': 'URL de la tasca',
|
||||||
|
'downloadPath': 'Ruta de la baixada',
|
||||||
|
'skipVerifyCert': 'Omet la verificació del certificat',
|
||||||
|
'archives': 'Comprimits',
|
||||||
|
'autoExtract': 'Extreu els comprimits automàticament',
|
||||||
|
'archivePassword': 'Contrasenya del comprimit',
|
||||||
|
'archivePasswordHint': 'Deixeu-lo en blanc si no està protegit amb contrasenya',
|
||||||
|
'deleteAfterExtract': 'Suprimeix el comprimit després de l\'extracció',
|
||||||
|
'extracting': 'S\'està extraient',
|
||||||
|
'extractDone': 'Extracció completada',
|
||||||
|
'extractError': 'Extracció fallida',
|
||||||
|
'waitingParts': 'S\'està esperant a les parts',
|
||||||
|
'name': 'Nom',
|
||||||
|
'size': 'Mida',
|
||||||
|
'unknown': 'Desconegut',
|
||||||
|
'fileSelectedCount': 'Fitxers: ',
|
||||||
|
'fileSelectedSize': 'Mida: ',
|
||||||
|
'httpHeader': 'Capçalera',
|
||||||
|
'httpHeaderName': 'Nomm de la capçalera',
|
||||||
|
'httpHeaderValue': 'Valor de la capçalera',
|
||||||
|
'login': 'Inici de sessió',
|
||||||
|
'username_required': 'Introduïu el vostre nom d\'usuari',
|
||||||
|
'password_required': 'Introduïu la vostra contrasenya',
|
||||||
|
'login_success': 'Inici de sessió correcte',
|
||||||
|
'login_failed': 'No s\'ha pogut iniciar la sessió, comproveu el vostre nom d\'usuari i contrasenya',
|
||||||
|
'login_failed_network':
|
||||||
|
'No s\'ha pogut iniciar la sessió, comproveu la vostra connexió de xarxa',
|
||||||
|
'insertPlaceholder': 'Inseriu un marcador de posició',
|
||||||
|
'placeholderYear': 'Any actual',
|
||||||
|
'placeholderMonth': 'Mes actual (01-12)',
|
||||||
|
'placeholderDay': 'Dia actual (01-31)',
|
||||||
|
'placeholderDate': 'Data completa (AAAA-MM-DD)',
|
||||||
|
'example': 'p.ex. @value',
|
||||||
|
'downloadCategories': 'Categories de baixades',
|
||||||
|
'categoryMusic': 'Música',
|
||||||
|
'categoryVideo': 'Vídeo',
|
||||||
|
'categoryDocument': 'Document',
|
||||||
|
'categoryProgram': 'Programa',
|
||||||
|
'categoryName': 'Nom de la categoria',
|
||||||
|
'categoryPath': 'Ruta de la categoria',
|
||||||
|
'builtInCategory': 'La categoria integrada no es pot suprimir',
|
||||||
|
'selectCategory': 'Seleccioneu una categoria',
|
||||||
|
'githubMirror': 'Rèplica del GitHub',
|
||||||
|
'githubMirrorEnable': 'Activa la rèplica del GitHub',
|
||||||
|
'githubMirrorDesc': 'Utilitzeu rèpliques per accelerar les baixades de contingut del GitHub',
|
||||||
|
'githubMirrorType': 'Tipus de rèplica',
|
||||||
|
'githubMirrorUrl': 'URL de la rèplica',
|
||||||
|
'githubMirrorUrlHint': 'Introduïu la URL de la rèplica',
|
||||||
|
'updateUrl': 'Actualitza la URL',
|
||||||
|
'updateUrlManual': 'Actualització manual',
|
||||||
|
'updateUrlListen': 'Escolta l\'actualització',
|
||||||
|
'updateUrlListeningTip': 'S\'està esperant l\'actualització de l\'URL',
|
||||||
|
'updateUrlCancelListen': 'Cancel·la l\'escolta',
|
||||||
|
'updateUrlDialogHint': 'Introduïu una nova URL de baixada',
|
||||||
|
'pendingUpdateFound': 'S\'ha trobat una tasca d\'actualització pendent',
|
||||||
|
'pendingUpdateConfirm':
|
||||||
|
'La tasca "@name" està esperant l\'actualització de la URL. Voleu actualitzar-la amb la URL nova?',
|
||||||
|
'pendingUpdateYes': 'Actualitza la tasca',
|
||||||
|
'pendingUpdateNo': 'Crea una tasca nova',
|
||||||
|
'desktopNotification': 'Notificacions d\'escriptori',
|
||||||
|
'notificationTaskDone': 'Tasca completada',
|
||||||
|
'notificationTaskError': 'Error de tasca',
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -107,6 +107,27 @@ const trTR = {
|
|||||||
'extensionAlreadyLatest': 'Zaten en son sürüm',
|
'extensionAlreadyLatest': 'Zaten en son sürüm',
|
||||||
'extensionFind': 'Eklenti Bul',
|
'extensionFind': 'Eklenti Bul',
|
||||||
'extensionDevelop': 'Eklenti Geliştir',
|
'extensionDevelop': 'Eklenti Geliştir',
|
||||||
|
'extensionStoreTitle': 'Eklenti Mağazası',
|
||||||
|
'extensionStoreSection': 'Mağaza Eklentileri',
|
||||||
|
'extensionInstalledSection': 'Yüklü Eklentiler',
|
||||||
|
'extensionInstalledEmpty': 'Henüz yüklü eklenti yok',
|
||||||
|
'extensionStoreEmpty': 'Eşleşen eklenti bulunamadı',
|
||||||
|
'extensionSearchHint': 'Ada, başlığa, açıklamaya veya yazara göre ara',
|
||||||
|
'extensionSortStars': 'En Çok Yıldız Alanlar',
|
||||||
|
'extensionSortInstalls': 'En Çok Yüklenenler',
|
||||||
|
'extensionSortUpdated': 'Son Güncellenenler',
|
||||||
|
'extensionSortToggle': 'Sıralama yönünü değiştir',
|
||||||
|
'extensionLoadMore': 'Daha Fazla Yükle',
|
||||||
|
'extensionNoMore': 'Daha fazla eklenti yok',
|
||||||
|
'extensionInstalled': 'Yüklü',
|
||||||
|
'extensionCanUpdate': 'Güncelleme Mevcut',
|
||||||
|
'extensionInstall': 'Yükle',
|
||||||
|
'extensionLoadLocal': 'Yerel Klasörden Yükle',
|
||||||
|
'extensionFilterAll': 'Tümü',
|
||||||
|
'extensionFilterMarket': 'Market',
|
||||||
|
'extensionFilterInstalled': 'Yüklü',
|
||||||
|
'extensionManualInstall': 'Manuel Yükleme',
|
||||||
|
'extensionInstallTools': 'Yükleme Seçenekleri',
|
||||||
'history': 'Geçmiş',
|
'history': 'Geçmiş',
|
||||||
'clearHistory': 'Geçmişi Temizle',
|
'clearHistory': 'Geçmişi Temizle',
|
||||||
'noHistoryFound': 'Geçmiş Bulunamadı',
|
'noHistoryFound': 'Geçmiş Bulunamadı',
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import 'langs/es_es.dart';
|
|||||||
import 'langs/uk_ua.dart';
|
import 'langs/uk_ua.dart';
|
||||||
import 'langs/hu_hu.dart';
|
import 'langs/hu_hu.dart';
|
||||||
import 'langs/pt_br.dart';
|
import 'langs/pt_br.dart';
|
||||||
|
import 'langs/ca_es.dart';
|
||||||
|
|
||||||
final messages = _Messages();
|
final messages = _Messages();
|
||||||
|
|
||||||
@@ -42,6 +43,7 @@ class _Messages extends Translations {
|
|||||||
...ukUA,
|
...ukUA,
|
||||||
...huHU,
|
...huHU,
|
||||||
...ptBR,
|
...ptBR,
|
||||||
...deDE
|
...deDE,
|
||||||
|
...caES
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user