mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-24 08:30:27 +00:00
Various fixes
This commit is contained in:
parent
7bce1e8f3b
commit
d9e23b9ebf
30 changed files with 246 additions and 45 deletions
|
@ -16,14 +16,14 @@ public class BrowserAlerts {
|
|||
|
||||
public static FileConflictChoice showFileConflictAlert(String file, boolean multiple) {
|
||||
var map = new LinkedHashMap<ButtonType, FileConflictChoice>();
|
||||
map.put(new ButtonType("Cancel", ButtonBar.ButtonData.CANCEL_CLOSE), FileConflictChoice.CANCEL);
|
||||
map.put(new ButtonType(AppI18n.get("cancel"), ButtonBar.ButtonData.CANCEL_CLOSE), FileConflictChoice.CANCEL);
|
||||
if (multiple) {
|
||||
map.put(new ButtonType("Skip", ButtonBar.ButtonData.OTHER), FileConflictChoice.SKIP);
|
||||
map.put(new ButtonType("Skip All", ButtonBar.ButtonData.OTHER), FileConflictChoice.SKIP_ALL);
|
||||
map.put(new ButtonType(AppI18n.get("skip"), ButtonBar.ButtonData.OTHER), FileConflictChoice.SKIP);
|
||||
map.put(new ButtonType(AppI18n.get("skipAll"), ButtonBar.ButtonData.OTHER), FileConflictChoice.SKIP_ALL);
|
||||
}
|
||||
map.put(new ButtonType("Replace", ButtonBar.ButtonData.OTHER), FileConflictChoice.REPLACE);
|
||||
map.put(new ButtonType(AppI18n.get("replace"), ButtonBar.ButtonData.OTHER), FileConflictChoice.REPLACE);
|
||||
if (multiple) {
|
||||
map.put(new ButtonType("Replace All", ButtonBar.ButtonData.OTHER), FileConflictChoice.REPLACE_ALL);
|
||||
map.put(new ButtonType(AppI18n.get("replaceAll"), ButtonBar.ButtonData.OTHER), FileConflictChoice.REPLACE_ALL);
|
||||
}
|
||||
return AppWindowHelper.showBlockingAlert(alert -> {
|
||||
alert.setTitle(AppI18n.get("fileConflictAlertTitle"));
|
||||
|
|
|
@ -212,8 +212,8 @@ public class StoreCreationComp extends DialogComp {
|
|||
.setContent(AppWindowHelper.alertContentText(AppI18n.get("confirmInvalidStoreContent")));
|
||||
alert.setAlertType(Alert.AlertType.CONFIRMATION);
|
||||
alert.getButtonTypes().clear();
|
||||
alert.getButtonTypes().add(new ButtonType("Retry", ButtonBar.ButtonData.CANCEL_CLOSE));
|
||||
alert.getButtonTypes().add(new ButtonType("Skip", ButtonBar.ButtonData.OK_DONE));
|
||||
alert.getButtonTypes().add(new ButtonType(AppI18n.get("retry"), ButtonBar.ButtonData.CANCEL_CLOSE));
|
||||
alert.getButtonTypes().add(new ButtonType(AppI18n.get("skip"), ButtonBar.ButtonData.OK_DONE));
|
||||
})
|
||||
.map(b -> b.getButtonData().isDefaultButton())
|
||||
.orElse(false);
|
||||
|
|
|
@ -65,7 +65,7 @@ public class App extends Application {
|
|||
"XPipe %s (%s)", t.getValue(), AppProperties.get().getVersion());
|
||||
var prefix = AppProperties.get().isStaging() ? "[Public Test Build, Not a proper release] " : "";
|
||||
var suffix = u.getValue() != null
|
||||
? AppI18n.get("updateReadyTitle", u.getValue().getVersion())
|
||||
? " " + AppI18n.get("updateReadyTitle", u.getValue().getVersion())
|
||||
: "";
|
||||
return prefix + base + suffix;
|
||||
},
|
||||
|
|
|
@ -84,7 +84,7 @@ public class ContextualFileReferenceChoiceComp extends Comp<CompStructure<HBox>>
|
|||
var gitShareButton = new ButtonComp(null, new FontIcon("mdi2g-git"), () -> {
|
||||
if (!AppPrefs.get().enableGitStorage().get()) {
|
||||
AppLayoutModel.get().selectSettings();
|
||||
AppPrefs.get().selectCategory(3);
|
||||
AppPrefs.get().selectCategory("synchronization");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -139,15 +139,15 @@ public class AppPrefs {
|
|||
new AboutCategory(),
|
||||
new SystemCategory(),
|
||||
new AppearanceCategory(),
|
||||
new SyncCategory(),
|
||||
new VaultCategory(),
|
||||
new PasswordManagerCategory(),
|
||||
new SecurityCategory(),
|
||||
new TerminalCategory(),
|
||||
new EditorCategory(),
|
||||
new RdpCategory(),
|
||||
new SyncCategory(),
|
||||
new VaultCategory(),
|
||||
new SshCategory(),
|
||||
new LocalShellCategory(),
|
||||
new SecurityCategory(),
|
||||
new PasswordManagerCategory(),
|
||||
new TroubleshootCategory(),
|
||||
new DeveloperCategory());
|
||||
var selected = AppCache.get("selectedPrefsCategory", Integer.class, () -> 0);
|
||||
|
@ -505,10 +505,12 @@ public class AppPrefs {
|
|||
}
|
||||
}
|
||||
|
||||
public void selectCategory(int selected) {
|
||||
public void selectCategory(String id) {
|
||||
AppLayoutModel.get().selectSettings();
|
||||
var index = selected >= 0 && selected < categories.size() ? selected : 0;
|
||||
selectedCategory.setValue(categories.get(index));
|
||||
var found = categories.stream().filter(appPrefsCategory -> appPrefsCategory.getId().equals(id)).findFirst();
|
||||
found.ifPresent(appPrefsCategory -> {
|
||||
selectedCategory.setValue(appPrefsCategory);
|
||||
});
|
||||
}
|
||||
|
||||
public String passwordManagerString(String key) {
|
||||
|
|
|
@ -241,7 +241,7 @@ public interface ExternalEditorType extends PrefsChoiceValue {
|
|||
|
||||
public WindowsType(String id, String executable, boolean detach) {
|
||||
super(id, executable);
|
||||
this.detach = true;
|
||||
this.detach = detach;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
package io.xpipe.app.prefs;
|
||||
|
||||
import io.xpipe.app.ext.PrefsChoiceValue;
|
||||
import io.xpipe.core.process.OsType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public interface ExternalPasswordManager extends PrefsChoiceValue {
|
||||
|
||||
String getTemplate();
|
||||
|
||||
static ExternalPasswordManager BITWARDEN = new ExternalPasswordManager() {
|
||||
@Override
|
||||
public String getTemplate() {
|
||||
return "bw get password $KEY --nointeraction --raw";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "bitwarden";
|
||||
}
|
||||
};
|
||||
|
||||
static ExternalPasswordManager ONEPASSWORD = new ExternalPasswordManager() {
|
||||
@Override
|
||||
public String getTemplate() {
|
||||
return "op read $KEY --force";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "1Password";
|
||||
}
|
||||
};
|
||||
|
||||
static ExternalPasswordManager DASHLANE = new ExternalPasswordManager() {
|
||||
@Override
|
||||
public String getTemplate() {
|
||||
return "dcli password --output console $KEY";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "Dashlane";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static ExternalPasswordManager LASTPASS = new ExternalPasswordManager() {
|
||||
@Override
|
||||
public String getTemplate() {
|
||||
return "lpass show --password $KEY";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "LastPass";
|
||||
}
|
||||
};
|
||||
|
||||
static ExternalPasswordManager MACOS_KEYCHAIN = new ExternalPasswordManager() {
|
||||
@Override
|
||||
public String getTemplate() {
|
||||
return "security find-generic-password -w -l $KEY";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "macOS keychain";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSelectable() {
|
||||
return OsType.getLocal() == OsType.MACOS;
|
||||
}
|
||||
};
|
||||
|
||||
static List<ExternalPasswordManager> ALL = Stream.of(ONEPASSWORD, BITWARDEN, DASHLANE, LASTPASS, MACOS_KEYCHAIN).filter(externalPasswordManager -> externalPasswordManager.isSelectable()).toList();
|
||||
}
|
|
@ -2,6 +2,7 @@ package io.xpipe.app.prefs;
|
|||
|
||||
import atlantafx.base.theme.Styles;
|
||||
import io.xpipe.app.comp.base.ButtonComp;
|
||||
import io.xpipe.app.core.AppI18n;
|
||||
import io.xpipe.app.fxcomps.Comp;
|
||||
import io.xpipe.app.fxcomps.impl.HorizontalComp;
|
||||
import io.xpipe.app.fxcomps.impl.TextFieldComp;
|
||||
|
@ -14,6 +15,8 @@ import io.xpipe.core.store.LocalStore;
|
|||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.MenuButton;
|
||||
import javafx.scene.control.MenuItem;
|
||||
import org.kordamp.ikonli.javafx.FontIcon;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -25,6 +28,22 @@ public class PasswordManagerCategory extends AppPrefsCategory {
|
|||
return "passwordManager";
|
||||
}
|
||||
|
||||
private Comp<?> createTemplateChoice() {
|
||||
return Comp.of(() -> {
|
||||
var cb = new MenuButton();
|
||||
cb.textProperty().bind(AppI18n.observable("templates"));
|
||||
ExternalPasswordManager.ALL.forEach(externalPasswordManager -> {
|
||||
var m = new MenuItem(externalPasswordManager.toTranslatedString().getValue());
|
||||
m.setOnAction(event -> {
|
||||
AppPrefs.get().passwordManagerCommand.set(externalPasswordManager.getTemplate());
|
||||
event.consume();
|
||||
});
|
||||
cb.getItems().add(m);
|
||||
});
|
||||
return cb;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Comp<?> create() {
|
||||
var prefs = AppPrefs.get();
|
||||
|
@ -48,6 +67,13 @@ public class PasswordManagerCategory extends AppPrefsCategory {
|
|||
});
|
||||
};
|
||||
|
||||
var c = new TextFieldComp(prefs.passwordManagerCommand, true).apply(struc -> struc.get().setPromptText("mypassmgr get $KEY")).minWidth(350);
|
||||
var visit = createTemplateChoice();
|
||||
var choice = new HorizontalComp(List.of(c, visit)).apply(struc -> {
|
||||
struc.get().setAlignment(Pos.CENTER_LEFT);
|
||||
struc.get().setSpacing(10);
|
||||
});
|
||||
|
||||
var testPasswordManager = new HorizontalComp(List.of(
|
||||
new TextFieldComp(testPasswordManagerValue)
|
||||
.apply(struc -> struc.get().setPromptText("Enter password key"))
|
||||
|
@ -63,8 +89,7 @@ public class PasswordManagerCategory extends AppPrefsCategory {
|
|||
.addTitle("passwordManager")
|
||||
.sub(new OptionsBuilder()
|
||||
.nameAndDescription("passwordManagerCommand")
|
||||
.addComp(new TextFieldComp(prefs.passwordManagerCommand, true)
|
||||
.apply(struc -> struc.get().setPromptText("mypassmgr get $KEY")))
|
||||
.addComp(choice)
|
||||
.nameAndDescription("passwordManagerCommandTest")
|
||||
.addComp(testPasswordManager))
|
||||
.buildComp();
|
||||
|
|
|
@ -13,6 +13,17 @@ import java.util.function.IntFunction;
|
|||
|
||||
public class DataStoreFormatter {
|
||||
|
||||
public static String formattedOsName(String osName) {
|
||||
osName = osName.replaceAll("^Microsoft ", "");
|
||||
|
||||
var proRequired = !LicenseProvider.get().checkOsName(osName);
|
||||
if (!proRequired) {
|
||||
return osName;
|
||||
}
|
||||
|
||||
return "[Pro] " + osName;
|
||||
}
|
||||
|
||||
public static ObservableValue<String> shellInformation(StoreEntryWrapper w) {
|
||||
return BindingsHelper.map(w.getPersistentState(), o -> {
|
||||
if (o instanceof ShellStoreState s) {
|
||||
|
@ -23,11 +34,11 @@ public class DataStoreFormatter {
|
|||
if (s.getShellDialect() != null
|
||||
&& !s.getShellDialect().getDumbMode().supportsAnyPossibleInteraction()) {
|
||||
return s.getOsName() != null
|
||||
? s.getOsName()
|
||||
? formattedOsName(s.getOsName())
|
||||
: s.getShellDialect().getDisplayName();
|
||||
}
|
||||
|
||||
return s.isRunning() ? s.getOsName() : "Connection failed";
|
||||
return s.isRunning() ? formattedOsName(s.getOsName()) : "Connection failed";
|
||||
}
|
||||
|
||||
return "?";
|
||||
|
|
|
@ -21,7 +21,9 @@ public abstract class LicenseProvider {
|
|||
|
||||
public abstract LicensedFeature getFeature(String id);
|
||||
|
||||
public abstract void checkShellControl(String s);
|
||||
public abstract boolean checkOsName(String name);
|
||||
|
||||
public abstract void checkOsNameOrThrow(String s);
|
||||
|
||||
public abstract void showLicenseAlert(LicenseRequiredException ex);
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package io.xpipe.app.util;
|
||||
|
||||
import io.xpipe.app.core.check.AppSystemFontCheck;
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.issue.TrackEvent;
|
||||
import io.xpipe.app.prefs.AppPrefs;
|
||||
import io.xpipe.core.process.OsType;
|
||||
|
@ -83,7 +84,7 @@ public enum PlatformState {
|
|||
: h.getMessage();
|
||||
TrackEvent.warn(h.getMessage());
|
||||
PlatformState.setCurrent(PlatformState.EXITED);
|
||||
return Optional.of(new HeadlessException(msg));
|
||||
return Optional.of(ErrorEvent.expected(new HeadlessException(msg)));
|
||||
} catch (Throwable t) {
|
||||
TrackEvent.warn(t.getMessage());
|
||||
PlatformState.setCurrent(PlatformState.EXITED);
|
||||
|
|
|
@ -62,5 +62,9 @@ public class RdpConfig {
|
|||
public static class TypedValue {
|
||||
String type;
|
||||
String value;
|
||||
|
||||
public static TypedValue string(String value) {
|
||||
return new TypedValue("s", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -183,6 +183,7 @@ public class ScanAlert {
|
|||
}
|
||||
|
||||
shellControl = newValue.getStore().control();
|
||||
shellControl.withoutLicenseCheck();
|
||||
shellControl.start();
|
||||
var a = applicable.apply(entry.get().get(), shellControl);
|
||||
|
||||
|
|
|
@ -127,13 +127,23 @@ public interface SecretRetrievalStrategy {
|
|||
return new SecretQueryResult(null, true);
|
||||
}
|
||||
|
||||
String r;
|
||||
try (var cc = new LocalStore().control().command(cmd).start()) {
|
||||
return new SecretQueryResult(InPlaceSecretValue.of(cc.readStdoutOrThrow()), false);
|
||||
r = cc.readStdoutOrThrow();
|
||||
} catch (Exception ex) {
|
||||
ErrorEvent.fromThrowable("Unable to retrieve password with command " + cmd, ex)
|
||||
.handle();
|
||||
return new SecretQueryResult(null, true);
|
||||
}
|
||||
|
||||
if (r.lines().count() > 1 || r.isBlank()) {
|
||||
throw ErrorEvent.expected(new IllegalArgumentException("Received not exactly one output line:\n" + r + "\n\n" +
|
||||
"XPipe requires your password manager command to output only the raw password." +
|
||||
" If the output includes any formatting, messages, or your password key either matched multiple entries or none," +
|
||||
" you will have to change the command and/or password key."));
|
||||
}
|
||||
|
||||
return new SecretQueryResult(InPlaceSecretValue.of(r), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -46,7 +46,7 @@ public class SecretRetrievalStrategyHelper {
|
|||
.apply(struc -> struc.get().setPromptText("Password key"))
|
||||
.hgrow(),
|
||||
new ButtonComp(null, new FontIcon("mdomz-settings"), () -> {
|
||||
AppPrefs.get().selectCategory(9);
|
||||
AppPrefs.get().selectCategory("passwordManager");
|
||||
App.getApp().getStage().requestFocus();
|
||||
})
|
||||
.grow(false, true)))
|
||||
|
@ -54,6 +54,7 @@ public class SecretRetrievalStrategyHelper {
|
|||
return new OptionsBuilder()
|
||||
.name("passwordKey")
|
||||
.addComp(content, keyProperty)
|
||||
.nonNull()
|
||||
.bind(
|
||||
() -> {
|
||||
return new SecretRetrievalStrategy.PasswordManager(keyProperty.getValue());
|
||||
|
|
|
@ -74,21 +74,6 @@
|
|||
-fx-border-width: 4;
|
||||
}
|
||||
|
||||
.store-header-bar .menu-button .context-menu > * > * {
|
||||
-fx-padding: 5px 10px 5px 10px;
|
||||
}
|
||||
|
||||
.store-header-bar .menu-button .context-menu > * {
|
||||
-fx-padding: 0;
|
||||
}
|
||||
|
||||
.store-header-bar .menu-button .context-menu {
|
||||
-fx-padding: 3px;
|
||||
-fx-background-radius: 4px;
|
||||
-fx-border-radius: 4px;
|
||||
-fx-border-color: -color-neutral-muted;
|
||||
}
|
||||
|
||||
.store-creation-bar, .store-sort-bar, .store-category-bar {
|
||||
-fx-background-radius: 0 4px 4px 0;
|
||||
-fx-border-radius: 0 4px 4px 0;
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
.menu-button .context-menu > * > * {
|
||||
-fx-padding: 5px 10px 5px 10px;
|
||||
}
|
||||
|
||||
.menu-button .context-menu > * {
|
||||
-fx-padding: 0;
|
||||
}
|
||||
|
||||
.menu-button .context-menu {
|
||||
-fx-padding: 3px;
|
||||
-fx-background-radius: 4px;
|
||||
-fx-border-radius: 4px;
|
||||
-fx-border-color: -color-neutral-muted;
|
||||
}
|
||||
|
||||
.context-menu > * > * {
|
||||
-fx-padding: 3px 10px 3px 10px;
|
||||
-fx-background-radius: 1px;
|
||||
|
|
|
@ -67,7 +67,12 @@ public interface ShellControl extends ProcessControl {
|
|||
}
|
||||
|
||||
default <T extends ShellStoreState> ShellControl withShellStateFail(StatefulDataStore<T> store) {
|
||||
return onStartupFail(shellControl -> {
|
||||
return onStartupFail(t -> {
|
||||
// Ugly
|
||||
if (t.getClass().getSimpleName().equals("LicenseRequiredException")) {
|
||||
return;
|
||||
}
|
||||
|
||||
var s = store.getState();
|
||||
s.setRunning(false);
|
||||
store.setState(s);
|
||||
|
|
|
@ -57,3 +57,4 @@ github=GitHub
|
|||
mstsc=Microsoft Terminal Services Client (MSTSC)
|
||||
remmina=Remmina
|
||||
microsoftRemoteDesktopApp=Microsoft Remote Desktop.app
|
||||
bitwarden=Bitwarden
|
||||
|
|
|
@ -326,7 +326,7 @@ system=System
|
|||
application=Anwendung
|
||||
storage=Speicherung
|
||||
runOnStartup=Beim Starten ausführen
|
||||
closeBehaviour=Verhalten schließen
|
||||
closeBehaviour=Exit-Verhalten
|
||||
closeBehaviourDescription=Legt fest, wie XPipe beim Schließen des Hauptfensters vorgehen soll.
|
||||
language=Sprache
|
||||
languageDescription=Die zu verwendende Anzeigesprache.\n\nBeachte, dass die automatischen Übersetzungen als Grundlage dienen und von den Mitwirkenden manuell korrigiert und verbessert werden. Du kannst die Übersetzungsarbeit auch unterstützen, indem du Übersetzungsverbesserungen auf GitHub einreichst.
|
||||
|
@ -366,7 +366,8 @@ developerModeDescription=Wenn diese Option aktiviert ist, hast du Zugriff auf ei
|
|||
editor=Editor
|
||||
custom=Benutzerdefiniert
|
||||
passwordManagerCommand=Passwortmanager-Befehl
|
||||
passwordManagerCommandDescription=Der Befehl, der ausgeführt werden soll, um Passwörter abzurufen. Der Platzhalterstring $KEY wird beim Aufruf durch den zitierten Passwortschlüssel ersetzt. Dies sollte deinen Passwortmanager CLI aufrufen, um das Passwort auf stdout auszugeben, z. B. mypassmgr get $KEY.\n\nDu kannst den Schlüssel dann so einstellen, dass er immer dann abgefragt wird, wenn du eine Verbindung aufbaust, die ein Passwort erfordert.
|
||||
#custom
|
||||
passwordManagerCommandDescription=Der Befehl, der ausgeführt werden soll, um Passwörter abzurufen. Der Platzhalterstring $KEY wird beim Aufruf durch den Passwortschlüssel mit Anführungszeichen ersetzt. Dies sollte deinen Passwortmanager CLI aufrufen, um das Passwort auf stdout auszugeben, z. B. mypassmgr get $KEY.\n\nDu kannst den Schlüssel dann so einstellen, dass er immer dann abgefragt wird, wenn du eine Verbindung aufbaust, die ein Passwort erfordert.
|
||||
passwordManagerCommandTest=Passwort-Manager testen
|
||||
passwordManagerCommandTestDescription=Du kannst hier testen, ob die Ausgabe korrekt aussieht, wenn du einen Passwortmanager-Befehl eingerichtet hast. Der Befehl sollte nur das Passwort selbst auf stdout ausgeben, keine andere Formatierung sollte in der Ausgabe enthalten sein.
|
||||
preferEditorTabs=Lieber neue Tabs öffnen
|
||||
|
@ -434,3 +435,9 @@ modified=Geändert
|
|||
isOnlySupported=wird nur mit einer professionellen Lizenz unterstützt
|
||||
areOnlySupported=werden nur mit einer professionellen Lizenz unterstützt
|
||||
updateReadyTitle=Update auf $VERSION$ bereit
|
||||
#custom
|
||||
templates=Vorlagen
|
||||
retry=Wiederholen
|
||||
retryAll=Alle Versuche wiederholen
|
||||
replace=Ersetzen
|
||||
replaceAll=Ersetze alles
|
||||
|
|
|
@ -328,8 +328,8 @@ system=System
|
|||
application=Application
|
||||
storage=Storage
|
||||
runOnStartup=Run on startup
|
||||
#context: setting
|
||||
closeBehaviour=Close behaviour
|
||||
#context: title
|
||||
closeBehaviour=Exit behaviour
|
||||
closeBehaviourDescription=Controls how XPipe should proceed upon closing its main window.
|
||||
language=Language
|
||||
languageDescription=The display language to use.\n\nNote that the translations use automatically generated translations as a base and are manually fixed and improved by contributors. You can also help the translation effort by submitting translation fixes on GitHub.
|
||||
|
@ -439,3 +439,9 @@ modified=Modified
|
|||
isOnlySupported=is only supported with a professional license
|
||||
areOnlySupported=are only supported with a professional license
|
||||
updateReadyTitle=Update to $VERSION$ ready
|
||||
#context: digital template
|
||||
templates=Templates
|
||||
retry=Retry
|
||||
retryAll=Retry all
|
||||
replace=Replace
|
||||
replaceAll=Replace all
|
||||
|
|
|
@ -424,3 +424,8 @@ modified=Modificado
|
|||
isOnlySupported=sólo es compatible con una licencia profesional
|
||||
areOnlySupported=sólo son compatibles con una licencia profesional
|
||||
updateReadyTitle=Actualiza a $VERSION$ ready
|
||||
templates=Plantillas
|
||||
retry=Reintentar
|
||||
retryAll=Reintentar todo
|
||||
replace=Sustituye
|
||||
replaceAll=Sustituir todo
|
||||
|
|
|
@ -424,3 +424,8 @@ modified=Modifié
|
|||
isOnlySupported=n'est pris en charge qu'avec une licence professionnelle
|
||||
areOnlySupported=ne sont pris en charge qu'avec une licence professionnelle
|
||||
updateReadyTitle=Mise à jour de $VERSION$ ready
|
||||
templates=Modèles
|
||||
retry=Réessayer
|
||||
retryAll=Réessayer tout
|
||||
replace=Remplacer
|
||||
replaceAll=Remplacer tout
|
||||
|
|
|
@ -424,3 +424,8 @@ modified=Modificato
|
|||
isOnlySupported=è supportato solo con una licenza professionale
|
||||
areOnlySupported=sono supportati solo con una licenza professionale
|
||||
updateReadyTitle=Aggiornamento a $VERSION$ ready
|
||||
templates=Modelli
|
||||
retry=Riprova
|
||||
retryAll=Riprova tutti
|
||||
replace=Sostituire
|
||||
replaceAll=Sostituisci tutto
|
||||
|
|
|
@ -424,3 +424,8 @@ modified=変更された
|
|||
isOnlySupported=プロフェッショナルライセンスでのみサポートされる
|
||||
areOnlySupported=プロフェッショナルライセンスでのみサポートされる
|
||||
updateReadyTitle=$VERSION$ に更新
|
||||
templates=テンプレート
|
||||
retry=リトライ
|
||||
retryAll=すべて再試行する
|
||||
replace=置き換える
|
||||
replaceAll=すべて置き換える
|
||||
|
|
|
@ -424,3 +424,8 @@ modified=Gewijzigd
|
|||
isOnlySupported=wordt alleen ondersteund met een professionele licentie
|
||||
areOnlySupported=worden alleen ondersteund met een professionele licentie
|
||||
updateReadyTitle=Bijwerken naar $VERSION$ klaar
|
||||
templates=Sjablonen
|
||||
retry=Opnieuw proberen
|
||||
retryAll=Alles opnieuw proberen
|
||||
replace=Vervangen
|
||||
replaceAll=Alles vervangen
|
||||
|
|
|
@ -424,3 +424,8 @@ modified=Modificado
|
|||
isOnlySupported=só é suportado com uma licença profissional
|
||||
areOnlySupported=só são suportados com uma licença profissional
|
||||
updateReadyTitle=Actualiza para $VERSION$ ready
|
||||
templates=Modelos
|
||||
retry=Repetir
|
||||
retryAll=Repetir tudo
|
||||
replace=Substitui
|
||||
replaceAll=Substitui tudo
|
||||
|
|
|
@ -424,3 +424,8 @@ modified=Изменено
|
|||
isOnlySupported=поддерживается только при наличии профессиональной лицензии
|
||||
areOnlySupported=поддерживаются только с профессиональной лицензией
|
||||
updateReadyTitle=Обновление на $VERSION$ готово
|
||||
templates=Шаблоны
|
||||
retry=Retry
|
||||
retryAll=Повторите все попытки
|
||||
replace=Замените
|
||||
replaceAll=Заменить все
|
||||
|
|
|
@ -424,3 +424,8 @@ modified=Değiştirilmiş
|
|||
isOnlySupported=yalnızca profesyonel lisans ile desteklenir
|
||||
areOnlySupported=yalnızca profesyonel lisans ile desteklenir
|
||||
updateReadyTitle=$VERSION$ için güncelleme hazır
|
||||
templates=Şablonlar
|
||||
retry=Yeniden Dene
|
||||
retryAll=Tümünü yeniden dene
|
||||
replace=Değiştirin
|
||||
replaceAll=Tümünü değiştirin
|
||||
|
|
|
@ -424,3 +424,8 @@ modified=已修改
|
|||
isOnlySupported=只有专业许可证才支持
|
||||
areOnlySupported=只有专业许可证才支持
|
||||
updateReadyTitle=更新至$VERSION$ ready
|
||||
templates=模板
|
||||
retry=重试
|
||||
retryAll=全部重试
|
||||
replace=替换
|
||||
replaceAll=全部替换
|
||||
|
|
Loading…
Reference in a new issue