Merge branch 'develop' into dogo-patch-1

This commit is contained in:
jliddev
2020-11-18 15:10:37 -06:00
committed by GitHub
28 changed files with 320 additions and 114 deletions

View File

@@ -5,4 +5,8 @@ phases:
commands:
- echo Install phase...
- cd wowup-electron
- npm i
- npm i
build:
commands:
- echo Build phase...
- npm run electron:publish:never

View File

@@ -1,7 +1,7 @@
{
"name": "wowup",
"productName": "WowUp",
"version": "2.0.0-beta.18",
"version": "2.0.0-beta.19",
"description": "Word of Warcraft addon updater",
"homepage": "https://wowup.io",
"author": {

View File

@@ -26,6 +26,7 @@ export interface AddonProvider {
getById(addonId: string, clientType: WowClientType): Observable<AddonSearchResult>;
isValidAddonUri(addonUri: URL): boolean;
isValidAddonId(addonId: string): boolean;
onPostInstall(addon: Addon): void;

View File

@@ -342,6 +342,10 @@ export class CurseAddonProvider implements AddonProvider {
return addonUri.host && addonUri.host.endsWith("curseforge.com") && addonUri.pathname.startsWith("/wow/addons");
}
isValidAddonId(addonId: string): boolean {
return !!addonId && !isNaN(parseInt(addonId, 10));
}
onPostInstall(addon: Addon): void {
throw new Error("Method not implemented.");
}

View File

@@ -148,6 +148,10 @@ export class GitHubAddonProvider implements AddonProvider {
return addonUri.host && addonUri.host.endsWith("github.com");
}
public isValidAddonId(addonId: string): boolean {
return addonId.indexOf("/") !== -1;
}
public onPostInstall(addon: Addon): void {}
public async scan(

View File

@@ -108,6 +108,10 @@ export class TukUiAddonProvider implements AddonProvider {
return false;
}
isValidAddonId(addonId: string): boolean {
return !!addonId && !isNaN(parseInt(addonId, 10));
}
onPostInstall(addon: Addon): void {}
async scan(

View File

@@ -88,17 +88,21 @@ export class WowInterfaceAddonProvider implements AddonProvider {
throw new Error("Method not implemented.");
}
getById(addonId: string, clientType: WowClientType): Observable<AddonSearchResult> {
public getById(addonId: string, clientType: WowClientType): Observable<AddonSearchResult> {
return from(this._circuitBreaker.fire(addonId)).pipe(
map((result) => (result ? this.toAddonSearchResult(result, "") : undefined))
);
}
isValidAddonUri(addonUri: URL): boolean {
public isValidAddonUri(addonUri: URL): boolean {
return addonUri.host && addonUri.host.endsWith("wowinterface.com");
}
onPostInstall(addon: Addon): void {
public isValidAddonId(addonId: string): boolean {
return !!addonId && !isNaN(parseInt(addonId, 10));
}
public onPostInstall(addon: Addon): void {
throw new Error("Method not implemented.");
}

View File

@@ -67,6 +67,10 @@ export class WowUpAddonProvider implements AddonProvider {
return false;
}
isValidAddonId(addonId: string): boolean {
return true;
}
onPostInstall(addon: Addon): void {
throw new Error("Method not implemented.");
}

View File

@@ -35,7 +35,8 @@
</div>
<div class="addon-version text-2 row align-items-center" [ngClass]="{ ignored: listItem.isIgnored }">
<div *ngIf="addonUtils.hasMultipleProviders(listItem.addon)" class="mr-2">
<mat-icon class="auto-update-icon" svgIcon="fas:code-branch" [matTooltip]="'This addon has multiple providers'">
<mat-icon class="auto-update-icon" svgIcon="fas:code-branch"
[matTooltip]="'PAGES.MY_ADDONS.MULTIPLE_PROVIDERS_TOOLTIP' | translate">
</mat-icon>
</div>
<div *ngIf="this.listItem.isAutoUpdate === true" class="mr-2">

View File

@@ -6,6 +6,10 @@
"PAGES.OPTIONS.WOW.CLIENT_TYPE_PATH_LABEL" | translate: { clientTypeName: (clientTypeName | translate) }
}}</mat-label>
<input matInput disabled [value]="clientLocation" />
<button mat-button color="accent" *ngIf="clientLocation" matSuffix mat-icon-button aria-label="Clear"
(click)="clearInstallPath()">
<mat-icon svgIcon="fas:times"></mat-icon>
</button>
<mat-hint class="text-2">
{{
"PAGES.OPTIONS.WOW.CLIENT_TYPE_INPUT_HINT"

View File

@@ -2,9 +2,12 @@ import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from "@angular
import { MatDialog } from "@angular/material/dialog";
import { MatSelectChange } from "@angular/material/select";
import { MatSlideToggleChange } from "@angular/material/slide-toggle";
import { TranslateService } from "@ngx-translate/core";
import * as _ from "lodash";
import { map } from "lodash";
import * as path from "path";
import { Subscription } from "rxjs";
import { from, Subscription } from "rxjs";
import { switchMap } from "rxjs/operators";
import { WowClientType } from "../../models/warcraft/wow-client-type";
import { AddonChannelType } from "../../models/wowup/addon-channel-type";
import { ElectronService } from "../../services";
@@ -12,6 +15,7 @@ import { WarcraftService } from "../../services/warcraft/warcraft.service";
import { WowUpService } from "../../services/wowup/wowup.service";
import { getEnumList, getEnumName } from "../../utils/enum.utils";
import { AlertDialogComponent } from "../alert-dialog/alert-dialog.component";
import { ConfirmDialogComponent } from "../confirm-dialog/confirm-dialog.component";
@Component({
selector: "app-wow-client-options",
@@ -40,7 +44,8 @@ export class WowClientOptionsComponent implements OnInit, OnDestroy {
private _electronService: ElectronService,
private _warcraftService: WarcraftService,
private _wowupService: WowUpService,
private _cdRef: ChangeDetectorRef
private _cdRef: ChangeDetectorRef,
private _translateService: TranslateService
) {
this.addonChannelInfos = this.getAddonChannelInfos();
@@ -75,6 +80,32 @@ export class WowClientOptionsComponent implements OnInit, OnDestroy {
this._wowupService.setDefaultAutoUpdate(this.clientType, evt.checked);
}
public async clearInstallPath() {
const dialogRef = this._dialog.open(ConfirmDialogComponent, {
data: {
title: this._translateService.instant("PAGES.OPTIONS.WOW.CLEAR_INSTALL_LOCATION_DIALOG.TITLE"),
message: this._translateService.instant("PAGES.OPTIONS.WOW.CLEAR_INSTALL_LOCATION_DIALOG.MESSAGE", {
clientName: this._translateService.instant(this.clientTypeName),
}),
},
});
const result = await dialogRef.afterClosed().toPromise();
if (!result) {
return;
}
try {
await this._warcraftService.removeWowFolderPath(this.clientType).toPromise();
this.clientLocation = "";
this._cdRef.detectChanges();
console.debug("Remove client location complete");
} catch (e) {
console.error("Failed to remove location", e);
}
}
async onSelectClientPath() {
const selectedPath = await this.selectWowClientPath(this.clientType);
if (selectedPath) {

View File

@@ -3,12 +3,17 @@ import { AddonDependency } from "app/models/wowup/addon-dependency";
import { AddonDependencyType } from "app/models/wowup/addon-dependency-type";
import { AddonSearchResultDependency } from "app/models/wowup/addon-search-result-dependency";
import { Toc } from "app/models/wowup/toc";
import { ADDON_PROVIDER_CURSEFORGE, ADDON_PROVIDER_TUKUI, ADDON_PROVIDER_WOWINTERFACE } from "common/constants";
import {
ADDON_PROVIDER_CURSEFORGE,
ADDON_PROVIDER_TUKUI,
ADDON_PROVIDER_WOWINTERFACE,
ERROR_ADDON_ALREADY_INSTALLED,
} from "common/constants";
import * as fs from "fs";
import * as _ from "lodash";
import * as path from "path";
import { forkJoin, from, Observable, Subject } from "rxjs";
import { map, mergeMap } from "rxjs/operators";
import { filter, first, map, mergeMap, switchMap } from "rxjs/operators";
import * as slug from "slug";
import { v4 as uuidv4 } from "uuid";
import { AddonProvider } from "../../addon-providers/addon-provider";
@@ -69,6 +74,16 @@ export class AddonService {
this._installQueue.pipe(mergeMap((item) => from(this.processInstallQueue(item)), 3)).subscribe((addonName) => {
console.log("Install complete", addonName);
});
// Attempt to remove addons for clients that were lost
this._warcraftService.installedClientTypes$
.pipe(
filter((clientTypes) => !!clientTypes),
switchMap((clientTypes) => from(this.reconcileOrphanAddons(clientTypes)))
)
.subscribe(() => {
console.debug("reconcileOrphanAddons complete");
});
}
public saveAddon(addon: Addon) {
@@ -217,7 +232,7 @@ export class AddonService {
addonId,
onUpdate,
completion,
originalAddon: originalAddon ? { ...originalAddon } : undefined
originalAddon: originalAddon ? { ...originalAddon } : undefined,
});
return promise;
@@ -306,8 +321,9 @@ export class AddonService {
this._addonStorage.set(addon.id, addon);
const actionLabel = `${getEnumName(WowClientType, addon.clientType)}|${addon.providerName}|${addon.externalId}|${addon.name
}`;
const actionLabel = `${getEnumName(WowClientType, addon.clientType)}|${addon.providerName}|${addon.externalId}|${
addon.name
}`;
this._analyticsService.trackAction("install-addon", {
clientType: getEnumName(WowClientType, addon.clientType),
provider: addon.providerName,
@@ -486,13 +502,15 @@ export class AddonService {
.filter((f) => !!f);
}
public async removeAddon(addon: Addon, removeDependencies: boolean = false) {
public async removeAddon(addon: Addon, removeDependencies: boolean = false, removeDirectories: boolean = true) {
const installedDirectories = addon.installedFolders?.split(",") ?? [];
const addonFolderPath = this._warcraftService.getAddonFolderPath(addon.clientType);
for (let directory of installedDirectories) {
const addonDirectory = path.join(addonFolderPath, directory);
await this._fileService.remove(addonDirectory);
if (removeDirectories) {
for (let directory of installedDirectories) {
const addonDirectory = path.join(addonFolderPath, directory);
await this._fileService.remove(addonDirectory);
}
}
this._addonStorage.remove(addon);
@@ -647,44 +665,51 @@ export class AddonService {
}
const externalIds: AddonExternalId[] = [];
if (toc.wowInterfaceId) {
externalIds.push({
id: toc.wowInterfaceId,
providerName: ADDON_PROVIDER_WOWINTERFACE,
});
}
if (toc.tukUiProjectId) {
externalIds.push({
id: toc.tukUiProjectId,
providerName: ADDON_PROVIDER_TUKUI,
});
}
if (toc.curseProjectId) {
externalIds.push({
id: toc.curseProjectId,
providerName: ADDON_PROVIDER_CURSEFORGE,
});
}
this.insertExternalId(externalIds, ADDON_PROVIDER_WOWINTERFACE, toc.wowInterfaceId);
this.insertExternalId(externalIds, ADDON_PROVIDER_TUKUI, toc.tukUiProjectId);
this.insertExternalId(externalIds, ADDON_PROVIDER_CURSEFORGE, toc.curseProjectId);
//If the addon does not include the current external id add it
if (!this.containsOwnExternalId(addon, externalIds)) {
externalIds.push({
id: addon.externalId,
providerName: addon.providerName
});
this.insertExternalId(externalIds, addon.providerName, addon.externalId);
}
addon.externalIds = externalIds;
}
public insertExternalId(externalIds: AddonExternalId[], providerName: string, addonId?: string) {
if (!addonId) {
return;
}
const exists =
_.findIndex(externalIds, (extId) => extId.id === addonId && extId.providerName === providerName) !== -1;
if (exists) {
console.debug(`External id exists ${providerName}|${addonId}`);
return;
}
if (this.getProvider(providerName).isValidAddonId(addonId)) {
externalIds.push({
id: addonId,
providerName: providerName,
});
} else {
console.debug(`Invalid provider id ${providerName}|${addonId}`);
}
}
public async setProvider(addon: Addon, externalId: string, providerName: string, clientType: WowClientType) {
const provider = this.getProvider(providerName);
if (!provider) {
throw new Error(`Provider not found: ${providerName}`);
}
if (this.isInstalled(externalId, clientType)) {
throw new Error(ERROR_ADDON_ALREADY_INSTALLED);
}
const externalAddon = await this.getAddon(externalId, providerName, clientType).toPromise();
if (!externalAddon) {
throw new Error(`External addon not found: ${providerName}|${externalId}`);
@@ -693,7 +718,21 @@ export class AddonService {
this.saveAddon(externalAddon);
await this.installAddon(externalAddon.id, undefined, addon);
await this.removeAddon(addon, false);
await this.removeAddon(addon, false, false);
}
public async reconcileOrphanAddons(installedClientTypes: WowClientType[]) {
console.debug("reconcileOrphanAddons", installedClientTypes);
const clientTypes = this._warcraftService.getAllClientTypes();
const unusedClients = _.difference(clientTypes, installedClientTypes);
console.debug("unusedClients", unusedClients);
for (let clientType of unusedClients) {
const addons = this._addonStorage.getAllForClientType(clientType);
for (let addon of addons) {
await this.removeAddon(addon, false, false);
}
}
}
public reconcileExternalIds(newAddon: Addon, oldAddon: Addon) {
@@ -701,14 +740,21 @@ export class AddonService {
return;
}
oldAddon.externalIds.forEach(oldExtId => {
const match = newAddon.externalIds.find(newExtId => newExtId.id === oldExtId.id && newExtId.providerName === oldExtId.providerName);
// Ensure all previously existing external ids are brought along during the swap
// some addons are not always the same between providers ;)
oldAddon.externalIds.forEach((oldExtId) => {
const match = newAddon.externalIds.find(
(newExtId) => newExtId.id === oldExtId.id && newExtId.providerName === oldExtId.providerName
);
if (match) {
return;
}
console.log(`Reconciling external id: ${oldExtId.providerName}|${oldExtId.id}`);
newAddon.externalIds.push({ ...oldExtId });
})
});
// Remove external ids that are not valid that we may have saved previously
_.remove(newAddon.externalIds, (extId) => !this.getProvider(extId.providerName).isValidAddonId(extId.id));
this.saveAddon(newAddon);
}
@@ -730,9 +776,7 @@ export class AddonService {
}
public async backfillAddons() {
const clientTypes = getEnumList<WowClientType>(WowClientType).filter(
(clientType) => clientType !== WowClientType.None
);
const clientTypes = this._warcraftService.getAllClientTypes();
for (let clientType of clientTypes) {
const addons = this._addonStorage.getAllForClientType(clientType);
@@ -761,7 +805,7 @@ export class AddonService {
public containsOwnExternalId(addon: Addon, array?: AddonExternalId[]): boolean {
const arr = array || addon.externalIds;
const result = arr && !!arr.find(ext => ext.id === addon.externalId && ext.providerName === addon.providerName);
const result = arr && !!arr.find((ext) => ext.id === addon.externalId && ext.providerName === addon.providerName);
return result;
}
@@ -819,6 +863,7 @@ export class AddonService {
summary: searchResult.summary,
screenshotUrls: searchResult.screenshotUrls,
dependencies,
externalChannel: getEnumName(AddonChannelType, latestFile.channelType),
};
}

View File

@@ -1,6 +1,7 @@
import { Injectable } from "@angular/core";
import { BehaviorSubject } from "rxjs";
import { filter, first, map } from "rxjs/operators";
import { first as ldFirst } from "lodash";
import { WowClientType } from "../../models/warcraft/wow-client-type";
import { WarcraftService } from "../warcraft/warcraft.service";
import { WowUpService } from "../wowup/wowup.service";
@@ -23,6 +24,22 @@ export class SessionService {
constructor(private _warcraftService: WarcraftService, private _wowUpService: WowUpService) {
this.loadInitialClientType().pipe(first()).subscribe();
this._warcraftService.installedClientTypes$
.pipe(filter((clientTypes) => !!clientTypes))
.subscribe((clientTypes) => this.onInstalledClientsChange(clientTypes));
}
public onInstalledClientsChange(installedClientTypes: WowClientType[]) {
if (!installedClientTypes.length) {
this._selectedClientTypeSrc.next(WowClientType.None);
}
if (installedClientTypes.indexOf(this.selectedClientType) !== -1) {
return;
}
this.selectedClientType = ldFirst(installedClientTypes);
}
public autoUpdateComplete() {

View File

@@ -36,4 +36,8 @@ export class PreferenceStorageService {
public getObject<T>(key: string): T | undefined {
return this._store.get(key, undefined) as T;
}
public remove(key: string): void {
this._store.delete(key);
}
}

View File

@@ -42,6 +42,9 @@ export class WarcraftService {
private readonly _impl: WarcraftServiceImpl;
private readonly _productsSrc = new BehaviorSubject<InstalledProduct[]>([]);
private readonly _installedClientTypesSrc = new BehaviorSubject<WowClientType[] | undefined>(undefined);
private readonly _allClientTypes = getEnumList<WowClientType>(WowClientType).filter(
(clientType) => clientType !== WowClientType.None
);
private _productDbPath = "";
@@ -108,12 +111,14 @@ export class WarcraftService {
return path.join(fullClientPath, INTERFACE_FOLDER_NAME, ADDON_FOLDER_NAME);
}
public getAllClientTypes() {
return [...this._allClientTypes];
}
public async getWowClientTypes() {
const clients: WowClientType[] = [];
const clientTypes = getEnumList<WowClientType>(WowClientType).filter(
(clientType) => clientType !== WowClientType.None
);
const clientTypes = this.getAllClientTypes();
for (let clientType of clientTypes) {
const clientLocation = this.getClientLocation(clientType);
@@ -142,9 +147,7 @@ export class WarcraftService {
const installedProducts = this.decodeProducts(this._productDbPath);
this._productsSrc.next(installedProducts);
const clientTypes = getEnumList<WowClientType>(WowClientType).filter(
(clientType) => clientType !== WowClientType.None
);
const clientTypes = this.getAllClientTypes();
for (const clientType of clientTypes) {
const clientLocation = this.getClientLocation(clientType);
@@ -169,6 +172,8 @@ export class WarcraftService {
this.setClientLocation(clientType, productLocation);
}
this.broadcastInstalledClients().subscribe();
return installedProducts;
}
@@ -235,6 +240,13 @@ export class WarcraftService {
return this._preferenceStorageService.set(clientLocationKey, clientPath);
}
public removeWowFolderPath(clientType: WowClientType) {
const clientLocationKey = this.getClientLocationKey(clientType);
this._preferenceStorageService.remove(clientLocationKey);
return this.broadcastInstalledClients();
}
public setWowFolderPath(clientType: WowClientType, folderPath: string): boolean {
const relativePath = this.getClientRelativePath(clientType, folderPath);
@@ -337,6 +349,12 @@ export class WarcraftService {
}
}
private broadcastInstalledClients() {
return from(this.getWowClientTypes()).pipe(
map((wowClientTypes) => this._installedClientTypesSrc.next(wowClientTypes))
);
}
private decodeProducts(productDbPath: string) {
if (this._electronService.isLinux) {
return [];

View File

@@ -1,5 +1,13 @@
{
"ChangeLogs": [
{
"Version": "2.0.0-beta.19",
"changes": [
"Add the ability to clear an installation path",
"Add the ability to have addons removed for installations that are missing",
"Fix some issues related to switching providers"
]
},
{
"Version": "2.0.0-beta.18",
"changes": [
@@ -9,9 +17,7 @@
},
{
"Version": "2.0.0-beta.17",
"changes": [
"Bug fixes"
]
"changes": ["Bug fixes"]
},
{
"Version": "2.0.0-beta.16",
@@ -33,10 +39,7 @@
},
{
"Version": "2.0.0-beta.15",
"changes": [
"Fix an issue where light/dark themes could overlap",
"Fix an issue with a nested translate call"
]
"changes": ["Fix an issue where light/dark themes could overlap", "Fix an issue with a nested translate call"]
},
{
"Version": "2.0.0-beta.14",

View File

@@ -10,8 +10,8 @@
"THEME": {
"ALLIANCE": "Allianz",
"DEFAULT": "Standard",
"GROUP_DARK": "Dark",
"GROUP_LIGHT": "Light",
"GROUP_DARK": "Dunkel",
"GROUP_LIGHT": "Hell",
"HORDE": "Horde"
},
"WOWUP_UPDATE": {
@@ -68,13 +68,13 @@
"e+0": "{count}",
"e+1": "{count}",
"e+2": "{count}",
"e+3": "{count} thousand",
"e+4": "{count} thousand",
"e+5": "{count} thousand",
"e+6": "{count} million",
"e+7": "{count} million",
"e+8": "{count} million",
"e+9": "{count} billion"
"e+3": "{count} Tausend",
"e+4": "{count} Tausend",
"e+5": "{count} Tausend",
"e+6": "{count} Millionen",
"e+7": "{count} Millionen",
"e+8": "{count} Millionen",
"e+9": "{count} Milliarden"
},
"ENUM": {
"ADDON_CHANNEL_TYPE": {
@@ -84,7 +84,7 @@
}
},
"ERRORS": {
"CHANGE_PROVIDER_ERROR": "Failed to change provider for {addonName} to {providerName}"
"CHANGE_PROVIDER_ERROR": "Anbieterwechsel von {addonName} nach {providerName} fehlgeschlagen"
},
"PROGRESS_SPINNER": {
"LOADING": "Laden..."
@@ -168,15 +168,15 @@
"BETA_ADDON_CHANNEL": "Beta",
"CHANNEL_SUBMENU_TITLE": "Kanal",
"IGNORE_ADDON_BUTTON": "Ignorieren",
"PROVIDER_SUBMENU_TITLE": "Providers",
"PROVIDER_SUBMENU_TITLE": "Anbieter",
"REINSTALL_ADDON_BUTTON": "Neu installieren",
"REMOVE_ADDON_BUTTON": "Entfernen",
"SHOW_FOLDER": "Dateiordner anzeigen",
"STABLE_ADDON_CHANNEL": "Stabil"
},
"CHANGE_ADDON_PROVIDER_CONFIRMATION": {
"MESSAGE": "Do you want to change the addon provider for {addonName} to {providerName}? This operation will uninstall your existing addon and replace it with a copy from the new provider.",
"TITLE": "Change Addon Provider?"
"MESSAGE": "Möchtest du den Addon Anbieter für {addonName} auf {providerName} festlegen? Dies wird das aktuelle Addon deinstallieren und durch eine Version vom neuen Anbieter ersetzen.",
"TITLE": "Addon Anbieter wechsel?"
},
"CHECK_UPDATES_BUTTON": "Updates prüfen",
"CHECK_UPDATES_BUTTON_TOOLTIP": "Nach neuen Addon-Updates suchen",
@@ -185,6 +185,7 @@
"TITLE": "Spalten anzeigen"
},
"FILTER_LABEL": "Filter",
"MULTIPLE_PROVIDERS_TOOLTIP": "This addon has multiple providers",
"PAGE_CONTEXT_FOOTER": {
"ADDONS_INSTALLED": "{count} {count, plural, =1{Addon} other{Addons}}",
"JOIN_DISCORD": "Schreibe mit uns auf Discord",
@@ -276,6 +277,10 @@
"WOW": {
"AUTO_UPDATE_DESCRIPTION": "Neu installierte Addons werden standardmäßig auf Auto-Update gesetzt",
"AUTO_UPDATE_LABEL": "Automatisch aktualisieren",
"CLEAR_INSTALL_LOCATION_DIALOG": {
"MESSAGE": "Are you sure you want to clear the install path for {clientName}? This will remove all stored addon information for this client.\n\nYour addon folders will not be removed.",
"TITLE": "Clear Install Location?"
},
"CLIENT_TYPE_INPUT_HINT": "Der Order, der den {clientTypeName} Clientorder \"{clientFolderName}\" enthält",
"CLIENT_TYPE_PATH_LABEL": "{clientTypeName} Pfad",
"DEFAULT_ADDON_CHANNEL_LABEL": "Standard-Addon-Kanal",

View File

@@ -185,6 +185,7 @@
"TITLE": "Show Columns"
},
"FILTER_LABEL": "Filter",
"MULTIPLE_PROVIDERS_TOOLTIP": "This addon has multiple providers",
"PAGE_CONTEXT_FOOTER": {
"ADDONS_INSTALLED": "{count} {count, plural, =1{addon} other{addons}}",
"JOIN_DISCORD": "Chat with us on Discord",
@@ -276,6 +277,10 @@
"WOW": {
"AUTO_UPDATE_DESCRIPTION": "Newly installed addons will be set to auto update by default",
"AUTO_UPDATE_LABEL": "Auto Update",
"CLEAR_INSTALL_LOCATION_DIALOG": {
"MESSAGE": "Are you sure you want to clear the install path for {clientName}? This will remove all stored addon information for this client.\n\nYour addon folders will not be removed.",
"TITLE": "Clear Install Location?"
},
"CLIENT_TYPE_INPUT_HINT": "The folder that contains the {clientTypeName} client folder \"{clientFolderName}\"",
"CLIENT_TYPE_PATH_LABEL": "{clientTypeName} path",
"DEFAULT_ADDON_CHANNEL_LABEL": "Default Addon Channel",

View File

@@ -185,6 +185,7 @@
"TITLE": "Mostrar Columna"
},
"FILTER_LABEL": "Filtro",
"MULTIPLE_PROVIDERS_TOOLTIP": "This addon has multiple providers",
"PAGE_CONTEXT_FOOTER": {
"ADDONS_INSTALLED": "{count} {count, plural, =1{addon} other{addons}}",
"JOIN_DISCORD": "Charle con nosotros en Discord",
@@ -276,6 +277,10 @@
"WOW": {
"AUTO_UPDATE_DESCRIPTION": "Los addons instalados después de activar esta opción se configurarán para actualizarse automáticamente de forma predeterminada",
"AUTO_UPDATE_LABEL": "Actualización automática",
"CLEAR_INSTALL_LOCATION_DIALOG": {
"MESSAGE": "Are you sure you want to clear the install path for {clientName}? This will remove all stored addon information for this client.\n\nYour addon folders will not be removed.",
"TITLE": "Clear Install Location?"
},
"CLIENT_TYPE_INPUT_HINT": "La carpeta que contiene la versión {clientTypeName} del cliente de World of Warcraft \"{clientFolderName}\"",
"CLIENT_TYPE_PATH_LABEL": "Ruta de {clientTypeName}",
"DEFAULT_ADDON_CHANNEL_LABEL": "Canal de Addons Predeterminado",

View File

@@ -10,8 +10,8 @@
"THEME": {
"ALLIANCE": "Alliance",
"DEFAULT": "Défaut",
"GROUP_DARK": "Dark",
"GROUP_LIGHT": "Light",
"GROUP_DARK": "Sombre",
"GROUP_LIGHT": "Clair",
"HORDE": "Horde"
},
"WOWUP_UPDATE": {
@@ -48,9 +48,9 @@
"CLIENT_TYPES": {
"BETA": "Beta",
"CLASSIC": "Classic",
"CLASSICPTR": "Classic PTR",
"CLASSICPTR": "PTR Classic",
"RETAIL": "Retail",
"RETAILPTR": "Retail PTR"
"RETAILPTR": "PTR Retail"
},
"DATES": {
"DATETIME_SHORT": "{d, date, short} {d, time, short}",
@@ -68,13 +68,13 @@
"e+0": "{count}",
"e+1": "{count}",
"e+2": "{count}",
"e+3": "{count} thousand",
"e+4": "{count} thousand",
"e+5": "{count} thousand",
"e+6": "{count} million",
"e+7": "{count} million",
"e+8": "{count} million",
"e+9": "{count} billion"
"e+3": "{count} {count, plural, one{millier} other{{count} milliers}}",
"e+4": "{count} milliers",
"e+5": "{count} milliers",
"e+6": "{count} {count, plural, one{million} other{{count} millions}} ",
"e+7": "{count} millions",
"e+8": "{count} millions",
"e+9": "{count} {count, plural, one{milliard} other{{count} milliards}} "
},
"ENUM": {
"ADDON_CHANNEL_TYPE": {
@@ -84,7 +84,7 @@
}
},
"ERRORS": {
"CHANGE_PROVIDER_ERROR": "Failed to change provider for {addonName} to {providerName}"
"CHANGE_PROVIDER_ERROR": "Impossible de changer le fournisseur {providerName} pour l´addon {addonName}"
},
"PROGRESS_SPINNER": {
"LOADING": "Chargement..."
@@ -101,7 +101,7 @@
"VIEW_ON_PROVIDER_PREFIX": "Voir sur"
},
"ALERT": {
"ERROR_TITLE": "Error",
"ERROR_TITLE": "Erreur",
"POSITIVE_BUTTON": "Ok"
},
"CONFIRM": {
@@ -168,15 +168,15 @@
"BETA_ADDON_CHANNEL": "Bêta",
"CHANNEL_SUBMENU_TITLE": "Canal",
"IGNORE_ADDON_BUTTON": "Ignorer",
"PROVIDER_SUBMENU_TITLE": "Providers",
"PROVIDER_SUBMENU_TITLE": "Fournisseurs",
"REINSTALL_ADDON_BUTTON": "Réinstaller",
"REMOVE_ADDON_BUTTON": "Désinstaller",
"SHOW_FOLDER": "Montrer le dossier",
"STABLE_ADDON_CHANNEL": "Stable"
},
"CHANGE_ADDON_PROVIDER_CONFIRMATION": {
"MESSAGE": "Do you want to change the addon provider for {addonName} to {providerName}? This operation will uninstall your existing addon and replace it with a copy from the new provider.",
"TITLE": "Change Addon Provider?"
"MESSAGE": "Souhaitez-vous changer le fournisseur d´addons pour {addonName} par {providerName}? Cette opération désinstallera l´addon existant et le remplacera par une copie du nouveau fournisseur.",
"TITLE": "Changer de fournisseur d´addon"
},
"CHECK_UPDATES_BUTTON": "Vérifier les mises à jour",
"CHECK_UPDATES_BUTTON_TOOLTIP": "Vérifier les dernières mises à jour des modules complémentaires",
@@ -185,6 +185,7 @@
"TITLE": "Afficher les colonnes"
},
"FILTER_LABEL": "Filter",
"MULTIPLE_PROVIDERS_TOOLTIP": "This addon has multiple providers",
"PAGE_CONTEXT_FOOTER": {
"ADDONS_INSTALLED": "{count} {count, plural, =1{addon} other{addons}}",
"JOIN_DISCORD": "Discutez avec nous sur Discord",
@@ -276,6 +277,10 @@
"WOW": {
"AUTO_UPDATE_DESCRIPTION": "Les addons nouvellement installés seront mises à jour automatiquement par défaut",
"AUTO_UPDATE_LABEL": "Mise à jour automatique",
"CLEAR_INSTALL_LOCATION_DIALOG": {
"MESSAGE": "Are you sure you want to clear the install path for {clientName}? This will remove all stored addon information for this client.\n\nYour addon folders will not be removed.",
"TITLE": "Clear Install Location?"
},
"CLIENT_TYPE_INPUT_HINT": "Ce dossier contient le client {clientTypeName} avec le dossier \"{clientFolderName}\"",
"CLIENT_TYPE_PATH_LABEL": "Chemin de {clientTypeName}",
"DEFAULT_ADDON_CHANNEL_LABEL": "Canal d'extension par défaut",

View File

@@ -1,7 +1,7 @@
{
"APP": {
"AUTO_UPDATE_NOTIFICATION_BODY": "Automaticamente {count, plural, =1{aggiornato} other{aggiornati}} {count} {count, plural, =1{addon} other{addons}}.",
"AUTO_UPDATE_NOTIFICATION_TITLE": "Aggiornamenti automatici",
"AUTO_UPDATE_NOTIFICATION_TITLE": "Aggiornamenti Automatici",
"SYSTEM_TRAY": {
"CHECK_UPDATE": "Controlla Aggiornamenti...",
"QUIT_ACTION": "Chiudi",
@@ -10,21 +10,21 @@
"THEME": {
"ALLIANCE": "Alleanza",
"DEFAULT": "Default",
"GROUP_DARK": "Dark",
"GROUP_LIGHT": "Light",
"GROUP_DARK": "Scuro",
"GROUP_LIGHT": "Chiaro",
"HORDE": "Orda"
},
"WOWUP_UPDATE": {
"DOWNLOADED_TOOLTIP": "Installa l'aggiornamento di WowUp",
"INSTALL_MESSAGE": "Vuoi riavviare WowUp per installare l'aggiornamento?",
"INSTALL_TITLE": "Aggiornamento di Wowup pronto",
"NOT_AVAILABLE": "L'ultima versione di Wowup è già installata",
"NOT_AVAILABLE": "È già installata l'ultima versione di Wowup",
"PORTABLE_DOWNLOAD_MESSAGE": "Vuoi scaricare manualmente l'ultima versione portatile?\n\nDovrai chiudere l'applicazione manualmente e sovrascrivere la nuova versione.",
"PORTABLE_DOWNLOAD_TITLE": "Richiesto Download Manuale",
"SNACKBAR_ACTION": "Aggiorna",
"SNACKBAR_TEXT": "È disponibile una nuova versione di WowUp",
"TOOLTIP": "L'aggiornamento di WowUp è pronto per l'installazione",
"UPDATE_ERROR": "Errore nell'ottenere l'ultima versione di Wowup"
"TOOLTIP": "Aggiornamento di WowUp disponibile",
"UPDATE_ERROR": "Fallito nell'ottenere l'ultima versione di Wowup"
}
},
"COMMON": {
@@ -57,8 +57,8 @@
"DAYS_AGO": "{count} {count, plural, one{giorno} other{giorni}} fa",
"HOURS_AGO": "{count} {count, plural, one{ora} other{ore}} fa",
"JUST_NOW": "Ora",
"MONTHS_AGO": "{count} {count, plural, one{month} other{months}} ago",
"YEARS_AGO": "{count} {count, plural, one{year} other{years}} ago",
"MONTHS_AGO": "{count} {count, plural, one{mese} other{mesi}} fa",
"YEARS_AGO": "{count} {count, plural, one{mese} other{mesi}} fa",
"YESTERDAY": "Ieri"
},
"DEPENDENCY": {
@@ -68,13 +68,13 @@
"e+0": "{count}",
"e+1": "{count}",
"e+2": "{count}",
"e+3": "{count} thousand",
"e+4": "{count} thousand",
"e+5": "{count} thousand",
"e+6": "{count} million",
"e+7": "{count} million",
"e+8": "{count} million",
"e+9": "{count} billion"
"e+3": "{count} {count, plural, one{mille} other{{count} mila}}",
"e+4": "{count} {count, plural, one{mille} other{{count} mila}}",
"e+5": "{count} {count, plural, one{mille} other{{count} mila}}",
"e+6": "{count} {count, plural, one{milione} other{milioni}}",
"e+7": "{count} {count, plural, one{milione} other{milioni}}",
"e+8": "{count} {count, plural, one{milione} other{milioni}}",
"e+9": "{count} {count, plural, one{bilione} other{bilioni}}"
},
"ENUM": {
"ADDON_CHANNEL_TYPE": {
@@ -84,7 +84,7 @@
}
},
"ERRORS": {
"CHANGE_PROVIDER_ERROR": "Failed to change provider for {addonName} to {providerName}"
"CHANGE_PROVIDER_ERROR": "Fallito nel cambiare provider per {addonName} con {providerName}"
},
"PROGRESS_SPINNER": {
"LOADING": "Caricamento in corso..."
@@ -101,7 +101,7 @@
"VIEW_ON_PROVIDER_PREFIX": "Visualizza su"
},
"ALERT": {
"ERROR_TITLE": "Error",
"ERROR_TITLE": "Errore",
"POSITIVE_BUTTON": "Ok"
},
"CONFIRM": {
@@ -113,7 +113,7 @@
"ADDON_URL_INPUT_PLACEHOLDER": "URL di esempio GitHub o WowInterface",
"CLOSE_BUTTON": "Chiudi",
"DESCRIPTION": "Se si desidera installare un addon direttamente da un URL incollarlo qui sotto per iniziare.",
"DOWNLOAD_COUNT": "{textCount} {count, plural, one{download} other{downloads}} on {provider}",
"DOWNLOAD_COUNT": "{textCount} {count, plural, one{download} other{downloads}} su {provider}",
"ERROR": {
"FAILED_TO_CONNECT": "Impossibile connettersi all'API, per favore riprovare più tardi.",
"INSTALL_FAILED": "Qualcosa è andato storto nell'installazione dell'addon, per favore riprovare.\n\nSe questo messaggio continua ad apparire puoi chiederci aiuto su Discord, in #wow-support channel.",
@@ -175,8 +175,8 @@
"STABLE_ADDON_CHANNEL": "Stabile"
},
"CHANGE_ADDON_PROVIDER_CONFIRMATION": {
"MESSAGE": "Do you want to change the addon provider for {addonName} to {providerName}? This operation will uninstall your existing addon and replace it with a copy from the new provider.",
"TITLE": "Change Addon Provider?"
"MESSAGE": "Vuoi cambiare il provider dell'addon {addonName} con {providerName}? Questa operazione disinstallerà il tuo addon esistente e lo sostituirà con una copia dal nuovo provider.",
"TITLE": "Cambiare Provider dell'Addon??"
},
"CHECK_UPDATES_BUTTON": "Controlla Aggiornamenti",
"CHECK_UPDATES_BUTTON_TOOLTIP": "Controlla gli ultimi aggiornamenti degli addons",
@@ -185,6 +185,7 @@
"TITLE": "Mostra Colonne"
},
"FILTER_LABEL": "Filtra",
"MULTIPLE_PROVIDERS_TOOLTIP": "This addon has multiple providers",
"PAGE_CONTEXT_FOOTER": {
"ADDONS_INSTALLED": "{count} {count, plural, =1{addon} other{addons}}",
"JOIN_DISCORD": "Contattaci su Discord",
@@ -206,7 +207,7 @@
"ADDON_COLUMN_HEADER": "Addon",
"ADDON_INSTALL_BUTTON": "Installa",
"ADDON_UPDATE_BUTTON": "Aggiorna",
"AUTHOR_COLUMN_HEADER": "Autore",
"AUTHOR_COLUMN_HEADER": "Autore/i",
"AUTO_UPDATE_ICON_TOOLTIP": "Aggiornamento automatico abilitato",
"GAME_VERSION_COLUMN_HEADER": "Versione Del Gioco",
"LATEST_VERSION_COLUMN_HEADER": "Ultima Versione",
@@ -276,6 +277,10 @@
"WOW": {
"AUTO_UPDATE_DESCRIPTION": "I nuovi addons installati saranno impostati di default per l'aggiornamento automatico ",
"AUTO_UPDATE_LABEL": "Aggiornamento Automatico",
"CLEAR_INSTALL_LOCATION_DIALOG": {
"MESSAGE": "Are you sure you want to clear the install path for {clientName}? This will remove all stored addon information for this client.\n\nYour addon folders will not be removed.",
"TITLE": "Clear Install Location?"
},
"CLIENT_TYPE_INPUT_HINT": "La cartella che contiene {clientTypeName}: \"{clientFolderName}\"",
"CLIENT_TYPE_PATH_LABEL": "{clientTypeName} path",
"DEFAULT_ADDON_CHANNEL_LABEL": "Canale Addon Predefinito",

View File

@@ -185,6 +185,7 @@
"TITLE": "컬럼 보기"
},
"FILTER_LABEL": "필터",
"MULTIPLE_PROVIDERS_TOOLTIP": "This addon has multiple providers",
"PAGE_CONTEXT_FOOTER": {
"ADDONS_INSTALLED": "{count}개의 애드온",
"JOIN_DISCORD": "디스코드 입장",
@@ -276,6 +277,10 @@
"WOW": {
"AUTO_UPDATE_DESCRIPTION": "새로 설치되는 애드온의 자동업데이트 옵션을 활성화합니다.",
"AUTO_UPDATE_LABEL": "자동 업데이트",
"CLEAR_INSTALL_LOCATION_DIALOG": {
"MESSAGE": "Are you sure you want to clear the install path for {clientName}? This will remove all stored addon information for this client.\n\nYour addon folders will not be removed.",
"TITLE": "Clear Install Location?"
},
"CLIENT_TYPE_INPUT_HINT": "\"{clientFolderName}\" 디렉토리를 포함하는 위치",
"CLIENT_TYPE_PATH_LABEL": "{clientTypeName} 설치 위치",
"DEFAULT_ADDON_CHANNEL_LABEL": "기본 애드온 채널",

View File

@@ -185,6 +185,7 @@
"TITLE": "Vis Kolonner"
},
"FILTER_LABEL": "Filter",
"MULTIPLE_PROVIDERS_TOOLTIP": "This addon has multiple providers",
"PAGE_CONTEXT_FOOTER": {
"ADDONS_INSTALLED": "{count} {count, plural, =1{utvidelse} other{utvidelser}}",
"JOIN_DISCORD": "Chat med oss på Discord",
@@ -276,6 +277,10 @@
"WOW": {
"AUTO_UPDATE_DESCRIPTION": "Nye utvidelser du installerer vil bli satt til å oppdateres automatisk",
"AUTO_UPDATE_LABEL": "Automatisk Oppdatering",
"CLEAR_INSTALL_LOCATION_DIALOG": {
"MESSAGE": "Are you sure you want to clear the install path for {clientName}? This will remove all stored addon information for this client.\n\nYour addon folders will not be removed.",
"TITLE": "Clear Install Location?"
},
"CLIENT_TYPE_INPUT_HINT": "Mappen som inneholder {clientTypeName} klientmappe \"{clientFolderName}\"",
"CLIENT_TYPE_PATH_LABEL": "{clientTypeName} filbane",
"DEFAULT_ADDON_CHANNEL_LABEL": "Standard kanal for utvidelser",

View File

@@ -185,6 +185,7 @@
"TITLE": "Exibir Colunas"
},
"FILTER_LABEL": "Filtrar",
"MULTIPLE_PROVIDERS_TOOLTIP": "This addon has multiple providers",
"PAGE_CONTEXT_FOOTER": {
"ADDONS_INSTALLED": "{count} {count, plural, =1{Addon} other{Addons}}",
"JOIN_DISCORD": "Converse conosco no Discord",
@@ -276,6 +277,10 @@
"WOW": {
"AUTO_UPDATE_DESCRIPTION": "Addons recém-instalados serão definidos para atualizar automáticamente por padrão",
"AUTO_UPDATE_LABEL": "Atualização Automática",
"CLEAR_INSTALL_LOCATION_DIALOG": {
"MESSAGE": "Are you sure you want to clear the install path for {clientName}? This will remove all stored addon information for this client.\n\nYour addon folders will not be removed.",
"TITLE": "Clear Install Location?"
},
"CLIENT_TYPE_INPUT_HINT": "A pasta \"{clientFolderName}\" que contém o {clientTypeName}",
"CLIENT_TYPE_PATH_LABEL": "Pasta {clientTypeName}",
"DEFAULT_ADDON_CHANNEL_LABEL": "Canal de Addon Padrão",

View File

@@ -168,7 +168,7 @@
"BETA_ADDON_CHANNEL": "Бета",
"CHANNEL_SUBMENU_TITLE": "Тип выпуска",
"IGNORE_ADDON_BUTTON": "Пропускать",
"PROVIDER_SUBMENU_TITLE": "Providers",
"PROVIDER_SUBMENU_TITLE": "Источники",
"REINSTALL_ADDON_BUTTON": "Переустановить",
"REMOVE_ADDON_BUTTON": "Удалить",
"SHOW_FOLDER": "Показать папку",
@@ -185,6 +185,7 @@
"TITLE": "Показать колонки"
},
"FILTER_LABEL": "Фильтр",
"MULTIPLE_PROVIDERS_TOOLTIP": "This addon has multiple providers",
"PAGE_CONTEXT_FOOTER": {
"ADDONS_INSTALLED": "{count} {count, plural, one{модификация} few{модификации} other{модификаций}}",
"JOIN_DISCORD": "Общайтесь с нами в Discord",
@@ -276,6 +277,10 @@
"WOW": {
"AUTO_UPDATE_DESCRIPTION": "Новые установленные модификации будут автоматически обновляться по умолчанию",
"AUTO_UPDATE_LABEL": "Автообновление",
"CLEAR_INSTALL_LOCATION_DIALOG": {
"MESSAGE": "Are you sure you want to clear the install path for {clientName}? This will remove all stored addon information for this client.\n\nYour addon folders will not be removed.",
"TITLE": "Clear Install Location?"
},
"CLIENT_TYPE_INPUT_HINT": "Папка которая содержит папку клиента {clientTypeName} \"{clientFolderName}\"",
"CLIENT_TYPE_PATH_LABEL": "Путь для {clientTypeName}",
"DEFAULT_ADDON_CHANNEL_LABEL": "Тип выпуска модификации по умолчанию",

View File

@@ -185,6 +185,7 @@
"TITLE": "顯示列表項"
},
"FILTER_LABEL": "篩選",
"MULTIPLE_PROVIDERS_TOOLTIP": "This addon has multiple providers",
"PAGE_CONTEXT_FOOTER": {
"ADDONS_INSTALLED": "共 {count} 個插件",
"JOIN_DISCORD": "在 Discord 上與我們交流(英語)",
@@ -276,6 +277,10 @@
"WOW": {
"AUTO_UPDATE_DESCRIPTION": "新安裝的插件將預設設定為自動更新",
"AUTO_UPDATE_LABEL": "自動更新",
"CLEAR_INSTALL_LOCATION_DIALOG": {
"MESSAGE": "Are you sure you want to clear the install path for {clientName}? This will remove all stored addon information for this client.\n\nYour addon folders will not be removed.",
"TITLE": "Clear Install Location?"
},
"CLIENT_TYPE_INPUT_HINT": "請選擇 {clientTypeName} 客戶端路徑(\"{clientFolderName}\" 的上級路徑)",
"CLIENT_TYPE_PATH_LABEL": "{clientTypeName} 路徑",
"DEFAULT_ADDON_CHANNEL_LABEL": "預設插件更新通道",

View File

@@ -185,6 +185,7 @@
"TITLE": "显示列表项"
},
"FILTER_LABEL": "筛选",
"MULTIPLE_PROVIDERS_TOOLTIP": "This addon has multiple providers",
"PAGE_CONTEXT_FOOTER": {
"ADDONS_INSTALLED": "共 {count} 个插件",
"JOIN_DISCORD": "在 Discord 上与我们交流(英语)",
@@ -276,6 +277,10 @@
"WOW": {
"AUTO_UPDATE_DESCRIPTION": "新安装的插件将默认设置为自动更新",
"AUTO_UPDATE_LABEL": "自动更新",
"CLEAR_INSTALL_LOCATION_DIALOG": {
"MESSAGE": "Are you sure you want to clear the install path for {clientName}? This will remove all stored addon information for this client.\n\nYour addon folders will not be removed.",
"TITLE": "Clear Install Location?"
},
"CLIENT_TYPE_INPUT_HINT": "请选择 {clientTypeName} 客户端路径(\"{clientFolderName}\" 的上级路径)",
"CLIENT_TYPE_PATH_LABEL": "{clientTypeName} 路径",
"DEFAULT_ADDON_CHANNEL_LABEL": "默认插件更新通道",

View File

@@ -61,3 +61,6 @@ export const ALLIANCE_THEME = "alliance-theme";
export const ALLIANCE_LIGHT_THEME = "alliance-theme-light-theme";
export const DEFAULT_BG_COLOR = "#444444";
export const DEFAULT_LIGHT_BG_COLOR = "#ebedef";
// ERRORS
export const ERROR_ADDON_ALREADY_INSTALLED = "ERROR_ADDON_ALREADY_INSTALLED"