Database": "Storage -> Database",
"Strongly Recommended": "Strongly Recommended",
+ "Subject (whoami)": "Subject (whoami)",
"Suppress notification of hidden files change": "Suppress notification of hidden files change",
"Suspend database reflecting": "Suspend database reflecting",
"Suspend file watching": "Suspend file watching",
@@ -1094,26 +1155,38 @@
"Test connection and save": "Test connection and save",
"Test Settings and Continue": "Test Settings and Continue",
"Testing only - Resolve file conflicts by syncing newer copies of the file, this can overwrite modified files. Be Warned.": "Testing only - Resolve file conflicts by syncing newer copies of the file, this can overwrite modified files. Be Warned.",
+ "The algorithm used for JWT authentication.": "The algorithm used for JWT authentication.",
+ "The Application ID for P2P connection. This should be same among your devices. Default is 'self-hosted-livesync' and could not be modified from the UI.": "The Application ID for P2P connection. This should be same among your devices. Default is 'self-hosted-livesync' and could not be modified from the UI.",
"The connection test cannot add a signalling relay while P2P is active. Use the active relay settings, or disconnect P2P before testing.": "The connection test cannot add a signalling relay while P2P is active. Use the active relay settings, or disconnect P2P before testing.",
"The connection to the server has been configured successfully. As the next step,": "The connection to the server has been configured successfully. As the next step,",
+ "The credential/password for the TURN servers.": "The credential/password for the TURN servers.",
"The delay for consecutive on-demand fetches": "The delay for consecutive on-demand fetches",
"The files in this Vault are almost identical to the server's.": "The files in this Vault are almost identical to the server's.",
"The following accepted nodes are missing its node information:\n- ${missingNodes}\n\nThis indicates that they have not been connected for some time or have been left on an older version.\nIt is preferable to update all devices if possible. If you have any devices that are no longer in use, you can clear all accepted nodes by locking the remote once.": "The following accepted nodes are missing its node information:\n- ${missingNodes}\n\nThis indicates that they have not been connected for some time or have been left on an older version.\nIt is preferable to update all devices if possible. If you have any devices that are no longer in use, you can clear all accepted nodes by locking the remote once.",
"The Group ID and passphrase are used to identify your group of devices. Make sure to use the same Group ID and passphrase on all devices you want to synchronise.": "The Group ID and passphrase are used to identify your group of devices. Make sure to use the same Group ID and passphrase on all devices you want to synchronise.",
"The Hash algorithm for chunk IDs": "The Hash algorithm for chunk IDs",
"The IndexedDB adapter often offers superior performance in certain scenarios, but it has been found to cause memory leaks when used with LiveSync mode. When using LiveSync mode, please use IDB adapter instead.": "The IndexedDB adapter often offers superior performance in certain scenarios, but it has been found to cause memory leaks when used with LiveSync mode. When using LiveSync mode, please use IDB adapter instead.",
+ "The key (PSK in HSxxx in base64, or private key in ESxxx in PEM) used for JWT authentication.": "The key (PSK in HSxxx in base64, or private key in ESxxx in PEM) used for JWT authentication.",
+ "The key ID. this should be matched with CouchDB->jwt_keys->ALG:_`kid`.": "The key ID. this should be matched with CouchDB->jwt_keys->ALG:_`kid`.",
"the latest synchronisation data will be downloaded from the server to this device.": "the latest synchronisation data will be downloaded from the server to this device.",
"the local database, that is to say the synchronisation information, must be reconstituted.": "the local database, that is to say the synchronisation information, must be reconstituted.",
"The maximum duration for which chunks can be incubated within the document. Chunks exceeding this period will graduate to independent chunks.": "The maximum duration for which chunks can be incubated within the document. Chunks exceeding this period will graduate to independent chunks.",
"The maximum number of chunks that can be incubated within the document. Chunks exceeding this number will immediately graduate to independent chunks.": "The maximum number of chunks that can be incubated within the document. Chunks exceeding this number will immediately graduate to independent chunks.",
"The maximum total size of chunks that can be incubated within the document. Chunks exceeding this size will immediately graduate to independent chunks.": "The maximum total size of chunks that can be incubated within the document. Chunks exceeding this size will immediately graduate to independent chunks.",
"The minimum interval for automatic synchronisation on event.": "The minimum interval for automatic synchronisation on event.",
+ "The Nostr relay servers to establish connections for P2P connections. Multiple servers can be separated by commas.": "The Nostr relay servers to establish connections for P2P connections. Multiple servers can be separated by commas.",
+ "The Passphrase for P2P connection. This should be same among your devices.": "The Passphrase for P2P connection. This should be same among your devices.",
"The remote database is locked for synchronisation to prevent vault corruption because this device is not marked as 'resolved'. Please back up your vault, reset the local database, and select 'Mark this device as resolved'. This warning will persist until replication confirms the device is resolved.": "The remote database is locked for synchronisation to prevent vault corruption because this device is not marked as 'resolved'. Please back up your vault, reset the local database, and select 'Mark this device as resolved'. This warning will persist until replication confirms the device is resolved.",
"The remote is already set up, and the configuration is compatible (or got compatible by this operation).": "The remote is already set up, and the configuration is compatible (or got compatible by this operation).",
+ "The Room ID for P2P connection. This should be same among your devices.": "The Room ID for P2P connection. This should be same among your devices.",
+ "The Rotation duration of token in minutes. Each generated tokens will be valid only within this duration.": "The Rotation duration of token in minutes. Each generated tokens will be valid only within this duration.",
"The Setup-URI does not appear to be valid. Please check that you have copied it correctly.": "The Setup-URI does not appear to be valid. Please check that you have copied it correctly.",
"The Setup-URI is valid and ready to use.": "The Setup-URI is valid and ready to use.",
"the single, authoritative master copy": "the single, authoritative master copy",
+ "The subject for JWT authentication. Mostly username.": "The subject for JWT authentication. Mostly username.",
"the synchronisation data on the server will be built based on the current data on this device.": "the synchronisation data on the server will be built based on the current data on this device.",
+ "The TURN servers to use for P2P connections. Multiple servers can be separated by commas.": "The TURN servers to use for P2P connections. Multiple servers can be separated by commas.",
+ "The username for the TURN servers.": "The username for the TURN servers.",
"Themes": "Themes",
"There is a way to resolve this on other devices.": "There is a way to resolve this on other devices.",
"There may be differences between the files in this Vault and the server.": "There may be differences between the files in this Vault and the server.",
@@ -1140,6 +1213,7 @@
"TURN Credential": "TURN Credential",
"TURN server settings are only necessary if you are behind a strict NAT or firewall that prevents direct P2P connections. In most cases, you can leave these fields blank.": "TURN server settings are only necessary if you are behind a strict NAT or firewall that prevents direct P2P connections. In most cases, you can leave these fields blank.",
"TURN Server URLs (comma-separated)": "TURN Server URLs (comma-separated)",
+ "TURN Servers": "TURN Servers",
"TURN Username": "TURN Username",
"TweakMismatchResolve.Action.DisableAutoAcceptCompatible": "Disable auto-accept",
"TweakMismatchResolve.Action.Dismiss": "Dismiss",
@@ -1465,8 +1539,11 @@
"Use internal API": "Use internal API",
"Use Internal API": "Use Internal API",
"Use JWT Authentication": "Use JWT Authentication",
+ "Use JWT instead of Basic Authentication": "Use JWT instead of Basic Authentication",
+ "Use Only Local Chunks": "Use Only Local Chunks",
"Use Path-Style Access": "Use Path-Style Access",
"Use Random Number": "Use Random Number",
+ "Use Request API to avoid `inevitable` CORS problem": "Use Request API to avoid `inevitable` CORS problem",
"Use Segmented-splitter": "Use Segmented-splitter",
"Use splitting-limit-capped chunk splitter": "Use splitting-limit-capped chunk splitter",
"Use the detailed flow": "Use the detailed flow",
@@ -1490,14 +1567,17 @@
"We will now proceed with the server configuration.": "We will now proceed with the server configuration.",
"Welcome to Self-hosted LiveSync": "Welcome to Self-hosted LiveSync",
"When you save a file in the editor, start a sync automatically": "When you save a file in the editor, start a sync automatically",
+ "While enabled, it causes very performance impact but debugging replication testing and other features will be enabled. Please disable this if you have not read the source code. Requires restart of Obsidian. Sometimes there is no implementation.": "While enabled, it causes very performance impact but debugging replication testing and other features will be enabled. Please disable this if you have not read the source code. Requires restart of Obsidian. Sometimes there is no implementation.",
"Write credentials in the file": "Write credentials in the file",
"Write logs into the file": "Write logs into the file",
"xxhash32 (Fast but less collision resistance)": "xxhash32 (Fast but less collision resistance)",
"xxhash64 (Fastest)": "xxhash64 (Fastest)",
+ "xxhash64 is the current default.": "xxhash64 is the current default.",
"Yes, I want to add this device to my existing synchronisation": "Yes, I want to add this device to my existing synchronisation",
"Yes, I want to set up a new synchronisation": "Yes, I want to set up a new synchronisation",
"You are adding this device to an existing synchronisation setup.": "You are adding this device to an existing synchronisation setup.",
"You can configure in the Obsidian Plugin Settings.": "You can configure in the Obsidian Plugin Settings.",
+ "You can enable this setting to process the files with size mismatches, these files can be created by some APIs or integrations.": "You can enable this setting to process the files with size mismatches, these files can be created by some APIs or integrations.",
"You should create a new synchronisation destination and rebuild your data there.": "You should create a new synchronisation destination and rebuild your data there.",
"You should perform this operation only in exceptional circumstances, such as when the server data is completely corrupted, when changes on all other devices are no longer needed, or when the database size has become unusually large in comparison to the Vault size.": "You should perform this operation only in exceptional circumstances, such as when the server data is completely corrupted, when changes on all other devices are no longer needed, or when the database size has become unusually large in comparison to the Vault size.",
"you wanted(Thank you)!": "you wanted(Thank you)!"
diff --git a/src/common/messagesJson/zh.json b/src/common/messagesJson/zh.json
index 8f7ceb5c..eb5c5b05 100644
--- a/src/common/messagesJson/zh.json
+++ b/src/common/messagesJson/zh.json
@@ -1,33 +1,88 @@
{
+ ", please select the option that best describes the current state of your Vault. The application will then check your files in the most appropriate way based on your selection.": "īŧč¯ˇéæŠæįŦĻåäŊ åēåŊåįļæįé饚ãį¨åēäŧæ šæŽäŊ įéæŠīŧ፿åéįæšåŧæŖæĨäŊ įæäģļã",
"(Active)": "īŧ厞å¯į¨īŧ",
"(BETA) Always overwrite with a newer file": "å§įģäŊŋ፿´æ°įæäģļčĻį(æĩč¯į)",
"(Beta) Use ignore files": "(æĩč¯į)äŊŋį¨åŋŊįĨæäģļ",
"(Days passed, 0 to disable automatic-deletion)": "(厞čŋ夊æ°,0ä¸ēįĻį¨čĒå¨å é¤)",
+ "(e.g., after editing many files whilst offline)": "īŧäžåĻīŧįĻģįēŋæé´įŧčžäē大éæäģļäšåīŧ",
+ "(e.g., immediately after restoring on another computer, or having recovered from a backup)": "īŧäžåĻīŧå¨åĻä¸å°įĩčä¸čŋåäšåīŧæäģå¤äģŊæĸå¤äšåīŧ",
+ "(e.g., setting up for the first time on a new smartphone, starting from a clean slate)": "īŧäžåĻīŧ卿°ææēä¸éĻæŦĄčŽžįŊŽīŧäģéļåŧå§īŧ",
"(ex. Read chunks online) If this option is enabled, LiveSync reads chunks online directly instead of replicating them locally. Increasing Custom chunk size is recommended.": "(äžåĻ,å¨įēŋč¯ģåå)åĻæå¯į¨æ¤é饚,LiveSync å°į´æĨå¨įēŋč¯ģåå,č䏿¯å¨æŦå°å¤åļåãåģē莎åĸå čĒåŽäšå大å°",
"(MB) If this is set, changes to local and remote files that are larger than this will be skipped. If the file becomes smaller again, a newer one will be used.": "(MB)åĻæčŽžįŊŽäēæ¤éĄš,大ä翤大å°įæŦå°åčŋ፿äģļįæ´æšå°čĸĢ莺čŋãåĻææäģļåæŦĄåå°,å°äŊŋ፿´æ°įæäģļ",
"(Mega chars)": "(įžä¸åįŦĻ)",
+ "(Missing)": "īŧįŧēå¤ąīŧ",
"(Not recommended) If set, credentials will be stored in the file.": "(ä¸åģē莎)åĻæčŽžįŊŽ,åæŽå°åå¨å¨æäģļä¸",
"(Obsolete) Use an old adapter for compatibility": "(厞åŧį¨)ä¸ēå
ŧ厚æ§äŊŋ፿§éé
å¨",
"(RegExp) Empty to sync all files. Set filter as a regular expression to limit synchronising files.": "īŧæŖå襨螞åŧīŧįįŠē襨į¤ēåæĨæææäģļãå¯čŽžįŊŽæŖå襨螞åŧæĨéåļéčĻåæĨįæäģļã",
"(RegExp) If this is set, any changes to local and remote files that match this will be skipped.": "īŧæŖå襨螞åŧīŧåĻæåˇ˛čŽžįŊŽīŧåææåšé
æ¤æ¨ĄåŧįæŦå°åčŋį̝æäģļåæ´éŊäŧčĸĢ莺čŋã",
"(Select this if another device is already using LiveSync.) This option adds this device to that existing synchronisation setup.": "īŧåĻæäŊ 厞įģå¨åĻä¸å°įĩčæææēä¸äŊŋį¨åæĨīŧč¯ˇéæŠæ¤éĄšãīŧæ¤é饚éåå°åŊå莞å¤å å
Ĩį°æ LiveSync é
įŊŽį፿ˇã",
+ "(Select this if you already have another synchronising device.) This option adds this device to the same synchronisation.": "īŧåĻæäŊ 厞æåĻä¸å°æŖå¨åæĨį莞å¤īŧč¯ˇéæŠæ¤éĄšãīŧæ¤é饚äŧææŦ莞å¤å å
Ĩåä¸ä¸ĒåæĨã",
"(Select this if you are configuring this device as the first synchronisation device.) This option is suitable if you are new to LiveSync and want to set it up from scratch.": "īŧåĻæäŊ æŖå¨å°æ¤čŽžå¤é
įŊŽä¸ēįŦŦä¸å°åæĨ莞å¤īŧč¯ˇéæŠæ¤éĄšãīŧæ¤é饚éååæŦĄäŊŋ፠LiveSyncīŧåšļ叿äģ头åŧå§é
įŊŽį፿ˇã",
+ "â: Overwrite Remote": "âīŧčĻįčŋį̝",
+ "â: Overwrite Local": "âīŧčĻįæŦå°",
+ "â
: Use newer": "â
īŧäŊŋį¨čžæ°įæŦ",
+ "+1 week": "+1 å¨",
"> [!INFO]- The connected devices have been detected as follows:\n${devices}": "> [!INFO]- åˇ˛æŖæĩå°äģĨä¸åˇ˛čŋæĨ莞å¤īŧ\n${devices}",
+ "â ī¸ Important Notice": "â ī¸ éčĻæį¤ē",
+ "â ī¸ Please Confirm the Following": "â ī¸ č¯ˇįĄŽčŽ¤äģĨä¸å
厚",
+ "â SELECT": "â éæŠ",
+ "â SYNC": "â åæĨ",
+ "â WATCH": "â įč§",
+ "đĄ Off": "đĄ å
ŗé",
+ "đĄ On": "đĄ åŧå¯",
+ "đ´ Disconnected": "đ´ 厞æåŧ",
+ "đĩī¸ Diag": "đĩī¸ č¯æ",
+ "đ Delete": "đ å é¤",
+ "đĸ Connected": "đĸ 厞čŋæĨ",
+ "${count} issue(s) detected!": "æŖæĩå° ${count} ä¸ĒéŽéĸīŧ",
"A Setup URI is a single string of text containing your server address and authentication details. Using a URI, if one was generated by your server installation script, provides a simple and secure configuration.": "Setup URI æ¯ä¸æŽĩå
åĢæåĄå¨å°åä¸čޤč¯äŋĄæ¯įææŦãåĻææåĄå¨åŽčŖ
čæŦ厞įģįæäē URIīŧäŊŋį¨åŽå¯äģĨæ´įŽå䏿´åŽå
¨å°åŽæé
įŊŽã",
+ "Accept": "æĨå",
+ "Accept in session": "æŦæŦĄäŧč¯å
æĨå",
+ "ACCEPTED": "厞æĨå",
+ "ACCEPTED (in session)": "厞æĨåīŧæŦæŦĄäŧč¯īŧ",
"Access Key": "čŽŋéŽå¯éĨ",
+ "Access Key ID": "čŽŋéŽå¯éĨ ID",
+ "Action": "æäŊ",
"Activate": "å¯į¨",
"Active Remote Configuration": "įæä¸įčŋį¨é
įŊŽ",
+ "Active Remote Type": "åŊåäŊŋį¨įčŋį¨įąģå",
"Add default patterns": "æˇģå éģčŽ¤æ¨Ąåŧ",
"Add new connection": "æ°åĸčŋæĨ",
+ "AddOn Module (ConfigSync) has not been loaded. This is very unexpected situation. Please report this issue.": "éå æ¨ĄåīŧConfigSyncīŧæĒčŊå čŊŊãčŋæ¯é常æå¤įæ
åĩã蝎æĨ忤éŽéĸã",
+ "AddOn Module (HiddenFileSync) has not been loaded. This is very unexpected situation. Please report this issue.": "éå æ¨ĄåīŧHiddenFileSyncīŧæĒčŊå čŊŊãčŋæ¯é常æå¤įæ
åĩã蝎æĨ忤éŽéĸã",
+ "Advanced": "éĢįē§",
+ "Advanced Settings": "éĢįē§čŽžįŊŽ",
+ "After restarting, the data on this device will be uploaded to the server as the 'master copy'. Please be aware that any unintended data currently on the server will be completely overwritten.": "éå¯åīŧæŦ莞å¤ä¸įæ°æŽå°äŊä¸ēãä¸ģ坿Ŧãä¸äŧ å°æåĄå¨ãč¯ˇæŗ¨æīŧæåĄå¨ä¸į°æįäģģäŊééĸææ°æŽéŊäŧčĸĢåŽå
¨čĻįã",
+ "After restarting, the database on this device will be rebuilt using data from the server. If there are any unsynchronised files in this vault, conflicts may occur with the server data.": "éå¯åīŧå°į¨æåĄå¨ä¸įæ°æŽéåģēæŦ莞å¤įæ°æŽåēãåĻææŦåē䏿æĒåæĨįæäģļīŧå¯čŊäŧ䏿åĄå¨æ°æŽäē§įå˛įĒã",
+ "After that, synchronise to a brand new vault on each other device with the new remote one by one.": "äšåīŧå¨å
ļäģ莞å¤ä¸éä¸Ē፿°įčŋį̝åæĨå°ä¸ä¸Ēå
¨æ°įåēã",
+ "All checks passed successfully!": "æææŖæĨå厞éčŋīŧ",
"All devices have the same progress value (${progress}). Your devices seem to be synchronised. And be able to proceed with Garbage Collection.": "ææčŽžå¤įčŋåēĻåŧåį¸åīŧ${progress}īŧãįčĩˇæĨäŊ į莞å¤åˇ˛įģåæĨīŧå¯äģĨįģ§įģæ§čĄååžåæļã",
+ "All the same or non-existent": "å
¨é¨į¸åæä¸åå¨",
+ "Allow in session": "æŦæŦĄäŧč¯å
å
莸",
+ "Allow permanently": "æ°¸äš
å
莸",
+ "Allow sleep during synchronisation": "åæĨæé´å
莸įĄį ",
+ "Allow sleep during synchronisation on the desktop": "æĄéĸį̝åæĨæé´å
莸įĄį ",
+ "Allow the operating system to sleep while finite synchronisation operations are in progress.": "å¨ä¸æŦĄæ§åæĨæäŊčŋčĄæé´īŧå
莸æäŊįŗģįģčŋå
ĨįĄį ã",
+ "Also, please note that if you are using Peer-to-Peer synchronization, this configuration will be used when you switch to other methods and connect to a remote server in the future.": "åĻå¤č¯ˇæŗ¨æīŧåĻæäŊ æŖå¨äŊŋį¨įšå¯šįšåæĨīŧåŊäŊ æĨååæĸå°å
ļäģæšåŧåšļčŋæĨčŋį̝æåĄå¨æļīŧå°äŊŋ፿¤é
įŊŽã",
"Always prompt merge conflicts": "å§įģæį¤ēååšļå˛įĒ",
"Analyse": "įĢåŗåæ",
"Analyse database usage": "åææ°æŽåēäŊŋ፿
åĩ",
"Analyse database usage and generate a TSV report for diagnosis yourself. You can paste the generated report with any spreadsheet you like.": "åææ°æŽåēäŊŋ፿
åĩåšļįæ TSV æĨåäģĨäžæ¨čĒčĄč¯æãæ¨å¯äģĨå°įæįæĨåį˛č´´å°æ¨åæŦĸįäģģäŊįĩå襨æ ŧä¸ã",
+ "Application ID": "åē፠ID",
+ "Apply All Selected": "åē፿æåˇ˛é饚",
"Apply Latest Change if Conflicting": "åĻæå˛įĒååēį¨ææ°æ´æš",
"Apply preset configuration": "åēį¨éĸ莞é
įŊŽ",
+ "Apply the settings": "åēį¨čŽžįŊŽ",
"Ask a passphrase at every launch": "æ¯æŦĄå¯å¨æļč¯ĸéŽå¯į įč¯",
+ "Auto Connect": "čĒå¨čŋæĨ",
+ "Auto Start P2P Connection": "čĒå¨å¯å¨ P2P čŋæĨ",
+ "Auto-accept compatible tweak mismatches": "čĒ卿Ĩåå
ŧ厚įč°æ´åŧä¸ä¸č´",
+ "Automatic": "čĒå¨",
+ "Automatically accepts mismatches that are compatible but potentially lossy by comparing tweak modification times.": "éčŋæ¯čžč°æ´åŧįäŋŽæšæļé´īŧčĒ卿Ĩåå
ŧ厚äŊå¯čŊææįé
įŊŽä¸ä¸č´ã",
+ "Automatically broadcast changes to connected peers": "čĒå¨å°æ´æšåšŋæįģ厞čŋæĨįå¯šįæš",
+ "Automatically start P2P connection on launch": "å¯å¨æļčĒå¨å¯å¨ P2P čŋæĨ",
"Automatically Sync all files when opening Obsidian.": "æåŧ Obsidian æļčĒå¨åæĨæææäģļ",
+ "Available Peers": "å¯į¨įå¯šįæš",
"Back": "čŋå",
"Back to non-configured": "æĸå¤ä¸ēæĒé
įŊŽįļæ",
"Batch database update": "æšéæ°æŽåēæ´æ°",
@@ -35,19 +90,37 @@
"Batch size": "æšé大å°",
"Batch size of on-demand fetching": "æéčˇåįæšé大å°",
"Before v0.17.16, we used an old adapter for the local database. Now the new adapter is preferred. However, it needs local database rebuilding. Please disable this toggle when you have enough time. If leave it enabled, also while fetching from the remote database, you will be asked to disable this.": "å¨ v0.17.16 äšå,æäģŦäŊŋ፿§éé
å¨äŊä¸ēæŦå°æ°æŽåēãį°å¨éĻéæ°éé
å¨ãäŊæ¯,åŽéčĻéåģēæŦå°æ°æŽåēã蝎卿čļŗå¤æļé´æļįĻ፿¤åŧå
ŗãåĻæäŋæå¯į¨įļæ,åšļä¸å¨äģčŋ፿°æŽåēčˇåæļ,įŗģįģå°čϿ࿍įĻ፿¤åŧå
ŗã",
+ "Broadcasting?": "æŖå¨åšŋæīŧ",
"Bucket Name": "å卿Ąļåį§°",
+ "by resetting the remote, you will be informed on other devices.": "éįŊŽčŋį̝åīŧå
ļäģ莞å¤äŧæļå°éįĨã",
"Cancel": "åæļ",
+ "Cancel all and reboot": "å
¨é¨åæļåšļéå¯",
"Cancel Garbage Collection": "åæļååžåæļ",
+ "Changing the encryption algorithm will prevent access to any data previously encrypted with a different algorithm. Ensure that all your devices are configured to use the same algorithm to maintain access to your data.": "æ´æšå å¯įŽæŗå°å¯ŧč´æ æŗčŽŋéŽæ¤åį¨å
ļäģįŽæŗå å¯įæ°æŽãč¯ˇįĄŽäŋææčŽžå¤éŊé
įŊŽä¸ēäŊŋį¨į¸åįŽæŗīŧäģĨäŋæå¯šæ°æŽįčŽŋéŽã",
+ "Changing this setting requires migrating existing data (a bit time may be taken) and restarting Obsidian. Please make sure to back up your data before proceeding.": "æ´æšæ¤čŽžįŊŽéčĻčŋį§ģį°ææ°æŽīŧå¯čŊéčĻä¸äēæļé´īŧåšļéå¯ Obsidianã蝎åĄåŋ
å¨įģ§įģäšåå¤äģŊæ°æŽã",
"Check": "įĢåŗæŖæĨ",
"Check and convert non-path-obfuscated files": "æŖæĨåšļčŊŦæĸæĒčŋčĄčˇ¯åžæˇˇæˇįæäģļ",
"Check for documents that have not been converted to path-obfuscated IDs and convert them if necessary.": "æŖæĨå°æĒčŊŦæĸä¸ēčˇ¯åžæˇˇæˇ ID įææĄŖīŧåšļå¨éčĻæļå°å
ļčŊŦæĸã",
+ "Check server requirements": "æŖæĨæåĄå¨čĻæą",
+ "Checking connection... Please wait.": "æŖå¨æŖæĨčŋæĨâĻ蝎į¨åã",
+ "Chunk revisions are always derived from their content. This stored key is retained only for compatibility.": "æ°æŽåäŋŽčŽĸįæŦå§įģįąå
ļå
厚æ¨å¯ŧåžåēãæ¤åˇ˛åå¨į莞įŊŽéĄšäģ
ä¸ēå
ŧ厚čäŋįã",
+ "Chunk Splitter": "æ°æŽååå˛å¨",
+ "Chunks": "å",
+ "Close": "å
ŗé",
+ "Close & Disconnect": "å
ŗéåšļæåŧčŋæĨ",
+ "Close this dialog": "å
ŗéæ¤å¯šč¯æĄ",
+ "Closed:": "厞å
ŗéīŧ",
"cmdConfigSync.showCustomizationSync": "æžį¤ēčĒåŽäšåæĨ",
"Comma separated `.gitignore, .dockerignore`": "į¨éåˇåé,äžåĻ `.gitignore, .dockerignore`",
+ "Command": "åŊäģ¤",
+ "Communicating": "éäŋĄä¸",
"Compaction in progress on remote database...": "æŖå¨čŋ፿°æŽåē䏿§čĄåįŧŠ...",
"Compaction on remote database completed successfully.": "čŋ፿°æŽåēåįŧŠåˇ˛æååŽæã",
"Compaction on remote database failed.": "čŋ፿°æŽåēåįŧŠå¤ąč´Ĩã",
"Compaction on remote database timed out.": "čŋ፿°æŽåēåįŧŠčļ
æļã",
+ "Compare file": "æ¯čžæäģļ",
"Compare the content of files between on local database and storage. If not matched, you will be asked which one you want to keep.": "æ¯čžæŦå°æ°æŽåēä¸åå¨ä¸įæäģļå
厚īŧåĻæä¸ä¸č´īŧäŊ å°čĸĢč¯ĸéŽčĻäŋįåĒä¸äģŊã",
+ "Compare time and take newer": "æ¯čžæļé´åšļéį¨čžæ°įæŦ",
"Compatibility (Conflict Behaviour)": "å
ŧ厚æ§īŧå˛įĒčĄä¸ēīŧ",
"Compatibility (Database structure)": "å
ŧ厚æ§īŧæ°æŽåēįģæīŧ",
"Compatibility (Internal API Usage)": "å
ŧ厚æ§īŧå
é¨ API äŊŋį¨īŧ",
@@ -55,47 +128,90 @@
"Compatibility (Remote Database)": "å
ŧ厚æ§īŧčŋįĢ¯æ°æŽåēīŧ",
"Compatibility (Trouble addressed)": "å
ŧ厚æ§īŧéŽéĸäŋŽå¤īŧ",
"Compute revisions for chunks": "ä¸ē chunks 莥įŽäŋŽčŽĸįæŦ(äģĨåįčĄä¸ē)",
+ "Configuration": "é
įŊŽ",
"Configuration Encryption": "é
įŊŽå å¯",
"Configure": "é
įŊŽ",
"Configure And Change Remote": "é
įŊŽåšļåæĸčŋį̝",
"Configure E2EE": "é
įŊŽ E2EE",
"Configure Remote": "é
įŊŽčŋį̝",
"Configure the same server information as your other devices again, manually, very advanced users only.": "æå¨éæ°čžå
Ĩä¸äŊ å
ļäģ莞å¤į¸åįæåĄå¨äŋĄæ¯ãäģ
éåéĢįē§į¨æˇã",
+ "Conflict & Deletion Options": "å˛įĒä¸å é¤é饚",
+ "Connect": "čŋæĨ",
+ "Connect to existing database and continue": "čŋæĨå°į°ææ°æŽåēåšļįģ§įģ",
+ "Connected to Signaling Server (as Peer ID: ${peerId})": "厞čŋæĨå°äŋĄä줿åĄå¨īŧå¯šįæš IDīŧ${peerId}īŧ",
+ "Connected:": "厞čŋæĨīŧ",
"Connection Method": "čŋæĨæšåŧ",
+ "Connection Settings": "čŋæĨ莞įŊŽ",
+ "Connection:": "čŋæĨīŧ",
+ "Content-derived chunk revisions (obsolete setting)": "åēäēå
åŽšįæ°æŽåäŋŽčŽĸīŧ厞čŋæļį莞įŊŽīŧ",
+ "Continue anyway": "äģįļįģ§įģ",
"Continue to CouchDB setup": "įģ§įģčŋčĄ CouchDB 莞įŊŽ",
"Continue to Peer-to-Peer only setup": "įģ§įģčŋčĄäģ
Peer-to-Peer 莞įŊŽ",
"Continue to S3/MinIO/R2 setup": "įģ§įģčŋčĄ S3/MinIO/R2 莞įŊŽ",
"Copy": "å¤åļ",
"Copy Report to clipboard": "å°æĨåå¤åļå°åĒč´´æŋ",
+ "CouchDB Configuration": "CouchDB é
įŊŽ",
"CouchDB Connection Tweak": "CouchDB čŋæĨč°äŧ",
+ "CouchDB validates the database name when you connect. The name must not be empty.": "CouchDB äŧå¨čŋæĨæļæ ĄéĒæ°æŽåēåį§°ãåį§°ä¸čŊä¸ēįŠēã",
+ "Create or connect to database and continue": "ååģēæčŋæĨå°æ°æŽåēåšļįģ§įģ",
+ "Create P2P remote": "ååģē P2P čŋį̝",
"Cross-platform": "čˇ¨åšŗå°",
"Current adapter: {adapter}": "åŊåéé
å¨īŧ{adapter}",
+ "Custom Headers": "čĒåŽäšč¯ˇæąå¤´",
+ "Custom headers for requesting the bucket. e.g. `x-custom-header1: value1\n x-custom-header2: value2`": "į¨äē蝎æąå卿ĄļįčĒåŽäšč¯ˇæąå¤´ãäžåĻīŧ`x-custom-header1: value1\n x-custom-header2: value2`",
+ "Custom headers for requesting the CouchDB. e.g. `x-custom-header1: value1\n x-custom-header2: value2`": "į¨äēč¯ˇæą CouchDB įčĒåŽäšč¯ˇæąå¤´ãäžåĻīŧ`x-custom-header1: value1\n x-custom-header2: value2`",
"Customization Sync": "čĒåŽäšåæĨ",
"Customization Sync (Beta3)": "čĒåŽäšåæĨīŧBeta3īŧ",
"Data Compression": "æ°æŽåįŧŠ",
+ "Data retrieval scheduled": "åˇ˛åŽææ°æŽčˇå",
+ "Data to Copy": "čĻå¤åļįæ°æŽ",
+ "Database -> Storage": "æ°æŽåē â åå¨",
"Database Adapter": "æ°æŽåēéé
å¨",
"Database Name": "æ°æŽåēåį§°",
"Database suffix": "æ°æŽåēåįŧ",
+ "Date": "æĨæ",
"Default": "éģ莤",
"Delay conflict resolution of inactive files": "æ¨čŋč§Ŗåŗä¸æ´ģ卿äģļ",
"Delay merge conflict prompt for inactive files.": "æ¨čŋæå¨č§Ŗåŗä¸æ´ģ卿äģļ",
"Delete": "å é¤",
"Delete all customization sync data": "å 餿æčĒåŽäšåæĨæ°æŽ",
"Delete all data on the remote server.": "å é¤čŋį̝æåĄå¨ä¸įæææ°æŽã",
+ "Delete All of": "å
¨é¨å é¤",
"Delete local database to reset or uninstall Self-hosted LiveSync": "å 餿Ŧå°æ°æŽåēäģĨéįŊŽæå¸čŊŊ Self-hosted LiveSync",
+ "Delete local files if deleted on remote": "čĨčŋį¨åˇ˛å é¤īŧåå 餿Ŧå°æäģļ",
+ "Delete local files if not on remote": "čĨčŋį¨ä¸åå¨īŧåå 餿Ŧå°æäģļ",
"Delete old metadata of deleted files on start-up": "å¯å¨æļå é¤åˇ˛å 餿äģļįæ§å
æ°æŽ",
"Delete Remote Configuration": "å é¤čŋį̝é
įŊŽ",
"Delete remote configuration '{name}'?": "čĻå é¤čŋį̝é
įŊŽâ{name}âåīŧ",
+ "Delete remote configuration '${name}'?": "å é¤čŋį̝é
įŊŽã${name}ãīŧ",
+ "DENIED": "厞æįģ",
+ "DENIED (in session)": "厞æįģīŧæŦæŦĄäŧč¯īŧ",
+ "Deny": "æįģ",
+ "Deny in session": "æŦæŦĄäŧč¯å
æįģ",
+ "Deny permanently": "æ°¸äš
æįģ",
+ "Deselect all": "åæļå
¨é",
"desktop": "æĄéĸ莞å¤",
+ "Desktop only; uses more battery and network.": "äģ
éæĄéĸį̝īŧäŧæļčæ´å¤įĩéåįŊįģæĩéã",
+ "Desktop only. Allow sleep on this device even when the general option is disabled.": "äģ
éæĄéĸį̝ãåŗäŊŋéį¨ééĄšåˇ˛įĻį¨īŧäšå
莸æ¤čŽžå¤čŋå
ĨįĄį ã",
+ "Detected Peers": "æŖæĩå°įå¯šįæš",
"Developer": "åŧåč
",
"Device": "莞å¤",
+ "device name": "莞å¤åį§°",
"Device name": "莞å¤åį§°",
+ "Device name to identify the device. Please use shorter one for the stable peer detection, i.e., \"iphone-16\" or \"macbook-2021\".": "į¨äēæ č¯čŽžå¤įåį§°ãä¸ēäēå¯šįæšæŖæĩæ´į¨ŗåŽīŧ蝎äŊŋį¨čžįįååīŧäžåĻãiphone-16ãæãmacbook-2021ãã",
+ "Device Peer ID": "莞å¤å¯šįæš ID",
"Device Setup Method": "莞å¤čŽžįŊŽæšåŧ",
+ "Devices:": "莞å¤īŧ",
+ "Diagnostic RTCPeerConnection is enabled": "厞å¯į¨č¯æį¨ RTCPeerConnection",
"dialog.yourLanguageAvailable": "Self-hosted LiveSync厞æäžæ¨č¯č¨įįŋģč¯,å æ¤å¯į¨äē%{Display language}\n\næŗ¨æīŧåšļéæææļæ¯éŊ厞įŋģč¯ãæäģŦæåž
æ¨įč´ĄįŽīŧ\næŗ¨æ 2īŧčĨæ¨ååģēéŽéĸæĨåīŧ **蝎åæĸå%{lang-def}** īŧįļåæĒååąåšæĒåžãæļæ¯åæĨåŋīŧæ¤æäŊå¯å¨čŽžįŊŽå¯šč¯æĄä¸åŽæ\næŋæ¨äŊŋį¨éĄēåŋīŧ",
"dialog.yourLanguageAvailable.btnRevertToDefault": "äŋæ%{lang-def}",
"dialog.yourLanguageAvailable.Title": " įŋģč¯å¯į¨!",
+ "Diff": "åˇŽåŧ",
+ "Different": "ä¸å",
"Disables all synchronization and restart.": "įĻ፿æåæĨåšļéæ°å¯å¨ã",
"Disables logging, only shows notifications. Please disable if you report an issue.": "įĻ፿Ĩåŋ莰åŊ,äģ
æžį¤ēéįĨãåĻææ¨æĨåéŽéĸ,蝎įĻ፿¤é饚",
+ "Disconnect": "æåŧčŋæĨ",
+ "Dismiss": "åŋŊįĨ",
"Display Language": "æžį¤ēč¯č¨",
"Display name": "æžį¤ēåį§°",
"Do not check configuration mismatch before replication": "å¨å¤åļå䏿ŖæĨé
įŊŽä¸åšé
",
@@ -109,7 +225,7 @@
"Doctor.Button.Skip": "äŋæä¸å",
"Doctor.Button.Yes": "įĄŽåŽ",
"Doctor.Dialogue.Main": "æ¨åĨŊīŧé
įŊŽåģįåˇ˛æ šæŽæ¨įčĻæąå¯å¨(æč°ĸæ¨)īŧīŧéæžįæ¯,æŖæĩå°é¨åé
įŊŽå卿Ŋå¨éŽéĸã蝎æžåŋ,æäģŦå°éä¸č§ŖåŗčŋäēéŽéĸã\n\næååįĨæ¨,æäģŦå°å°ąäģĨä¸äē饚čŋčĄįĄŽčޤīŧ\n\nä¸ēæ°æŽå莥įŽäŋŽčŽĸįæŦ(æ¤åčĄä¸ē)\nåĸåŧēå大å°\n\næäģŦåŧå§å¤įå?",
- "Doctor.Dialogue.MainFix": "\n## ${name}\n\n| Current | Ideal |\n|:---:|:---:|\n| ${current} | ${ideal} |\n\n**Recommendation Level:** ${level}\n\n### Why this has been detected?\n\n${reason}\n\n${note}\n\nFix this to the ideal value?",
+ "Doctor.Dialogue.MainFix": "\n## ${name}\n\n| åŊååŧ | įæŗåŧ |\n|:---:|:---:|\n| ${current} | ${ideal} |\n\n**åģē莎įē§åĢīŧ** ${level}\n\n### ä¸ēäŊæŖæĩå°æ¤éŽéĸīŧ\n\n${reason}\n\n${note}\n\næ¯åĻå°å
ļäŋŽæŖä¸ēįæŗåŧīŧ",
"Doctor.Dialogue.Title": "Self-hosted LiveSync é
įŊŽč¯æ",
"Doctor.Dialogue.TitleAlmostDone": "å
¨é¨åŽæ!",
"Doctor.Dialogue.TitleFix": "äŋŽå¤éŽéĸ ${current}/${total}",
@@ -121,44 +237,98 @@
"Doctor.Message.RebuildLocalRequired": "æŗ¨æīŧéčĻéåģēæŦå°æ°æŽåēäģĨåē፿¤éĄšīŧ",
"Doctor.Message.RebuildRequired": "æŗ¨æīŧéčĻéåģēæčŊåē፿¤éĄšīŧ",
"Doctor.Message.SomeSkipped": "æäģŦå°æäēéŽéĸįįģäēäģĨåå¤įãæ¯åĻčĻå¨ä¸æŦĄå¯å¨æļåæŦĄč¯ĸéŽæ¨īŧ",
- "Doctor.RULES.E2EE_V02500.REASON": "The End-to-End Encryption has got now more robust and faster. Also because, the previous E2EE was found to be compromised in a re-conducted code review. It should be applied as soon as possible. Really apologises for your inconvenience. And, this setting is not forward compatible. All synchronised devices must be updated to v0.25.0 or higher. Rebuilds are not required and will be converted from the new transfer to the new format, However, it is recommended to rebuild whenever possible.",
+ "Doctor.RULES.E2EE_V02500.REASON": "į̝å°į̝å å¯į°å¨ååžæ´å ፺åĨãæ´åŋĢãåĻå¤īŧäšåįį̝å°į̝å å¯å¨éæ°čŋčĄįäģŖį åŽĄæĨä¸čĸĢåį°åå¨åŽå
¨æŧæ´īŧå æ¤åēå°ŊåŋĢå¯į¨ã寚æ¤įģäŊ å¸ĻæĨįä¸äžŋīŧæäģŦæˇąčĄ¨ææãæ¤å¤īŧč¯Ĩ莞įŊŽä¸ååå
ŧ厚īŧææåˇ˛åæĨį莞å¤éŊåŋ
éĄģæ´æ°å° v0.25.0 ææ´éĢįæŦãæ ééåģēīŧæ°æŽäŧäģæ°įäŧ čžæšåŧčŊŦæĸä¸ēæ°æ ŧåŧīŧä¸čŋäģåģē莎å°Ŋå¯čŊčŋčĄéåģēã",
+ "Document History": "ææĄŖåå˛čްåŊ",
"Duplicate": "å¤åļ",
"Duplicate remote": "å¤åļčŋį̝é
įŊŽ",
"E2EE Configuration": "E2EE é
įŊŽ",
"Edge case addressing (Behaviour)": "čžšįŧæ
åĩå¤įīŧčĄä¸ēīŧ",
"Edge case addressing (Database)": "čžšįŧæ
åĩå¤įīŧæ°æŽåēīŧ",
"Edge case addressing (Processing)": "čžšįŧæ
åĩå¤įīŧå¤įīŧ",
+ "Effectively a directory. Should end with `/`. e.g., `vault-name/`.": "åŽé
䏿¯ä¸ä¸ĒįŽåŊīŧåēäģĨ `/` įģå°žīŧäžåĻ `vault-name/`ã",
"Emergency restart": "į´§æĨéå¯",
"Enable advanced features": "å¯į¨éĢįē§åčŊ",
+ "Enable advanced mode": "å¯į¨éĢįē§æ¨Ąåŧ",
"Enable customization sync": "å¯į¨čĒåŽäšåæĨ",
+ "Enable Developers' Debug Tools (If available).": "å¯į¨åŧåč
č°č¯åˇĨå
ˇīŧåĻå¯į¨īŧã",
"Enable Developers' Debug Tools.": "å¯į¨åŧåč
č°č¯åˇĨå
ˇ ",
"Enable edge case treatment features": "å¯į¨čžšįŧæ
åĩå¤įåčŊ",
+ "Enable forcePathStyle": "å¯į¨ forcePathStyle",
+ "Enable P2P Replicator": "å¯į¨ P2P å¤åļå¨",
+ "Enable P2P Synchronization": "å¯į¨ P2P åæĨ",
+ "Enable per-file customization sync": "å¯į¨éæäģļčĒåŽäšåæĨ",
+ "Enable power user mode": "å¯į¨ä¸åŽļæ¨Ąåŧ",
"Enable poweruser features": "å¯į¨éĢįē§į¨æˇåčŊ",
"Enable this if your Object Storage doesn't support CORS": "åĻææ¨įå¯ščąĄåå¨ä¸æ¯æ CORS,蝎å¯į¨æ¤åčŊ ",
"Enable this option to automatically apply the most recent change to documents even when it conflicts": "å¯į¨æ¤é饚å¯å¨ææĄŖå˛įĒæļčĒå¨åē፿æ°įæ´æš",
+ "Enabled": "厞å¯į¨",
+ "Enabling end-to-end encryption ensures that your data is encrypted on your device before being sent to the remote server. This means that even if someone gains access to the server, they won't be able to read your data without the passphrase. Make sure to remember your passphrase, as it will be required to decrypt your data on other devices.": "å¯į¨į̝å°į̝å å¯å¯įĄŽäŋæ°æŽå¨åéå°čŋį̝æåĄå¨äšå厞卿Ŧ莞å¤ä¸å å¯ãčŋæåŗįåŗäŊŋæäēēčˇåžäēæåĄå¨įčŽŋéŽæéīŧæ˛Ąæå¯į įč¯äšæ æŗč¯ģåäŊ įæ°æŽã蝎åĄåŋ
莰äŊäŊ įå¯į įč¯īŧå ä¸ēå¨å
ļäģ莞å¤ä¸č§Ŗå¯æ°æŽæļéčĻį¨å°åŽã",
"Encrypt contents on the remote database. If you use the plugin's synchronization feature, enabling this is recommended.": "å å¯čŋ፿°æŽåēä¸įå
厚ãåĻææ¨äŊŋ፿äģļįåæĨåčŊ,ååģē莎å¯į¨æ¤åčŊ ",
+ "Encrypt file properties (paths, times, sizes, and Chunk references) used by Hidden File Sync and Customisation Sync. This affects future writes; manually rebuild the remote database to protect existing properties. Requires E2EE V2, Property Encryption, and compatible clients on every device.": "å å¯éčæäģļåæĨåčĒåŽäšåæĨæäŊŋį¨įæäģļåąæ§īŧ莝åžãæļé´ã大å°äģĨåæ°æŽååŧį¨īŧãæ¤čŽžįŊŽåŊąåäšåįåå
ĨīŧåĻéäŋæ¤åˇ˛æåąæ§īŧ蝎æå¨éåģēčŋ፿°æŽåēãéčĻ E2EE V2 ååąæ§å å¯īŧåšļ䏿¯å°čŽžå¤ä¸įåŽĸæˇį̝éŊéå
ŧ厚ã",
+ "Encrypt internal file Properties": "å å¯å
鍿äģļåąæ§",
"Encrypting sensitive configuration items": "å 坿æé
įŊŽéĄš",
+ "Encryption Algorithm": "å å¯įŽæŗ",
+ "Encryption passphrase. If changed, you should overwrite the server's database with the new (encrypted) files.": "å å¯å¯į įč¯ãåĻææ´æšīŧäŊ åēåŊ፿°įīŧå å¯åįīŧæäģļčĻįæåĄå¨ä¸įæ°æŽåēã",
"Encryption phassphrase. If changed, you should overwrite the server's database with the new (encrypted) files.": "å å¯å¯į ãåĻææ´æš,æ¨åēč¯Ĩ፿°į(å å¯į)æäģļčĻįæåĄå¨įæ°æŽåē ",
"End-to-End Encryption": "į̝å°į̝å å¯",
+ "End-to-End Encryption Algorithm": "į̝å°į̝å å¯įŽæŗ",
"Endpoint URL": "įģį̝URL",
"Enhance chunk size": "åĸ大å大å°",
+ "Enter a complete HTTP or HTTPS URL.": "čžå
ĨåŽæ´į HTTP æ HTTPS å°åã",
+ "Enter a folder prefix (optional)": "čžå
Ĩæäģļ多åįŧīŧå¯éīŧ",
"Enter Server Information": "čžå
ĨæåĄå¨äŋĄæ¯",
+ "Enter Setup URI": "čžå
Ĩ莞įŊŽ URI",
"Enter the server information manually": "æå¨čžå
ĨæåĄå¨äŋĄæ¯",
+ "Enter TURN credential": "čžå
Ĩ TURN åæŽ",
+ "Enter TURN username": "čžå
Ĩ TURN ፿ˇå",
+ "Enter your Access Key ID": "čžå
ĨäŊ įčŽŋéŽå¯éĨ ID",
+ "Enter your Bucket Name": "čžå
ĨäŊ įå卿Ąļåį§°",
+ "Enter your database name": "čžå
ĨäŊ įæ°æŽåēåį§°",
+ "Enter your JWT Key ID": "čžå
ĨäŊ į JWT å¯éĨ ID",
+ "Enter your JWT secret or private key": "čžå
ĨäŊ į JWT å¯éĨæį§éĨ",
+ "Enter your JWT Subject (CouchDB Username)": "čžå
ĨäŊ į JWT ä¸ģäŊīŧCouchDB ፿ˇåīŧ",
+ "Enter your passphrase": "čžå
ĨäŊ įå¯į įč¯",
+ "Enter your password": "čžå
ĨäŊ įå¯į ",
+ "Enter your Region (e.g., us-east-1, auto for R2)": "čžå
ĨäŊ įåēåīŧäžåĻ us-east-1īŧR2 åĄĢ autoīŧ",
+ "Enter your Secret Access Key": "čžå
ĨäŊ įį§æčŽŋéŽå¯éĨ",
+ "Enter your username": "čžå
ĨäŊ į፿ˇå",
+ "Error during connection test: ${reason}": "čŋæĨæĩč¯åēéīŧ${reason}",
+ "Error during testAndFixSettings: ${reason}": "testAndFixSettings åēéīŧ${reason}",
+ "Experimental Settings": "åŽéĒæ§čŽžįŊŽ",
"Export": "å¯ŧåē",
"Failed to connect to remote for compaction.": "æ æŗčŋæĨå°čŋ፿°æŽåēäģĨæ§čĄåįŧŠã",
"Failed to connect to remote for compaction. ${reason}": "æ æŗčŋæĨå°čŋ፿°æŽåēäģĨæ§čĄåįŧŠã${reason}",
+ "Failed to connect to the server: ${reason}": "æ æŗčŋæĨå°æåĄå¨īŧ${reason}",
+ "Failed to connect to the server. Please check your settings.": "æ æŗčŋæĨå°æåĄå¨ãč¯ˇæŖæĨäŊ į莞įŊŽã",
+ "Failed to connect to the signalling relay: ${reason}": "æ æŗčŋæĨå°äŋĄäģ¤ä¸įģ§īŧ${reason}",
+ "Failed to create replicator instance.": "ååģēå¤åļå¨åŽäžå¤ąč´Ĩã",
+ "Failed to parse Setup-URI.": "æ æŗč§ŖæčŽžįŊŽ URIã",
"Failed to start one-shot replication before Garbage Collection. Garbage Collection Cancelled.": "æ æŗå¨ååžåæļåå¯å¨ä¸æŦĄæ§å¤åļãååžåæļ厞åæļã",
"Failed to start replication after Garbage Collection.": "ååžåæļåæ æŗå¯å¨å¤åļã",
+ "Failed:": "å¤ąč´Ĩīŧ",
"Fetch": "čˇå",
"Fetch chunks on demand": "æéčˇåå",
"Fetch database with previous behaviour": "äŊŋį¨äģĨåįčĄä¸ēčˇåæ°æŽåē",
"Fetch remote settings": "čˇåčŋįĢ¯čŽžįŊŽ",
+ "FETCHING": "čˇåä¸",
+ "Fetching status...": "æŖå¨čˇåįļæâĻ",
+ "File integrity": "æäģļåŽæ´æ§",
+ "File prefix on the bucket": "å卿Ąļä¸įæäģļåįŧ",
"File to resolve conflict": "éæŠčĻč§Ŗåŗå˛įĒįæäģļ",
+ "File to view History": "čĻæĨįåå˛čްåŊįæäģļ",
"Filename": "æäģļå",
+ "Files with modification times greater than this value (in seconds since the Unix epoch) will not have their events reflected. Set to 0 to disable this limit.": "äŋŽæšæļé´å¤§ä翤åŧīŧUnix æļé´æŗīŧåäŊä¸ēį§īŧįæäģļīŧå
ļäēäģļå°ä¸äŧčĸĢåæ ã莞ä¸ē 0 å¯įĻ፿¤éåļã",
+ "Final Confirmation: Overwrite Server Data with This Device's Files": "æįģįĄŽčŽ¤īŧ፿Ŧ莞å¤įæäģļčĻįæåĄå¨æ°æŽ",
+ "Finalise the process and resume normal operation": "åŽææĩį¨åšļæĸ夿Ŗå¸¸čŋčĄ",
"First, please select the option that best describes your current situation.": "éĻå
īŧč¯ˇéæŠæįŦĻåäŊ åŊåæ
åĩįé饚ã",
+ "Fix": "äŋŽå¤",
"Flag and restart": "æ 莰åéå¯",
+ "Flagged Selective": "厞æ 莰ä¸ēéæŠæ§",
+ "Folder Prefix": "æäģļ多åįŧ",
+ "For HS256/HS512 algorithms, provide the shared secret key. For ES256/ES512 algorithms, provide the pkcs8 PEM-formatted private key.": "寚äē HS256/HS512 įŽæŗīŧ蝎æäžå
ąäēĢå¯éĨã寚äē ES256/ES512 įŽæŗīŧ蝎æäž pkcs8 PEM æ ŧåŧįį§éĨã",
"Forces the file to be synced when opened.": "æåŧæäģļæļåŧēåļåæĨč¯Ĩæäģļ ",
"Fresh Start Wipe": "å
¨æ°åŧå§æ¸
é¤",
+ "Furthermore, if conflicts are already present in the server data, they will be synchronised to this device as they are, and you will need to resolve them locally.": "æ¤å¤īŧåĻææåĄå¨æ°æŽä¸åˇ˛åå¨å˛įĒīŧåŽäģŦäŧåæ ˇåæĨå°æŦ莞å¤īŧäŊ éčĻ卿Ŧ莞å¤ä¸č§Ŗåŗã",
"Garbage Collection cancelled by user.": "፿ˇåˇ˛åæļååžåæļã",
"Garbage Collection completed. Deleted chunks: ${deletedChunks} / ${totalChunks}. Time taken: ${seconds} seconds.": "ååžåæļåŽæã厞å é¤ chunksīŧ${deletedChunks} / ${totalChunks}ãčæļīŧ${seconds} į§ã",
"Garbage Collection Confirmation": "ååžåæļįĄŽčŽ¤",
@@ -166,55 +336,119 @@
"Garbage Collection: Found ${unusedChunks} unused chunks to delete.": "ååžåæļīŧåį° ${unusedChunks} ä¸ĒæĒäŊŋį¨į chunks åž
å é¤ã",
"Garbage Collection: Scanned ${scanned} / ~${docCount}": "ååžåæļīŧ厞æĢæ ${scanned} / ~${docCount}",
"Garbage Collection: Scanning completed. Total chunks: ${totalChunks}, Used chunks: ${usedChunks}": "ååžåæļīŧæĢæåŽæãæģ chunksīŧ${totalChunks}īŧ厞äŊŋ፠chunksīŧ${usedChunks}",
+ "Gathering information...": "æŖå¨æļéäŋĄæ¯âĻ",
+ "Generate Random ID": "įæéæē ID",
+ "Group ID": "įģ ID",
"Handle files as Case-Sensitive": "å°æäģļč§ä¸ēåēå大å°å",
+ "Have you created a backup before proceeding?": "įģ§įģäšåīŧäŊ å¤äģŊäēåīŧ",
+ "Hidden file synchronization have been temporarily disabled. Please enable them after the fetching, if you need them.": "éčæäģļåæĨ厞čĸĢ临æļåį¨ãčˇååŽæååĻéäŊŋį¨īŧ蝎鿰å¯į¨ã",
"Hidden Files": "éčæäģļ",
"Hide completely": "åŽå
¨éč",
+ "Hide not applicable items": "éčä¸éį¨éĄš",
+ "Hide password": "éčå¯į ",
+ "Higher (${local} > ${remote})": "čžéĢīŧ${local} > ${remote}īŧ",
+ "Highlight diff": "éĢäēŽåˇŽåŧ",
+ "How should files that were deleted on other devices be handled?\n\n- **${NEWER_CLEANUP}**: Delete local files if they were deleted on remote.\n This is useful if you want to keep your vault clean and consistent across devices, but please make sure you have a backup if there is already any important data in your vault.\n- **${NEWER_SYNC_ALL}**: Recreate remote files even if they were deleted on remote.\n This option will keep all your local files, but it may cause duplicates if there are files that exist on local but not on remote. You can clean up these duplicates manually after the synchronisation.": "å
ļäģ莞å¤ä¸å é¤įæäģļåēåĻäŊå¤įīŧ\n\n- **${NEWER_CLEANUP}**īŧåĻææäģļå¨čŋį¨čĸĢå é¤īŧåå 餿Ŧå°æäģļã\n åĻæäŊ å¸æčŽŠå莞å¤ä¸įåēäŋææ´æ´ä¸č´īŧčŋäŧåžæį¨ãäŊåĻæåēä¸åˇ˛æéčĻæ°æŽīŧ蝎åĄåŋ
å
ååĨŊå¤äģŊã\n- **${NEWER_SYNC_ALL}**īŧåŗäŊŋæäģļå¨čŋį¨åˇ˛čĸĢå é¤īŧäšéæ°ååģēčŋ፿äģļã\n æ¤é饚äŧäŋįäŊ ææįæŦå°æäģļīŧäŊåĻææäēæäģļåĒåå¨äēæŦå°čä¸åå¨äēčŋį¨īŧå¯čŊäŧäē§įé夿äģļãåæĨåŽæåīŧäŊ å¯äģĨæå¨æ¸
įčŋäēé夿äģļã",
"How to display network errors when the sync server is unreachable.": "åŊåæĨæåĄå¨ä¸å¯čžžæļīŧåĻäŊæžį¤ēįŊįģé蝝ã",
+ "How to handle extra existing local files?": "åĻäŊå¤įæŦå°åˇ˛æįå¤äŊæäģļīŧ",
"How would you like to configure the connection to your server?": "äŊ 叿åĻäŊé
įŊŽä¸æåĄå¨įčŋæĨīŧ",
+ "However, This should not be enabled if you want to increase your secrecy more.": "ä¸čŋīŧåĻæäŊ 叿čŋ䏿ĨæéĢäŋ坿§īŧåä¸åēå¯į¨æ¤éĄšã",
"I am adding a device to an existing synchronisation setup": "æčĻå°čŽžå¤å å
Ĩį°æåæĨé
įŊŽ",
"I am ready, unlock the database": "æåˇ˛åå¤åĨŊīŧįĢåŗč§Ŗéæ°æŽåē",
"I am setting this up for the first time": "ææ¯įŦŦ䏿ŦĄčŋčĄčŽžįŊŽ",
+ "I am setting up a new server for the first time / I want to reset my existing server.": "ææ¯įŦŦ䏿ŦĄčŽžįŊŽæ°æåĄå¨īŧææŗéįŊŽį°ææåĄå¨ã",
+ "I am unable to create a backup of my Vault.": "ææ æŗå¤äģŊæįåēã",
+ "I am unable to create a backup of my Vaults.": "ææ æŗå¤äģŊæįåēã",
+ "I have created a backup of my Vault.": "æåˇ˛å¤äģŊæįåēã",
"I have made a backup, mark this device as resolved": "æåˇ˛åŽæå¤äģŊīŧå°æ¤čŽžå¤æ 莰ä¸ēâåˇ˛įĄŽčŽ¤â",
"I know my server details, let me enter them": "æįĨéæåĄå¨č¯Ļæ
īŧ莊ææå¨čžå
Ĩ",
+ "I understand that all changes made on other smartphones or computers possibly could be lost.": "ææįŊå¨å
ļäģææēæįĩč䏿åįæ´æšå¯čŊäŧä¸ĸå¤ąã",
+ "I understand that other devices will no longer be able to synchronise, and will need to be reset the synchronisation information.": "ææįŊå
ļäģ莞å¤å°æ æŗįģ§įģåæĨīŧéčĻéįŊŽåæĨäŋĄæ¯ã",
+ "I understand that this action is irreversible once performed.": "ææįŊæ¤æäŊ䏿Ͽ§čĄäžŋä¸å¯æ¤éã",
+ "I understand the risks and will proceed without a backup.": "ææįŊéŖéŠīŧå°å¨æ˛Ąæå¤äģŊįæ
åĩä¸įģ§įģã",
+ "I Understand, Overwrite Server": "ææįŊīŧčĻįæåĄå¨",
+ "If \"Auto Start P2P Connection\" is enabled, the P2P connection will be started automatically when the plug-in launches.": "åĻæå¯į¨äēãčĒå¨å¯å¨ P2P čŋæĨãīŧæäģļå¯å¨æļäŧčĒå¨åģēįĢ P2P čŋæĨã",
"If disabled(toggled), chunks will be split on the UI thread (Previous behaviour).": "åĻæįĻį¨(åæĸ),chunks å°å¨ UI įēŋį¨ä¸åå˛(äģĨåįčĄä¸ē)",
"If enabled per-filed efficient customization sync will be used. We need a small migration when enabling this. And all devices should be updated to v0.23.18. Once we enabled this, we lost a compatibility with old versions.": "åĻæå¯į¨,å°äŊŋį¨åēäēæäģļįãéĢæįčĒåŽäšåæĨãå¯į¨æ¤åčŊéčĻčŋčĄä¸æŦĄå°įčŋį§ģãææčŽžå¤éŊåēæ´æ°å° v0.23.18ã䏿Ļå¯į¨æ¤åčŊ,æäģŦå°å¤ąåģ䏿§įæŦįå
ŧ厚æ§",
+ "If enabled, changes will be automatically broadcasted to all connected peers. Notified peers will start fetching the changes.": "å¯į¨åīŧæ´æšå°čĒå¨åšŋæį쿿厞čŋæĨįå¯šįæšãæļå°éįĨįå¯šįæšå°åŧå§čˇåčŋäēæ´æšã",
"If enabled, chunks will be split into no more than 100 items. However, dedupe is slightly weaker.": "åĻæå¯į¨,æ°æŽåå°čĸĢå垿ä¸čļ
čŋ 100 饚ãäŊæ¯,åģéææäŧį¨åŧą",
+ "If enabled, efficient per-file customization sync will be used. A minor migration is required when enabling this feature, and all devices must be updated to v0.23.18. Enabling this feature will result in losing compatibility with older versions.": "å¯į¨åīŧå°äŊŋ፿´éĢæįéæäģļčĒåŽäšåæĨãå¯į¨æ¤åčŊéčĻčŋčĄä¸æŦĄå°č§æ¨Ąčŋį§ģīŧåšļ䏿æčŽžå¤éŊåŋ
éĄģæ´æ°å° v0.23.18ãå¯į¨æ¤åčŊåīŧå°å¤ąåģ䏿§įæŦįå
ŧ厚æ§ã",
"If enabled, newly created chunks are temporarily kept within the document, and graduated to become independent chunks once stabilised.": "åĻæå¯į¨,æ°ååģēįæ°æŽåå°ææļäŋįå¨ææĄŖä¸,åšļå¨į¨ŗåŽåæä¸ēįŦįĢæ°æŽå",
"If enabled, the â icon will be shown inside the status instead of the file warnings banner. No details will be shown.": "åĻæå¯į¨īŧįļææ å
å°æžį¤ē â åžæ īŧčéæäģļčĻ忍Ēåš
īŧä¸äŧæžį¤ēäģģäŊč¯ĻįģäŋĄæ¯ã",
+ "If enabled, the file properties will be encrypted in the remote database. This is useful for protecting sensitive information in file paths, sizes, and IDs of its chunks. If you are using V1 E2EE, this only obfuscates the file path.": "å¯į¨åīŧæäģļåąæ§å°å¨čŋ፿°æŽåēä¸å å¯ãčŋæåŠäēäŋæ¤æäģļ莝åžã大å°äģĨåå
ļæ°æŽå ID įææäŋĄæ¯ãåĻæäŊ äŊŋį¨įæ¯ V1 E2EEīŧååĒäŧæˇˇæˇæäģļ莝åžã",
"If enabled, the file under 1kb will be processed in the UI thread.": "åĻæå¯į¨,å°äē 1kb įæäģļå°å¨ UI įēŋį¨ä¸å¤į",
+ "If enabled, the forcePathStyle option will be used for bucket operations.": "å¯į¨åīŧå卿ĄļæäŊå°äŊŋ፠forcePathStyle é饚ã",
"If enabled, the notification of hidden files change will be suppressed.": "åĻæå¯į¨,å°ä¸åéįĨéčæäģļčĸĢæ´æš",
+ "If enabled, the P2P connection will be automatically started when the application launches.": "å¯į¨åīŧåēį¨å¯å¨æļå°čĒå¨åŧå§ P2P čŋæĨã",
+ "If enabled, the plugin will not attempt to connect to the remote database even if the chunk was not found locally.": "å¯į¨åīŧåŗäŊŋæŦ尿ǿžå°æ°æŽåīŧæäģļäšä¸äŧå°č¯čŋæĨčŋ፿°æŽåēã",
+ "If enabled, the request API will be used to avoid `inevitable` CORS problems. This is a workaround and may not work in all cases. PLEASE READ THE DOCUMENTATION BEFORE USING THIS OPTION. This is a less-secure option.": "å¯į¨åīŧå°äŊŋ፠Request API æĨéŋå
`inevitable` CORS éŽéĸãčŋåĒæ¯ä¸į§åéæšæŗīŧåšļé卿ææ
åĩä¸éŊææã蝎åĄåŋ
å¨äŊŋ፿¤é饚åé
č¯ģææĄŖãčŋæ¯ä¸ä¸ĒåŽå
¨æ§čžäŊįé饚ã",
"If this enabled, all chunks will be stored with the revision made from its content. (Previous behaviour)": "åĻæå¯į¨,ææ chunks å°äŊŋ፿ šæŽå
ļå
厚įæįäŋŽčŽĸįæŦåå¨(äģĨåįčĄä¸ē)",
"If this enabled, All files are handled as case-Sensitive (Previous behaviour).": "åĻæå¯į¨,æææäģļéŊå°č§ä¸ēåēå大å°å(äģĨåįčĄä¸ē)",
"If this enabled, chunks will be split into semantically meaningful segments. Not all platforms support this feature.": "åĻæå¯į¨æ¤åčŊ,æ°æŽåå°čĸĢå垿å
ˇæč¯äšæäšįæŽĩčŊãåšļéææåšŗå°éŊæ¯ææ¤åčŊ",
+ "If this enabled, JWT will be used for authentication.": "å¯į¨åå°äŊŋ፠JWT 莤č¯ã",
"If this is set, changes to local files which are matched by the ignore files will be skipped. Remote changes are determined using local ignore files.": "åĻæčŽžįŊŽäēæ¤éĄš,ä¸åŋŊįĨæäģļåšé
įæŦå°æäģļįæ´æšå°čĸĢ莺čŋãčŋ፿´æšäŊŋ፿Ŧå°åŋŊįĨæäģļįĄŽåŽ",
"If this option is enabled, PouchDB will hold the connection open for 60 seconds, and if no change arrives in that time, close and reopen the socket, instead of holding it open indefinitely. Useful when a proxy limits request duration but can increase resource usage.": "åĻæå¯į¨æ¤é饚,PouchDB å°äŋæčŋæĨæåŧ 60 į§,åϿ卿¤æļé´å
æ˛Ąææ´æšå°čžž,åå
ŗéåšļéæ°æåŧåĨæĨå,č䏿¯æ éæäŋææåŧãåŊäģŖįéåļč¯ˇæąæįģæļé´æļæį¨,äŊå¯čŊäŧåĸå čĩæēäŊŋį¨Ã",
+ "If you cannot avoid CORS issues, you might want to try this option. It uses Obsidian's internal API to communicate with the CouchDB server. Not compliant with web standards, but works. Note that this might break in future Obsidian versions.": "åĻææ æŗéŋåŧ CORS éŽéĸīŧå¯äģĨč¯č¯æ¤é饚ãåŽäŊŋ፠Obsidian įå
é¨ API ä¸ CouchDB æåĄå¨éäŋĄãä¸įŦĻå Web æ åīŧäŊå¯į¨ãč¯ˇæŗ¨æīŧæĒæĨį Obsidian įæŦå¯čŊäŧäŊŋå
ļå¤ąæã",
+ "If you cannot avoid CORS issues, you might want to try this option. It uses Obsidian's internal API to communicate with the S3 server. Not compliant with web standards, but works. Note that this might break in future Obsidian versions.": "åĻææ æŗéŋåŧ CORS éŽéĸīŧå¯äģĨč¯č¯æ¤é饚ãåŽäŊŋ፠Obsidian įå
é¨ API ä¸ S3 æåĄå¨éäŋĄãä¸įŦĻå Web æ åīŧäŊå¯į¨ãč¯ˇæŗ¨æīŧæĒæĨį Obsidian įæŦå¯čŊäŧäŊŋå
ļå¤ąæã",
+ "If you have unsynchronised changes in your Vault on this device, they will likely diverge from the server's versions after the reset. This may result in a large number of file conflicts.": "åĻææŦ莞å¤įåē䏿æĒåæĨįæ´æšīŧéįŊŽååŽäģŦåžå¯čŊ䏿åĄå¨ä¸įįæŦäē§įåæ§ãčŋå¯čŊå¯ŧč´å¤§éæäģļå˛įĒã",
+ "If you reached the payload size limit when using IBM Cloudant, please decrease batch size and batch limit to a lower value.": "åĻæå¨äŊŋ፠IBM Cloudant æļčžžå°čŊŊčˇå¤§å°ä¸éīŧč¯ˇææšé大å°åæšéä¸éč°äŊã",
+ "If you understand the risks and still wish to proceed, select so.": "åĻæäŊ æįŊéŖéŠäŊäģ叿įģ§įģīŧč¯ˇéæŠæ¤éĄšã",
+ "If you want to store the data in a specific folder within the bucket, you can specify a folder prefix here. Otherwise, leave it blank to store data at the root of the bucket.": "åĻææŗææ°æŽåæžå¨å卿Ąļå
įæåŽæäģļ多ä¸īŧå¯äģĨ卿¤æåŽæäģļ多åįŧãåĻåįįŠēīŧæ°æŽå°åæžå¨å卿Ąļæ šįŽåŊã",
+ "If you want to use `LiveSync`, you should broadcast changes. All `watching` peers which detects this will start the replication for fetching.": "åĻæčĻäŊŋ፠`LiveSync`īŧäŊ åēåŊåšŋæåæ´ãæææŖæĩå°åæ´į `watching` å¯šįæšéŊäŧå¯å¨å¤åļäģĨčˇåæ°æŽã",
+ "Ignore": "åŋŊįĨ",
"Ignore and Proceed": "åŋŊįĨåšļįģ§įģ",
"Ignore files": "åŋŊįĨæäģļ",
"Ignore patterns": "åŋŊįĨæ¨Ąåŧ",
"Import connection": "å¯ŧå
ĨčŋæĨ",
+ "IMPORTANT": "éčĻ",
+ "In most cases, you should stick with the default algorithm (${algorithm}), This setting is only required if you have an existing Vault encrypted in a different format.": "大夿°æ
åĩä¸īŧäŊ åēåŊæ˛ŋį¨éģčŽ¤įŽæŗīŧ${algorithm}īŧãåĒæåŊäŊ æäģĨå
ļäģæ ŧåŧå å¯įį°æåēæļīŧæéčĻæ¤éĄščŽžįŊŽã",
+ "In this scenario, Self-hosted LiveSync will recreate metadata for every file and deliberately generate conflicts. Where the file content is identical, these conflicts will be resolved automatically.": "卿¤æ
åŊĸä¸īŧSelf-hosted LiveSync äŧä¸ēæ¯ä¸Ēæäģļéåģēå
æ°æŽåšļææåļé å˛įĒãčĨæäģļå
厚åŽå
¨į¸åīŧčŋäēå˛įĒå°čĸĢčĒå¨č§Ŗåŗã",
+ "Incoming:": "æĨæļīŧ",
"Incubate Chunks in Document": "å¨ææĄŖä¸åĩåå",
+ "Initial Action": "åå§æäŊ",
"Initialise all journal history, On the next sync, every item will be received and sent.": "åå§åæææĨåŋåå˛ã䏿ŦĄåæĨæļīŧææéĄšįŽéŊäŧéæ°æĨæļåšļåéã",
+ "Initialise journal received history. On the next sync, every item except this device sent will be downloaded again.": "åå§åæĨåŋį厞æĨæļåå˛ã䏿ŦĄåæĨæļīŧ餿Ŧ莞å¤åˇ˛åéį饚å¤īŧå
ļäŊéŊäŧéæ°ä¸čŊŊã",
+ "Initialise journal sent history. On the next sync, every item except this device received will be sent again.": "åå§åæĨåŋį厞åéåå˛ã䏿ŦĄåæĨæļīŧ餿Ŧ莞å¤åˇ˛æĨæļį饚å¤īŧå
ļäŊéŊäŧéæ°åéã",
+ "Internal timestamp for resolving compatible tweak mismatches.": "į¨äēč§Ŗåŗå
ŧ厚įč°æ´åŧä¸ä¸č´éŽéĸįå
鍿ļé´æŗã",
"Interval (sec)": "é´é(į§)",
+ "INVERTED": "厞åčŊŦ",
+ "Issue detection log:": "éŽéĸæŖæĩæĨåŋīŧ",
+ "It is strongly advised to create a backup before proceeding. Continuing without a backup may lead to data loss.": "åŧēįåģē莎å¨įģ§įģäšåå
åå¤äģŊãæ˛Ąæå¤äģŊįģ§įģå¯čŊå¯ŧč´æ°æŽä¸ĸå¤ąã",
+ "Just for a minute, please!": "蝎į¨įä¸åéīŧ",
+ "JWT (JSON Web Token) authentication allows you to securely authenticate with the CouchDB server using tokens. Ensure that your CouchDB server is configured to accept JWTs and that the provided key and settings match the server's configuration. Incidentally, I have not verified it very thoroughly.": "JWTīŧJSON Web TokenīŧčēĢäģŊéĒč¯čŽŠäŊ å¯äģĨį¨äģ¤įåŽå
¨å°éčŋ CouchDB æåĄå¨éĒč¯čēĢäģŊãč¯ˇįĄŽäŋäŊ į CouchDB æåĄå¨åˇ˛é
įŊŽä¸ēæĨå JWTīŧ䏿æäžįå¯éĨå莞įŊŽä¸æåĄå¨é
įŊŽį¸įŦĻãéĄēå¸Ļ䏿īŧæåšļæ˛Ąæé常åŊģåēå°éĒč¯čŋåŽã",
+ "JWT Algorithm": "JWT įŽæŗ",
+ "JWT Expiration Duration (minutes)": "JWT æææīŧåéīŧ",
+ "JWT Key": "JWT å¯éĨ",
+ "JWT Key ID (kid)": "JWT å¯éĨ IDīŧkidīŧ",
+ "JWT Subject (sub)": "JWT ä¸ģäŊīŧsubīŧ",
"K.exp": "åŽéĒæ§",
"K.long_p2p_sync": "%{title_p2p_sync} (%{exp})",
"K.P2P": "%{Peer}-to-%{Peer}",
"K.Peer": "Peer",
"K.ScanCustomization": "æĢæčĒåŽäš",
"K.short_p2p_sync": "P2PåæĨ(%{exp})",
- "K.title_p2p_sync": "Peer-to-PeeråæĨ",
+ "K.title_p2p_sync": "Peer-to-Peer åæĨ",
"Keep empty folder": "äŋįįŠēæäģļ多",
- "lang_def": "Default",
+ "Keep local files even if deleted on remote": "åŗäŊŋčŋį¨åˇ˛å é¤īŧäšäŋįæŦå°æäģļ",
+ "Keep local files even if not on remote": "åŗäŊŋčŋį¨ä¸åå¨īŧäšäŋįæŦå°æäģļ",
+ "Keep replication active in the background": "å¨åå°äŋæå¤åļčŋčĄ",
+ "Key ID": "å¯éĨ ID",
+ "Keypair or pre-shared key": "å¯éĨ寚æéĸå
ąäēĢå¯éĨ",
+ "lang_def": "éģ莤",
"lang-de": "Deutsche",
"lang-def": "%{lang_def}",
"lang-es": "EspaÃąol",
"lang-fr": "Français",
+ "lang-he": "å¸äŧ¯æĨč¯",
"lang-ja": "æĨæŦčĒ",
"lang-ko": "íęĩė´",
"lang-ru": "Đ ŅŅŅĐēиК",
"lang-zh": "įŽäŊ䏿",
"lang-zh-tw": "įšéĢ䏿",
+ "Last tweak modified timestamp": "č°æ´åŧæåäŋŽæšæļé´æŗ",
"Later": "į¨å",
"Limit: {datetime} ({timestamp})": "éåļīŧ{datetime}īŧ{timestamp}īŧ",
+ "live": "åŽæļ",
"LiveSync could not handle multiple vaults which have same name without different prefix, This should be automatically configured.": "LiveSync æ æŗå¤įå
ˇæį¸ååį§°äŊæ˛Ąæä¸ååįŧįå¤ä¸Ēåēãčŋåēč¯ĨčĒå¨é
įŊŽ",
"liveSyncReplicator.beforeLiveSync": "å¨LiveSyncå,å
å¯å¨OneShot䏿ŦĄ...",
"liveSyncReplicator.cantReplicateLowerValue": "æäģŦæ æŗå¤åļæ´å°įåŧ",
@@ -227,6 +461,7 @@
"liveSyncReplicator.liveSyncBegin": "LiveSync åŧå§...",
"liveSyncReplicator.lockRemoteDb": "éåŽčŋ፿°æŽåēäģĨ鞿ĸæ°æŽæå",
"liveSyncReplicator.markDeviceResolved": "å°æ¤čŽžå¤æ 莰ä¸ēâ厞觪åŗâ",
+ "liveSyncReplicator.mismatchedTweakDetected": "æŖæĩå°čŽžå¤äšé´įé
įŊŽåå¨ä¸ä¸č´ãæ§čĄä¸æŦĄæå¨å¤åļå°å°č¯č§Ŗåŗæ¤éŽéĸã",
"liveSyncReplicator.oneShotSyncBegin": "OneShotåæĨåŧå§...(${syncMode})",
"liveSyncReplicator.remoteDbCorrupted": "čŋ፿°æŽåēčžæ°æåˇ˛æå,č¯ˇįĄŽäŋ厞åŽčŖ
ææ°įæŦįself-hosted-livesync",
"liveSyncReplicator.remoteDbCreatedOrConnected": "čŋ፿°æŽåē厞ååģēæčŋæĨ",
@@ -241,6 +476,8 @@
"liveSyncSetting.originalValue": "åå§åŧīŧ${value}",
"liveSyncSetting.valueShouldBeInRange": "åŧåēč¯Ĩå¨ ${min} < value < ${max} äšé´",
"liveSyncSettings.btnApply": "åēį¨",
+ "Local Database Tweak": "æŦå°æ°æŽåē垎č°",
+ "Local only": "äģ
æŦå°",
"Lock": "éåŽ",
"Lock Server": "éåŽæåĄå¨",
"Lock the remote server to prevent synchronization with other devices.": "éåŽčŋį̝æåĄå¨īŧäģĨéģæĸå
ļäģ莞å¤įģ§įģåæĨã",
@@ -249,17 +486,27 @@
"logPane.pause": "æå",
"logPane.title": "Self-hosted LiveSync æĨåŋ",
"logPane.wrap": "čĒ卿ĸčĄ",
+ "Longest chunk line threshold value (Not Configurable from the UI Now).": "æéŋæ°æŽåčĄįéåŧīŧįŽåæ æŗå¨įéĸä¸é
įŊŽīŧã",
+ "Lower (${local} < ${remote})": "čžäŊīŧ${local} < ${remote}īŧ",
+ "Maintenance Commands": "įģ´æ¤åŊäģ¤",
+ "Maintenance mode": "įģ´æ¤æ¨Ąåŧ",
"Maximum delay for batch database updating": "æšéæ°æŽåēæ´æ°įæå¤§åģļčŋ",
+ "Maximum file modification time for reflected file events": "å¯åæ æäģļäēäģļįæå¤§äŋŽæšæļé´",
"Maximum file size": "æå¤§æäģļ大å°",
"Maximum Incubating Chunk Size": "æå¤§åĩåå大å°",
"Maximum Incubating Chunks": "æå¤§åĩååæ°",
"Maximum Incubation Period": "æå¤§åĩåæ",
+ "Maximum request size for manually resending chunks": "æå¨éæ°åéæ°æŽåæļįæå¤§č¯ˇæąå¤§å°",
"MB (0 to disable).": "MB(0ä¸ēįĻį¨)",
+ "MB per request": "æ¯æŦĄč¯ˇæąį MB æ°",
+ "Memory cache": "å
åįŧå",
"Memory cache size (by total characters)": "å
åįŧå大å°(ææģåįŦĻæ°)",
"Memory cache size (by total items)": "å
åįŧå大å°(ææģéĄšįŽæ°)",
"Merge": "ååšļ",
+ "Minimum Chunk Size (Not Configurable from the UI Now).": "æå°æ°æŽå大å°īŧįŽåæ æŗå¨įéĸä¸é
įŊŽīŧã",
"Minimum delay for batch database updating": "æšéæ°æŽåēæ´æ°įæå°åģļčŋ",
"Minimum interval for syncing": "åæĨæå°é´é",
+ "Mixed": "æˇˇå",
"moduleCheckRemoteSize.logCheckingStorageSizes": "æŖå¨æŖæĨåå¨å¤§å°",
"moduleCheckRemoteSize.logCurrentStorageSize": "čŋį¨åå¨å¤§å°īŧ${measuredSize}",
"moduleCheckRemoteSize.logExceededWarning": "čŋį¨åå¨å¤§å°īŧ${measuredSize} čļ
čŋ ${notifySize}",
@@ -267,6 +514,8 @@
"moduleCheckRemoteSize.msgConfirmRebuild": "čŋå¯čŊéčĻä¸äēæļé´ãæ¨įįæŗį°å¨éåģēææå
厚åīŧ",
"moduleCheckRemoteSize.msgDatabaseGrowing": "**æ¨įæ°æŽåēæŖå¨å大īŧ** äŊåĢæ
åŋ,æäģŦį°å¨å¯äģĨč§ŖåŗåŽãå¨čŋį¨åå¨įŠēé´į¨åŽäšåčŋææļé´ã\n\n| æĩéå¤§å° | é
įŊŽå¤§å° |\n| --- | --- |\n| ${estimatedSize} | ${maxSize} |\n\n> [!MORE]-\n> åĻææ¨åˇ˛įģäŊŋį¨äēåžå¤åš´,æ°æŽåēä¸å¯čŊäŧ᧝ᴝæĒåŧį¨į chunksââäšå°ąæ¯ååžãå æ¤,æäģŦåģē莎éåģēææå
厚ãåŽå¯čŊäŧååžå°åžå¤ã\n>\n> åĻææ¨įåē厚éåĒæ¯å¨åĸå ,æåĨŊ卿´įæäģļåéåģēææå
厚ãåŗäŊŋæ¨ä¸ēäēå éčŋį¨å é¤äēæäģļ,Self-hosted LiveSync äšä¸äŧå é¤åŽé
æ°æŽãčŋ大č´[æææĄŖčްåŊ](https://github.com/vrtmrz/obsidian-livesync/blob/main/docs/tech_info.md)ã\n>\n> åĻææ¨ä¸äģæåĸå ,å¯äģĨå°éįĨéåļåĸå 100MBãåĻææ¨å¨čĒåˇąįæåĄå¨ä¸čŋčĄ,å°ąæ¯čŋį§æ
åĩãäŊæ¯,æåĨŊčŋæ¯ä¸æļå°éåģēææå
厚ã\n>\n\n> [!WARNING]\n> åĻææ¨æ§čĄéåģēææå
厚,č¯ˇįĄŽäŋææčŽžå¤éŊ厞åæĨãå°ŊįŽĄåĻæ¤,æäģļäŧå°Ŋå¯čŊå°ååšļ\n",
"moduleCheckRemoteSize.msgSetDBCapacity": "æäģŦå¯äģĨ莞įŊŽä¸ä¸Ēæå¤§æ°æŽåē厚éčĻå,**äģĨäžŋå¨čŋį¨åå¨įŠēé´čå°ŊåéåčĄå¨**ã\næ¨æŗå¯į¨čŋä¸ĒåčŊå?\n\n> [!MORE]-\n> - 0: ä¸čĻååå¨å¤§å°ã\n> åĻææ¨å¨čŋį¨åå¨(å°¤å
￝čĒæįŽĄ)䏿čļŗå¤įįŠēé´,忍迤ééĄšãæ¨å¯äģĨæå¨æŖæĨåå¨å¤§å°åšļéåģēã\n> - 800: åĻæčŋį¨åå¨å¤§å°čļ
čŋ 800MB åååēčĻåã\n> åĻææ¨äŊŋį¨įæ¯ fly.io(1GB éåļ) æ IBM Cloudant,忍迤é饚ã\n> - 2000: åĻæčŋį¨åå¨å¤§å°čļ
čŋ 2GB åååēčĻåã\n\nåĻæčžžå°éåļ,įŗģįģäŧčĻæąæäģŦéæĨåĸ大éåļ\n",
+ "moduleCheckRemoteSize.noticeExceeded": "čŋį̝åå¨äŊ᧝ä¸ē ${measuredSize}īŧ厞čļ
čŋé
įŊŽįæééåŧ ${notifySize}ã{HERE}",
+ "moduleCheckRemoteSize.noticeNotConfigured": "æĒé
įŊŽčŋį̝åå¨äŊ᧝æéã{HERE}",
"moduleCheckRemoteSize.option2GB": "2GB (æ å)",
"moduleCheckRemoteSize.option800MB": "800MB (Cloudant, fly.io)",
"moduleCheckRemoteSize.optionAskMeLater": "į¨åéŽæ",
@@ -274,6 +523,7 @@
"moduleCheckRemoteSize.optionIncreaseLimit": "åĸå å° ${newMax}MB",
"moduleCheckRemoteSize.optionNoWarn": "ä¸,č¯ˇæ°¸čŋä¸čĻčĻå",
"moduleCheckRemoteSize.optionRebuildAll": "įĢåŗéåģēææå
厚",
+ "moduleCheckRemoteSize.optionReview": "æĨįé饚",
"moduleCheckRemoteSize.titleDatabaseSizeLimitExceeded": "čŋį¨åå¨å¤§å°čļ
åēéåļ",
"moduleCheckRemoteSize.titleDatabaseSizeNotify": "莞įŊŽæ°æŽåē大å°éįĨ",
"moduleInputUIObsidian.defaultTitleConfirmation": "įĄŽčŽ¤",
@@ -294,6 +544,7 @@
"moduleLiveSyncMain.optionResumeAndRestart": "æĸå¤åšļéå¯ Obsidian",
"moduleLiveSyncMain.titleScramEnabled": "į´§æĨåæĸ厞å¯į¨",
"moduleLocalDatabase.logWaitingForReady": "įåž
å°ąįģĒ...",
+ "moduleLog.pathComponentTooLong": "æ¤čˇ¯åžä¸å
åĢčļ
čŋ ${maxBytes} ä¸Ē UTF-8 åčįæäģļææäģļ多åã卿äē Android å Linux æäģļįŗģįģä¸å¯čŊæ æŗæŖå¸¸åˇĨäŊã",
"moduleLog.showLog": "æžį¤ēæĨåŋ",
"moduleMigration.fix0256.buttons.checkItLater": "į¨åæŖæĨ",
"moduleMigration.fix0256.buttons.DismissForever": "æåˇ˛įģäŋŽå¤äēīŧä¸åč¯ĸéŽ",
@@ -328,23 +579,41 @@
"moduleMigration.titleCaseSensitivity": "大å°åæææ§",
"moduleObsidianMenu.replicate": "å¤åļ",
"More actions": "æ´å¤æäŊ",
+ "Mostly Complete: Decision Required": "åēæŦåŽæīŧéčĻäŊ åŗåŽ",
"Move remotely deleted files to the trash, instead of deleting.": "å°čŋį¨å é¤įæäģļį§ģčŗåæļįĢ,č䏿¯į´æĨå é¤",
+ "Move remotely deleted files to the trash, instead of deleting. (This setting is ineffective from Obsidian v1.7.2 onwards, as deletion always respects your Obsidian preferences.)": "å°čŋį¨å é¤įæäģļį§ģčŗåæļįĢīŧč䏿¯į´æĨå é¤ãīŧæ¤čŽžįŊŽčĒ Obsidian v1.7.2 čĩˇä¸åįæīŧå ä¸ēå 餿äŊå§įģéĩåžĒäŊ å¨ Obsidian ä¸įååĨŊ莞įŊŽãīŧ",
+ "My remote server is already set up. I want to join this device.": "æįčŋį̝æåĄå¨åˇ˛čŽžįŊŽåĨŊīŧææŗčŽŠæŦ莞å¤å å
Ĩã",
+ "Name": "åį§°",
"Network warning style": "įŊįģčĻåæ ˇåŧ",
+ "NEW": "æ°åĸ",
"New Remote": "æ°åģēčŋį̝",
+ "Newer (${diff})": "čžæ°īŧ${diff}īŧ",
+ "No checks have been performed yet.": "尿ǿ§čĄäģģäŊæŖæĨã",
"No connected device information found. Cancelling Garbage Collection.": "æĒæžå°åˇ˛čŋæĨ莞å¤įäŋĄæ¯ãæŖå¨åæļååžåæļã",
+ "No Connection": "æĒčŋæĨ",
+ "No devices available. Waiting for other devices to connect...": "æ˛Ąæå¯į¨į莞å¤ãæŖå¨įåž
å
ļäģ莞å¤čŋæĨâĻ",
+ "No Items.": "æ 饚įŽã",
"No limit configured": "æĒé
įŊŽéåļ",
+ "NO PREVIEW": "æ éĸč§",
"No, please take me back": "ä¸īŧčŋåä¸ä¸æĨ",
"Node ID": "čįš ID",
"Node Information Missing": "čįšäŋĄæ¯įŧēå¤ą",
"Non-Synchronising files": "ä¸åæĨįæäģļ",
"Normal Files": "æŽéæäģļ",
"Not all messages have been translated. And, please revert to \"Default\" when reporting errors.": "åšļéæææļæ¯éŊ厞įŋģč¯ã蝎卿Ĩåé蝝æļæĸå¤ä¸ē\"éģ莤\"",
+ "Not configured": "æĒé
įŊŽ",
+ "Not now": "æä¸",
+ "Note that the Group ID is not limited to the generated format; you can use any string as the Group ID.": "č¯ˇæŗ¨æīŧįģ ID ä¸éäēįæįæ ŧåŧīŧäŊ å¯äģĨäŊŋį¨äģģæåįŦĻ串äŊä¸ēįģ IDã",
+ "Note that you can generate a new Setup URI by running the \"Copy settings as a new Setup URI\" command in the command palette.": "č¯ˇæŗ¨æīŧäŊ å¯äģĨå¨åŊäģ¤éĸæŋä¸čŋčĄãCopy settings as a new Setup URIãåŊä줿Ĩįææ°į莞įŊŽ URIã",
"Notify all setting files": "éįĨææčŽžįŊŽæäģļ",
"Notify customized": "éįĨčĒåŽäščŽžįŊŽ",
"Notify when other device has newly customized.": "åŊå
ļäģčŽžå¤ææ°įčĒåŽäščŽžįŊŽæļéįĨ ",
"Notify when the estimated remote storage size exceeds on start up": "å¯å¨æļåŊäŧ°čŽĄįčŋį¨åå¨å¤§å°čļ
åēæļéįĨ",
+ "Now we can choose how to split the chunks; V3 is the most efficient. If you have troubled, please make this Default or Legacy.": "į°å¨å¯äģĨéæŠæ°æŽåįå垿šåŧīŧV3 æįæéĢãåĻæéå°éŽéĸīŧ蝎尿¤éĄščŽžä¸ē Default æ Legacyã",
"Number of batches to process at a time. Defaults to 40. Minimum is 2. This along with batch size controls how many docs are kept in memory at a time.": "䏿ŦĄå¤įįæšéæ°éãéģ莤ä¸ē 40ãæå°ä¸ē 2ãæ¤čŽžįŊŽä¸æšé大å°ä¸čĩˇæ§åļ䏿ŦĄå¨å
åä¸äŋįå¤å°ææĄŖ",
"Number of changes to sync at a time. Defaults to 50. Minimum is 2.": "䏿ŦĄåæĨįæ´æšæ°éãéģ莤ä¸ē 50ãæå°ä¸ē 2ã",
+ "Obfuscate Properties": "æˇˇæˇåąæ§",
+ "Obfuscating properties (e.g., path of file, size, creation and modification dates) adds an additional layer of security by making it harder to identify the structure and names of your files and folders on the remote server. This helps protect your privacy and makes it more difficult for unauthorized users to infer information about your data.": "æˇˇæˇåąæ§īŧäžåĻæäģļ莝åžã大å°ãååģēåäŋŽæšæĨæīŧéčŋ莊čŋį̝æåĄå¨ä¸æ´éžč¯åĢäŊ æäģļåæäģļ多įįģæä¸åį§°īŧåĸå äēä¸åąéĸå¤įåŽå
¨äŋéãčŋæåŠäēäŋæ¤äŊ įéį§īŧäščŽŠæĒææį¨æˇæ´éžæ¨æåēå
ŗäēäŊ æ°æŽįäŋĄæ¯ã",
"Obsidian version": "Obsidian įæŦ",
"obsidianLiveSyncSettingTab.btnApply": "åēį¨",
"obsidianLiveSyncSettingTab.btnCheck": "æŖæĨ",
@@ -407,6 +676,7 @@
"obsidianLiveSyncSettingTab.logPassphraseNotCompatible": "é蝝īŧå¯į ä¸čŋ፿åĄå¨ä¸å
ŧ厚īŧ蝎åæŦĄæŖæĨīŧ",
"obsidianLiveSyncSettingTab.logRebuildNote": "åæĨ厞įĻį¨,åĻæéčĻ,蝎čˇååšļéæ°å¯į¨",
"obsidianLiveSyncSettingTab.logSelectAnyPreset": "č¯ˇéæŠäģģä¸éĸ莞ã",
+ "obsidianLiveSyncSettingTab.logServerConfigurationCheck": "æåĄå¨é
įŊŽæŖæĨ",
"obsidianLiveSyncSettingTab.msgAreYouSureProceed": "æ¨įĄŽåŽčĻįģ§įģåīŧ",
"obsidianLiveSyncSettingTab.msgChangesNeedToBeApplied": "éčĻåē፿´æšīŧ",
"obsidianLiveSyncSettingTab.msgConfigCheck": "--é
įŊŽæŖæĨ--",
@@ -456,7 +726,7 @@
"obsidianLiveSyncSettingTab.okCorsOriginMatched": "â CORS æēæŖå¸¸",
"obsidianLiveSyncSettingTab.okCorsOrigins": "â cors.origins 莞įŊŽæŖįĄŽ",
"obsidianLiveSyncSettingTab.okEnableCors": "â httpd.enable_cors 莞įŊŽæŖįĄŽ",
- "obsidianLiveSyncSettingTab.okEnableCorsChttpd": "â chttpd.enable_cors is ok.",
+ "obsidianLiveSyncSettingTab.okEnableCorsChttpd": "â chttpd.enable_cors 莞įŊŽæŖįĄŽ",
"obsidianLiveSyncSettingTab.okMaxDocumentSize": "â couchdb.max_document_size 莞įŊŽæŖįĄŽ",
"obsidianLiveSyncSettingTab.okMaxRequestSize": "â chttpd.max_http_request_size 莞įŊŽæŖįĄŽ",
"obsidianLiveSyncSettingTab.okRequireValidUser": "â chttpd.require_valid_user 莞įŊŽæŖįĄŽ",
@@ -483,6 +753,7 @@
"obsidianLiveSyncSettingTab.panelSetup": "莞įŊŽ",
"obsidianLiveSyncSettingTab.serverVersion": "æåĄå¨äŋĄæ¯: ${info}",
"obsidianLiveSyncSettingTab.titleActiveRemoteServer": "æ´ģå¨čŋ፿åĄå¨",
+ "obsidianLiveSyncSettingTab.titleAdvancedSettings": "éĢįē§čŽžįŊŽ",
"obsidianLiveSyncSettingTab.titleAppearance": "å¤č§",
"obsidianLiveSyncSettingTab.titleConflictResolution": "å˛įĒå¤į",
"obsidianLiveSyncSettingTab.titleCouchDB": "CouchDB æåĄå¨",
@@ -490,11 +761,16 @@
"obsidianLiveSyncSettingTab.titleEncryptionNotEnabled": "å°æĒå¯į¨å å¯",
"obsidianLiveSyncSettingTab.titleEncryptionPassphraseInvalid": "å å¯å¯į į蝿 æ",
"obsidianLiveSyncSettingTab.titleExtraFeatures": "å¯į¨éĸå¤åéĢįē§åčŊ",
+ "obsidianLiveSyncSettingTab.titleExtraFeaturesGroup": "æŠåąåčŊ",
+ "obsidianLiveSyncSettingTab.titleExtraMenus": "æŠåąčå",
"obsidianLiveSyncSettingTab.titleFetchConfig": "čˇåé
įŊŽ",
"obsidianLiveSyncSettingTab.titleFetchConfigFromRemote": "äģčŋ፿åĄå¨čˇåé
įŊŽ",
"obsidianLiveSyncSettingTab.titleFetchSettings": "čˇå莞įŊŽ",
+ "obsidianLiveSyncSettingTab.titleHelpAndInformation": "帎åŠä¸äŋĄæ¯",
+ "obsidianLiveSyncSettingTab.titleHelpAndTroubleshooting": "帎åŠä¸įéžč§Ŗį",
"obsidianLiveSyncSettingTab.titleHiddenFiles": "éčæäģļ",
"obsidianLiveSyncSettingTab.titleLogging": "æĨåŋ",
+ "obsidianLiveSyncSettingTab.titleMaintenanceAndRecovery": "įģ´æ¤ä¸æĸå¤",
"obsidianLiveSyncSettingTab.titleMinioS3R2": "MinIOãS3ãR2",
"obsidianLiveSyncSettingTab.titleNotification": "éįĨ",
"obsidianLiveSyncSettingTab.titleOnlineTips": "å¨įēŋæį¤ē",
@@ -504,6 +780,7 @@
"obsidianLiveSyncSettingTab.titleRemoteServer": "čŋį̝æåĄå¨",
"obsidianLiveSyncSettingTab.titleReset": "éįŊŽ",
"obsidianLiveSyncSettingTab.titleSetupOtherDevices": "莞įŊŽå
ļäģ莞å¤",
+ "obsidianLiveSyncSettingTab.titleSynchronisation": "åæĨ",
"obsidianLiveSyncSettingTab.titleSynchronizationMethod": "åæĨæšåŧ",
"obsidianLiveSyncSettingTab.titleSynchronizationPreset": "åæĨéĸ莞",
"obsidianLiveSyncSettingTab.titleSyncSettings": "åæĨ莞įŊŽ",
@@ -511,16 +788,30 @@
"obsidianLiveSyncSettingTab.titleUpdateThinning": "æ´æ°į˛žįŽ",
"obsidianLiveSyncSettingTab.warnCorsOriginUnmatched": "â CORS æēä¸åšé
{from}->{to}",
"obsidianLiveSyncSettingTab.warnNoAdmin": "â æ¨æ˛ĄæįŽĄįåæé",
+ "Of course, we can back up the data before proceeding.": "åŊįļīŧæäģŦå¯äģĨå
å¤äģŊæ°æŽåįģ§įģã",
+ "Off": "å
ŗé",
"Ok": "įĄŽåŽ",
"Old Algorithm": "æ§įŽæŗ",
+ "Older (${diff})": "čžæ§īŧ${diff}īŧ",
"Older fallback (Slow, W/O WebAssembly)": "æ§įåéīŧčžæ
ĸīŧæ WebAssemblyīŧ",
+ "On": "åŧå¯",
+ "On the source device, from the command palette, run the 'Show settings as a QR code' command.": "卿ē莞å¤ä¸īŧäģåŊäģ¤éĸæŋčŋčĄãShow settings as a QR codeãåŊäģ¤ã",
+ "On the source device, open Obsidian.": "卿ē莞å¤ä¸æåŧ Obsidianã",
+ "On this device, please keep this Vault open.": "蝎卿Ŧ莞å¤ä¸äŋææ¤åēå¤äēæåŧįļæã",
+ "On this device, switch to the camera app or use a QR code scanner to scan the displayed QR code.": "卿Ŧ莞å¤ä¸åæĸå°į¸æēåēį¨īŧæäŊŋį¨äēįģ´į æĢæå¨æĢææžį¤ēįäēįģ´į ã",
"Open": "æåŧ",
+ "Open connection": "æåŧčŋæĨ",
+ "Open P2P Setup...": "æåŧ P2P 莞įŊŽâĻ",
"Open the dialog": "æåŧå¯šč¯æĄ",
+ "Other files": "å
ļäģæäģļ",
"Overwrite": "čĻį",
+ "Overwrite all with remote files": "å
¨é¨į¨čŋ፿äģļčĻį",
"Overwrite patterns": "čĻ῍Ąåŧ",
"Overwrite remote": "čĻįčŋį̝",
"Overwrite remote with local DB and passphrase.": "äŊŋ፿Ŧå°æ°æŽåēåå¯į įč¯čĻįčŋį̝ã",
"Overwrite Server Data with This Device's Files": "፿ŦčŽžå¤æäģļčĻįæåĄå¨æ°æŽ",
+ "P2P Configuration": "P2P é
įŊŽ",
+ "P2P Status": "P2P įļæ",
"P2P.AskPassphraseForDecrypt": "čŋį¨å¯šįæšå
ąäēĢäēé
įŊŽīŧ蝎čžå
Ĩå¯į įč¯äģĨč§Ŗå¯é
įŊŽ",
"P2P.AskPassphraseForShare": "čŋį¨å¯šįæšč¯ˇæąæ¤čŽžå¤é
įŊŽīŧ蝎čžå
Ĩå¯į įč¯äģĨå
ąäēĢé
įŊŽãäŊ å¯äģĨéčŋåæļæ¤å¯šč¯æĄæĨåŋŊįĨæ¤č¯ˇæą",
"P2P.DisabledButNeed": "%{title_p2p_sync} 厞įĻį¨ãäŊ įĄŽåŽčĻå¯į¨åŽåīŧ",
@@ -531,92 +822,127 @@
"P2P.Note.important_note": "The Experimental Implementation of the Peer-to-Peer Replicator.",
"P2P.Note.important_note_sub": "This feature is still in the experimental stage. Please be aware that this feature may not work as expected. Furthermore, it may have some bugs, security issues, and other issues. Please use this feature at your own risk. Please contribute to the development of this feature.",
"P2P.Note.Summary": "What is this feature? (and some important notes, please read once)",
- "P2P.NotEnabled": "%{title_p2p_sync} is not enabled. We cannot open a new connection.",
- "P2P.P2PReplication": "%{P2P} Replication",
+ "P2P.NotEnabled": "%{title_p2p_sync} æĒå¯į¨īŧæ æŗåģēįĢæ°čŋæĨã",
+ "P2P.P2PReplication": "%{P2P} å¤åļ",
"P2P.PaneTitle": "%{long_p2p_sync}",
- "P2P.ReplicatorInstanceMissing": "P2P Sync replicator is not found, possibly not have been configured or enabled.",
- "P2P.SeemsOffline": "Peer ${name} seems offline, skipped.",
- "P2P.SyncAlreadyRunning": "P2P Sync is already running.",
- "P2P.SyncCompleted": "P2P Sync completed.",
- "P2P.SyncStartedWith": "P2P Sync with ${name} have been started.",
+ "P2P.ReplicatorInstanceMissing": "æĒæžå° P2P åæĨå¤åļå¨īŧå¯čŊå°æĒé
įŊŽææĒå¯į¨ã",
+ "P2P.SeemsOffline": "å¯šįæš ${name} äŧŧäšå¤äēįĻģįēŋįļæīŧ厞莺čŋã",
+ "P2P.SyncAlreadyRunning": "P2P åæĨ厞å¨čŋčĄã",
+ "P2P.SyncCompleted": "P2P åæĨåˇ˛åŽæã",
+ "P2P.SyncStartedWith": "厞åŧå§ä¸ ${name} į P2P åæĨã",
"paneMaintenance.markDeviceResolvedAfterBackup": "蝎å¨åŽæå¤äģŊåå°æ¤čŽžå¤æ 莰ä¸ē厞å¤įã",
"paneMaintenance.remoteLockedAndDeviceNotAccepted": "čŋįĢ¯æ°æŽåē厞éåŽīŧ䏿¤čŽžå¤å°æĒčĸĢæĨåã",
"paneMaintenance.remoteLockedResolvedDevice": "čŋįĢ¯æ°æŽåē厞éåŽīŧäŊæ¤čŽžå¤åˇ˛čĸĢæĨåã",
"paneMaintenance.unlockDatabaseReady": "į°å¨å¯äģĨč§Ŗéæ°æŽåēã",
"Passphrase": "å¯į ",
+ "Passphrase is required.": "å¯į į蝿¯åŋ
åĄĢ饚ã",
"Passphrase of sensitive configuration items": "ææé
įŊŽéĄšįå¯į ",
"password": "å¯į ",
"Password": "å¯į ",
"Paste a connection string": "į˛č´´čŋæĨåįŦĻ串",
"Paste the Setup URI generated from one of your active devices.": "į˛č´´äģä¸å°åˇ˛å¨äŊŋį¨į莞å¤ä¸įæį Setup URIã",
+ "Path": "莝åž",
"Path Obfuscation": "čˇ¯åžæˇˇæˇ",
"Patterns to match files for overwriting instead of merging": "į¨äēåšé
éčĻįčéååšļæäģļ῍Ąåŧ",
"Patterns to match files for syncing": "į¨äēåšé
åæĨæäģļ῍Ąåŧ",
+ "Peer ID:": "å¯šįæš IDīŧ",
+ "Peer to Peer Replicator": "įšå¯šįšå¤åļå¨",
"Peer-to-Peer only": "äģ
Peer-to-Peer",
"Peer-to-Peer Synchronisation": "įšå¯šįšåæĨ",
+ "Peers": "å¯šįæš",
"Per-file-saved customization sync": "ææäģļäŋåįčĒåŽäšåæĨ",
"Perform": "æ§čĄ",
"Perform cleanup": "æ§čĄæ¸
į",
"Perform Garbage Collection": "æ§čĄååžåæļ",
"Perform Garbage Collection to remove unused chunks and reduce database size.": "æ§čĄååžåæļäģĨæ¸
įæĒäŊŋį¨į chunks åšļåå°æ°æŽåēäŊ᧝ã",
"Periodic Sync interval": "åŽæåæĨé´é",
+ "PERMANENT": "æ°¸äš
",
"Pick a file to resolve conflict": "éæŠčĻč§Ŗåŗå˛įĒįæäģļ",
+ "Pick a file to show history": "éæŠä¸ä¸ĒæäģļäģĨæžį¤ēåå˛čްåŊ",
+ "Please be aware that the End-to-End Encryption passphrase is not validated until the synchronisation process actually commences. This is a security measure designed to protect your data.": "č¯ˇæŗ¨æīŧį̝å°į̝å å¯å¯į įč¯čĻįå°åæĨčŋį¨įæŖåŧå§åæäŧčĸĢæ ĄéĒãčŋæ¯ä¸ēäŋæ¤äŊ įæ°æŽč莞莥įåŽå
¨æĒæŊã",
+ "Please configure your end-to-end encryption settings.": "蝎é
įŊŽäŊ įį̝å°į̝å å¯čŽžįŊŽã",
"Please disable 'Read chunks online' in settings to use Garbage Collection.": "čĻäŊŋį¨ååžåæļīŧ蝎å¨čŽžįŊŽä¸įĻį¨âRead chunks onlineâã",
"Please enable 'Compute revisions for chunks' in settings to use Garbage Collection.": "čĻäŊŋį¨ååžåæļīŧ蝎å¨čŽžįŊŽä¸å¯į¨âCompute revisions for chunksâã",
+ "Please enter the CouchDB server information below.": "蝎å¨ä¸æšåĄĢå CouchDB æåĄå¨äŋĄæ¯ã",
+ "Please enter the details required to connect to your S3/MinIO/R2 compatible object storage service.": "蝎åĄĢåčŋæĨå°äŊ į S3/MinIO/R2 å
ŧåŽšå¯ščąĄåå¨æåĄæéįäŋĄæ¯ã",
+ "Please enter the Peer-to-Peer Synchronisation information below.": "蝎å¨ä¸æšåĄĢåįšå¯šįšåæĨäŋĄæ¯ã",
+ "Please enter the Setup URI that was generated during server installation or on another device, along with the vault passphrase.": "蝎čžå
Ĩ卿åĄå¨åŽčŖ
æļæå¨å
ļäģ莞å¤ä¸įæį莞įŊŽ URIīŧäģĨååēįå¯į įč¯ã",
+ "Please follow the steps below to import settings from your existing device.": "蝎æäģĨ䏿ĨéǤäģäŊ į°æį莞å¤å¯ŧå
Ĩ莞įŊŽã",
+ "PLEASE NOTE": "č¯ˇæŗ¨æ",
"Please select 'Cancel' explicitly to cancel this operation.": "åĻéåæļæ¤æäŊīŧ蝎æįĄŽéæŠâåæļâã",
"Please select a method to import the settings from another device.": "č¯ˇéæŠä¸į§äģå
ļäģ莞å¤å¯ŧå
Ĩ莞įŊŽįæšæŗã",
+ "Please select an active P2P remote configuration to change P2P sync targets.": "č¯ˇéæŠä¸ä¸Ēįæį P2P čŋį̝é
įŊŽäģĨæ´æš P2P åæĨįŽæ ã",
"Please select an option to proceed": "č¯ˇéæŠä¸ä¸Ēé饚äģĨįģ§įģ",
+ "Please select the button below to restart and proceed to the data fetching confirmation.": "蝎įšåģ䏿šæéŽéå¯īŧįļåčŋå
Ĩæ°æŽčˇåįĄŽčŽ¤ã",
+ "Please select the button below to restart and proceed to the final confirmation.": "蝎įšåģ䏿šæéŽéå¯īŧįļåčŋå
ĨæįģįĄŽčŽ¤ã",
"Please select the type of server to which you are connecting.": "č¯ˇéæŠäŊ čĻčŋæĨįæåĄå¨įąģåã",
+ "Please select your situation.": "č¯ˇéæŠäŊ įæ
åĩã",
"Please set device name to identify this device. This name should be unique among your devices. While not configured, we cannot enable this feature.": "č¯ˇčŽžįŊŽčŽžå¤åį§°äģĨæ 蝿¤čŽžå¤ãč¯Ĩåį§°å¨äŊ įææčŽžå¤äšé´åēäŋæå¯ä¸īŧæĒé
įŊŽåæ æŗå¯į¨æ¤åčŊã",
"Please set this device name": "č¯ˇčŽžįŊŽæ¤čŽžå¤åį§°",
+ "Please understand that this is intended behaviour.": "č¯ˇæŗ¨æčŋæ¯éĸæčĄä¸ēã",
+ "Please use V2, V1 is deprecated and will be removed in the future, It was not a very appropriate algorithm. Only for compatibility V1 is kept.": "蝎äŊŋ፠V2ãV1 厞čĸĢåŧį¨īŧå°æĨäŧčĸĢį§ģé¤īŧåŽåšļ䏿¯é常åéįįŽæŗãäģ
åēäēå
ŧ厚æ§čäŋį V1ã",
"Plug-in version": "æäģļįæŦ",
+ "Plugins": "æäģļ",
"Prepare the 'report' to create an issue": "åå¤ 'æĨå' äģĨååģēéŽéĸå",
"Presets": "éĸ莞",
+ "Prevent fetching configuration from server": "éģæĸäģæåĄå¨čˇåé
įŊŽ",
+ "Proceed": "įģ§įģ",
"Proceed Garbage Collection": "įģ§įģæ§čĄååžåæļ",
+ "Proceed to the next step.": "įģ§įģä¸ä¸æĨã",
"Proceed with Setup URI": "įģ§įģäŊŋ፠Setup URI",
"Proceeding with Garbage Collection, ignoring missing nodes.": "æŖå¨įģ§įģæ§čĄååžåæļīŧåšļåŋŊįĨįŧēå¤ąčįšã",
"Proceeding with Garbage Collection.": "æŖå¨æ§čĄååžåæļã",
+ "Process files even if seems to be corrupted": "åŗäŊŋæäģļįäŧŧæåäščŋčĄå¤į",
"Process small files in the foreground": "å¨åå°å¤įå°æäģļ",
"Progress": "čŋåēĻ",
"Property Encryption": "åąæ§å å¯",
"PureJS fallback (Fast, W/O WebAssembly)": "PureJS åéīŧåŋĢéīŧæ WebAssemblyīŧ",
"Purge all download/upload cache.": "æ¸
餿æä¸čŊŊīŧä¸äŧ įŧåã",
"Purge all journal counter": "æ¸
餿ææĨåŋ莥æ°å¨",
+ "Reboot to re-run the process": "éå¯äģĨéæ°čŋčĄæ¤æĩį¨",
"Rebuild local and remote database with local files.": "äŊŋ፿Ŧå°æäģļéåģēæŦå°åčŋįĢ¯æ°æŽåēã",
"Rebuilding Operations (Remote Only)": "éåģēæäŊīŧäģ
čŋį̝īŧ",
+ "Recovery and Repair": "æĸå¤ä¸äŋŽå¤",
"Recreate all": "å
¨é¨éåģē",
"Recreate missing chunks for all files": "ä¸ēæææäģļéåģēįŧēå¤ąį chunks",
- "RedFlag.Fetch.Method.Desc": "How do you want to fetch?\n- %{RedFlag.Fetch.Method.FetchSafer}.\n **Low Traffic**, **High CPU**, **Low Risk**\n Recommended if ...\n - Files possibly inconsistent\n - Files were not so much\n- %{RedFlag.Fetch.Method.FetchSmoother}.\n **Low Traffic**, **Moderate CPU**, **Low to Moderate Risk**\n Recommended if ...\n - Files probably consistent\n - You have a lot of files.\n- %{RedFlag.Fetch.Method.FetchTraditional}.\n **High Traffic**, **Low CPU**, **Low to Moderate Risk**\n\n>[!INFO]- Details\n> ## %{RedFlag.Fetch.Method.FetchSafer}.\n> **Low Traffic**, **High CPU**, **Low Risk**\n> This option first creates a local database using existing local files before fetching data from the remote source.\n> If matching files exist both locally and remotely, only the differences between them will be transferred.\n> However, files present in both locations will initially be handled as conflicted files. They will be resolved automatically if they are not actually conflicted, but this process may take time.\n> This is generally the safest method, minimizing data loss risk.\n> ## %{RedFlag.Fetch.Method.FetchSmoother}.\n> **Low Traffic**, **Moderate CPU**, **Low to Moderate Risk** (depending operation)\n> This option first creates chunks from local files for the database, then fetches data. Consequently, only chunks missing locally are transferred. However, all metadata is taken from the remote source.\n> Local files are then compared against this metadata at launch. The content considered newer will overwrite the older one (by modified time). This outcome is then synchronised back to the remote database.\n> This is generally safe if local files are genuinely the latest timestamp. However, it can cause problems if a file has a newer timestamp but older content (like the initial `welcome.md`).\n> This uses less CPU and faster than \"%{RedFlag.Fetch.Method.FetchSafer}\", but it may lead to data loss if not used carefully.\n> ## %{RedFlag.Fetch.Method.FetchTraditional}.\n> **High Traffic**, **Low CPU**, **Low to Moderate Risk** (depending operation)\n> All things will be fetched from the remote.\n> Similar to the %{RedFlag.Fetch.Method.FetchSmoother}, but all chunks are fetched from the remote source.\n> This is the most traditional way to fetch, typically consuming the most network traffic and time. It also carries a similar risk of overwriting remote files to the '%{RedFlag.Fetch.Method.FetchSmoother}' option.\n> However, it is often considered the most stable method because it is the longest-established and most straightforward approach.",
- "RedFlag.Fetch.Method.FetchSafer": "Create a local database once before fetching",
- "RedFlag.Fetch.Method.FetchSmoother": "Create local file chunks before fetching",
- "RedFlag.Fetch.Method.FetchTraditional": "Fetch everything from the remote",
- "RedFlag.Fetch.Method.Title": "How do you want to fetch?",
- "RedFlag.FetchRemoteConfig.Buttons.Cancel": "No, use local settings",
- "RedFlag.FetchRemoteConfig.Buttons.Fetch": "Yes, fetch and apply remote settings",
- "RedFlag.FetchRemoteConfig.Message": "Do you want to fetch and apply remotely stored preference settings to the device?",
- "RedFlag.FetchRemoteConfig.Title": "Fetch Remote Configuration",
+ "RedFlag.Fetch.Method.Desc": "äŊ 叿åĻäŊčˇåīŧ\n- %{RedFlag.Fetch.Method.FetchSafer}ã\n **äŊæĩé**īŧ**éĢ CPU**īŧ**äŊéŖéŠ**\n å¨äģĨ䏿
åĩ䏿¨čäŊŋį¨īŧ\n - æäģļå¯čŊä¸ä¸č´\n - æäģļæ°éä¸å¤\n- %{RedFlag.Fetch.Method.FetchSmoother}ã\n **äŊæĩé**īŧ**ä¸į CPU**īŧ**äŊå°ä¸įéŖéŠ**\n å¨äģĨ䏿
åĩ䏿¨čäŊŋį¨īŧ\n - æäģļåēæŦä¸č´\n - äŊ æå¤§éæäģļã\n- %{RedFlag.Fetch.Method.FetchTraditional}ã\n **éĢæĩé**īŧ**äŊ CPU**īŧ**äŊå°ä¸įéŖéŠ**\n\n>[!INFO]- č¯Ļæ
\n> ## %{RedFlag.Fetch.Method.FetchSafer}ã\n> **äŊæĩé**īŧ**éĢ CPU**īŧ**äŊéŖéŠ**\n> æ¤é饚äŧå
åŠį¨į°æįæŦå°æäģļååģēæŦå°æ°æŽåēīŧįļååäģčŋ፿ēčˇåæ°æŽã\n> åĻææŦå°åčŋį¨åå¨į¸åįæäģļīŧååĒäŧäŧ čžä¸¤č
äšé´įåˇŽåŧã\n> äŊæ¯īŧ两螚éŊåå¨įæäģļæåäŧčĸĢč§ä¸ēå˛įĒæäģļãåĻæåŽäģŦåŽé
ä¸åšļæ å˛įĒīŧå°äŧčĸĢčĒå¨č§Ŗåŗīŧä¸čŋčŋä¸čŋį¨å¯čŊéčĻä¸äēæļé´ã\n> čŋéå¸¸æ¯æåŽå
¨įæšæŗīŧčŊå°æ°æŽä¸ĸå¤ąéŖéŠéå°æäŊã\n> ## %{RedFlag.Fetch.Method.FetchSmoother}ã\n> **äŊæĩé**īŧ**ä¸į CPU**īŧ**äŊå°ä¸įéŖéŠ**īŧč§å
ˇäŊæäŊčåŽīŧ\n> æ¤é饚äŧå
æ šæŽæŦå°æäģļä¸ēæ°æŽåēååģēæ°æŽåīŧįļååčˇåæ°æŽãå æ¤īŧåĒäŧäŧ čžæŦå°įŧēå°įæ°æŽåãäŊæ¯īŧææå
æ°æŽéŊåčĒčŋ፿ēã\n> å¯å¨æļīŧįŗģįģäŧå°æŦå°æäģļä¸čŋäēå
æ°æŽčŋčĄæ¯čžãčĸĢ莤ä¸ēæ´æ°įå
厚īŧæäŋŽæšæļé´å¤æīŧå°čĻįčžæ§įįæŦãéåīŧčŋä¸įģæäŧåæĨåčŋ፿°æŽåēã\n> åĻææŦå°æäģļįæļé´æŗįĄŽåŽæ¯ææ°įīŧčŋé常æ¯åŽå
¨įãäŊåĻææä¸Ēæäģļįæļé´æŗčžæ°ãå
厚å´čžæ§īŧäžåĻæåį `welcome.md`īŧīŧå°ąå¯čŊåŧåéŽéĸã\n> čŋį§æšåŧæ¯â%{RedFlag.Fetch.Method.FetchSafer}âå ፿´å°į CPUãéåēĻæ´åŋĢīŧäŊåĻæäŊŋį¨ä¸åŊīŧå¯čŊäŧå¯ŧč´æ°æŽä¸ĸå¤ąã\n> ## %{RedFlag.Fetch.Method.FetchTraditional}ã\n> **éĢæĩé**īŧ**äŊ CPU**īŧ**äŊå°ä¸įéŖéŠ**īŧč§å
ˇäŊæäŊčåŽīŧ\n> ææå
厚éŊå°äģčŋį¨čˇåã\n> ä¸ %{RedFlag.Fetch.Method.FetchSmoother} į¸äŧŧīŧäŊæææ°æŽåéŊäģčŋ፿ēčˇåã\n> čŋæ¯æäŧ įģįčˇåæšåŧīŧé常æļčæå¤įįŊįģæĩéåæļé´ãä¸â%{RedFlag.Fetch.Method.FetchSmoother}âé饚įąģäŧŧīŧåŽåæ ˇåå¨čĻįčŋ፿äģļįéŖéŠã\n> ä¸čŋīŧåŽé常čĸĢ莤ä¸ēæ¯æį¨ŗåŽįæšæŗīŧå ä¸ēåŽæ¯å垿æ äš
ãæį´æĨįåæŗã",
+ "RedFlag.Fetch.Method.FetchSafer": "čˇååå
ååģē䏿ŦĄæŦå°æ°æŽåē",
+ "RedFlag.Fetch.Method.FetchSmoother": "čˇååå
ååģēæŦå°æäģļįæ°æŽå",
+ "RedFlag.Fetch.Method.FetchTraditional": "äģčŋį¨čˇåå
¨é¨å
厚",
+ "RedFlag.Fetch.Method.Title": "äŊ æŗåĻäŊčˇåīŧ",
+ "RedFlag.FetchRemoteConfig.Buttons.Cancel": "åĻīŧäŊŋ፿Ŧå°čŽžįŊŽ",
+ "RedFlag.FetchRemoteConfig.Buttons.Fetch": "æ¯īŧčˇååšļåēį¨čŋį¨čŽžįŊŽ",
+ "RedFlag.FetchRemoteConfig.Message": "æ¯åĻčĻčˇåčŋį¨äŋåįååĨŊ莞įŊŽīŧåšļå°å
ļåēį¨å°æ¤čŽžå¤īŧ",
+ "RedFlag.FetchRemoteConfig.Title": "čˇåčŋį¨é
įŊŽ",
"Reduces storage space by discarding all non-latest revisions. This requires the same amount of free space on the remote server and the local client.": "éčŋä¸ĸåŧææéææ°įæŦæĨåå°åå¨įŠēé´ãčŋéčĻčŋ፿åĄå¨åæŦå°åŽĸæˇį̝å
ˇå¤į¸åæ°éįå¯į¨įŠēé´ã",
"Reducing the frequency with which on-disk changes are reflected into the DB": "éäŊå°įŖįä¸įæ´æšåæ å°æ°æŽåēä¸įéĸį",
+ "Refresh": "县°",
"Region": "åēå",
+ "Relay settings": "ä¸įģ§čŽžįŊŽ",
+ "Reload": "éæ°å čŊŊ",
"Remediation": "äŋŽå¤čŽžįŊŽ",
"Remediation Setting Changed": "äŋŽå¤čŽžįŊŽåˇ˛æ´æš",
"Remote Database Tweak (In sunset)": "čŋįĢ¯æ°æŽåēč°äŧīŧéæĨæˇæą°īŧ",
"Remote Databases": "čŋįĢ¯æ°æŽåē",
"Remote name": "čŋį̝åį§°",
+ "Remote only": "äģ
čŋį̝",
"Remote server type": "čŋ፿åĄå¨įąģå",
"Remote Type": "čŋį¨įąģå",
"Rename": "éåŊå",
- "Replicator.Dialogue.Locked.Action.Dismiss": "Cancel for reconfirmation",
- "Replicator.Dialogue.Locked.Action.Fetch": "Reset Synchronisation on This Device",
- "Replicator.Dialogue.Locked.Action.Unlock": "Unlock the remote database",
+ "Replicate now": "įĢåŗå¤åļ",
+ "Replicating": "å¤åļä¸",
+ "Replicating...": "å¤åļä¸âĻ",
+ "Replicator.Dialogue.Locked.Action.Dismiss": "åæļåšļéæ°įĄŽčŽ¤",
+ "Replicator.Dialogue.Locked.Action.Fetch": "éįŊŽæ¤čŽžå¤ä¸įåæĨ",
+ "Replicator.Dialogue.Locked.Action.Unlock": "č§Ŗéčŋ፿°æŽåē",
"Replicator.Dialogue.Locked.Message": "Remote database is locked. This is due to a rebuild on one of the terminals.\nThe device is therefore asked to withhold the connection to avoid database corruption.\n\nThere are three options that we can do:\n\n- %{Replicator.Dialogue.Locked.Action.Fetch}\n The most preferred and reliable way. This will dispose the local database once, and fetch all from the remote database again, In most case, we can perform this safely. However, it takes some time and should be done in stable network.\n- %{Replicator.Dialogue.Locked.Action.Unlock}\n This method can only be used if we are already reliably synchronised by other replication methods. This does not simply mean that we have the same files. If you are not sure, you should avoid it.\n- %{Replicator.Dialogue.Locked.Action.Dismiss}\n This will cancel the operation. And we will asked again on next request.\n",
- "Replicator.Dialogue.Locked.Message.Fetch": "Fetch all has been scheduled. Plug-in will be restarted to perform it.",
- "Replicator.Dialogue.Locked.Message.Unlocked": "The remote database has been unlocked. Please retry the operation.",
- "Replicator.Dialogue.Locked.Title": "Locked",
- "Replicator.Message.Cleaned": "Database cleaning up is in process. replication has been cancelled",
- "Replicator.Message.InitialiseFatalError": "No replicator is available, this is the fatal error.",
- "Replicator.Message.Pending": "Some file events are pending. Replication has been cancelled.",
- "Replicator.Message.SomeModuleFailed": "Replication has been cancelled by some module failure",
+ "Replicator.Dialogue.Locked.Message.Fetch": "åˇ˛åŽæčˇåå
¨é¨å
厚ãæäģļå°éå¯äģĨæ§čĄč¯ĨæäŊã",
+ "Replicator.Dialogue.Locked.Message.Unlocked": "čŋ፿°æŽåē厞觪éã蝎éč¯č¯ĨæäŊã",
+ "Replicator.Dialogue.Locked.Title": "厞éåŽ",
+ "Replicator.Message.Cleaned": "æ°æŽå翏
įæŖå¨čŋčĄä¸īŧå¤åļ厞åæļã",
+ "Replicator.Message.InitialiseFatalError": "æ˛Ąæå¯į¨įå¤åļå¨īŧčŋæ¯ä¸ä¸Ēč´åŊé蝝ã",
+ "Replicator.Message.Pending": "é¨åæäģļäēäģļäģå¨įåž
å¤įãå¤åļ厞åæļã",
+ "Replicator.Message.SomeModuleFailed": "įąäēæä¸Ēæ¨Ąååēéīŧå¤åļ厞åæļã",
"Replicator.Message.VersionUpFlash": "An update has been detected. Please open the Settings dialogue and check the Change Log. Replication has been cancelled.",
"Requires restart of Obsidian": "éčĻéå¯ Obsidian",
"Requires restart of Obsidian.": "éčĻéå¯ Obsidian ",
@@ -628,6 +954,7 @@
"Reset": "éįŊŽ",
"Reset all": "å
¨é¨éįŊŽ",
"Reset all journal counter": "éįŊŽæææĨåŋ莥æ°å¨",
+ "Reset and Resume Synchronisation": "éįŊŽåšļæĸå¤åæĨ",
"Reset journal received history": "éįŊŽæĨåŋæĨæļåå˛",
"Reset journal sent history": "éįŊŽæĨåŋåéåå˛",
"Reset notification threshold and check the remote database usage": "éįŊŽéįĨéåŧåšļæŖæĨčŋ፿°æŽåēäŊŋ፿
åĩ",
@@ -640,13 +967,29 @@
"Resolve all conflicted files": "č§Ŗåŗææå˛įĒæäģļ",
"Resolve All conflicted files by the newer one": "å°ææå˛įĒæäģļč§Ŗæä¸ēčžæ°įįæŦ",
"Resolve all conflicted files by the newer one. Caution: This will overwrite the older one, and cannot resurrect the overwritten one.": "å°ææå˛įĒæäģļįģä¸äŋįčžæ°įįæŦãæŗ¨æīŧčŋäŧčĻįčžæ§įįæŦīŧä¸čĸĢčĻįįå
åŽšæ æŗæĸå¤ã",
+ "Restart and Fetch Data": "éå¯åšļčˇåæ°æŽ",
+ "Restart and Initialise Server": "éå¯åšļåå§åæåĄå¨",
"Restart Now": "įĢåŗéå¯",
+ "Restarting Obsidian is strongly recommended. Until restart, some changes may not take effect, and display may be inconsistent. Are you sure to restart now?": "åŧēįåģē莎éå¯ Obsidianãå¨éå¯äšåīŧé¨åæ´æšå¯čŊä¸äŧįæīŧæžį¤ēäšå¯čŊä¸ä¸č´ãįĄŽåŽį°å¨éå¯åīŧ",
"Restore or reconstruct local database from remote.": "äģčŋį̝æĸ夿éåģēæŦå°æ°æŽåēã",
+ "Rev": "äŋŽčŽĸ",
+ "Revert changes": "čŋåæ´æš",
+ "Revoke": "æ¤é",
+ "Room ID": "æŋé´ ID",
+ "Room ID suffix:": "æŋé´ ID åįŧīŧ",
+ "Rotation Duration": "äģ¤įčŊŽæĸæļéŋ",
"Run Doctor": "įĢåŗč¯æ",
+ "S3/MinIO/R2 Configuration": "S3/MinIO/R2 é
įŊŽ",
"S3/MinIO/R2 Object Storage": "S3/MinIO/R2 å¯ščąĄåå¨",
+ "Same": "į¸å",
+ "Same or local only": "į¸åæäģ
æŦå°",
+ "Save and Apply": "äŋååšļåēį¨",
"Save settings to a markdown file. You will be notified when new settings arrive. You can set different files by the platform.": "å°čŽžįŊŽäŋåå°ä¸ä¸Ē Markdown æäģļä¸ãåŊæ°čŽžįŊŽå°čžžæļ,æ¨å°æļå°éįĨãæ¨å¯äģĨæ šæŽåšŗå°čŽžįŊŽä¸åįæäģļ ",
+ "Save without connecting": "ä¸čŋæĨį´æĨäŋå",
"Saving will be performed forcefully after this number of seconds.": "卿¤į§æ°åå°åŧēåļæ§čĄäŋå ",
+ "Saving without a successful connection test keeps this profile, but automatic synchronisation may fail until the connection is corrected.": "æĒéčŋčŋæĨæĩč¯å°ąäŋåäŧäŋįæ¤æĄŖæĄīŧäŊå¨čŋæĨäŋŽæŖäšåīŧčĒå¨åæĨå¯čŊäŧå¤ąč´Ĩã",
"Scan a QR Code (Recommended for mobile)": "æĢæäēįģ´į īŧį§ģå¨į̝æ¨čīŧ",
+ "Scan changes": "æĢæåæ´",
"Scan changes on customization sync": "å¨čĒåŽäšåæĨæļæĢææ´æš",
"Scan customization automatically": "čĒ卿̿čĒåŽäščŽžįŊŽ",
"Scan customization before replicating.": "å¨å¤åļåæĢæčĒåŽäščŽžįŊŽ ",
@@ -655,17 +998,28 @@
"Scan for Broken files": "æĢææåæäģļ",
"Scan for hidden files before replication": "å¤åļåæĢæéčæäģļ",
"Scan hidden files periodically": "åŽææĢæéčæäģļ",
+ "Scan QR Code": "æĢæäēįģ´į ",
"Scan the QR code displayed on an active device using this device's camera.": "äŊŋį¨åŊå莞å¤įæå头æĢæåĻä¸å°åˇ˛å¨äŊŋį¨čŽžå¤ä¸æžį¤ēįäēįģ´į ã",
"Schedule and Restart": "åŽæåšļéå¯",
"Scram Switches": "į´§æĨåŧå
ŗ",
"Scram!": "į´§æĨå¤įŊŽ",
"Seconds, 0 to disable": "į§,0ä¸ēįĻį¨",
"Seconds. Saving to the local database will be delayed until this value after we stop typing or saving.": "į§ã卿äģŦåæĸčžå
Ĩæäŋåå,äŋåå°æŦå°æ°æŽåēå°åģļčŋæ¤åŧ ",
- "Secret Key": "Secret Key",
+ "Secret Access Key": "į§æčŽŋéŽå¯éĨ",
+ "Secret Key": "į§æå¯éĨ",
+ "Select active P2P remote": "éæŠįæį P2P čŋį̝",
+ "Select All Shiny": "å
¨é Shiny",
+ "Select Flagged Shiny": "éæŠåˇ˛æ 莰į Shiny",
+ "Select P2P remote...": "éæŠ P2P čŋį̝âĻ",
"Select the database adapter to use.": "éæŠčĻäŊŋį¨įæ°æŽåēéé
å¨ã",
+ "Selecting this option will result in the current data on this device being used to initialise the server. Any existing data on the server will be completely overwritten.": "éæŠæ¤é饚åīŧå°į¨æŦ莞å¤įåŊåæ°æŽæĨåå§åæåĄå¨ãæåĄå¨ä¸į°æįæ°æŽå°čĸĢåŽå
¨čĻįã",
+ "Selecting this option will result in this device joining the existing server. You need to fetching the existing synchronisation data from the server to this device.": "éæŠæ¤é饚åīŧæŦ莞å¤å°å å
Ĩį°ææåĄå¨ãäŊ éčĻææåĄå¨ä¸åˇ˛æįåæĨæ°æŽčˇåå°æŦ莞å¤ã",
+ "Selective": "éæŠæ§",
"Send": "åé",
"Send chunks": "åé chunks",
+ "SENDING": "åéä¸",
"Server URI": "æåĄå¨ URI",
+ "SESSION": "äŧč¯",
"Setting.GenerateKeyPair.Desc": "æäģŦ厞įģįæäēä¸įģå¯éĨ寚īŧ\n\næŗ¨æīŧčŋįģå¯éĨ寚äšåå°ä¸äŧåæŦĄæžį¤ēã蝎åĄåŋ
åĻĨåäŋįŽĄīŧåĻæä¸ĸå¤ąīŧäŊ éčĻéæ°įææ°įå¯éĨ寚ã\næŗ¨æ 2īŧå
ŦéĨé፠spki æ ŧåŧīŧį§éĨé፠pkcs8 æ ŧåŧãä¸ēæšäžŋå¤åļīŧå
ŦéĨä¸įæĸčĄäŧčĸĢčŊŦæĸä¸ē `\\n`ã\næŗ¨æ 3īŧå
ŦéĨåēé
įŊŽå¨čŋįĢ¯æ°æŽåēä¸īŧį§éĨåēé
įŊŽå¨æŦå°čŽžå¤ä¸ã\n\n>[!äģ
éæŦäēēæĨį]-\n> \n>\n> ### å
ŦéĨ\n> ```\n${public_key}\n> ```\n>\n> ### į§éĨ\n> ```\n${private_key}\n> ```\n>\n>
\n\n>[!æ´æŽĩå¤åļ]-\n>\n> \n>\n> ```\n${public_key}\n${private_key}\n> ```\n>\n>
",
"Setting.GenerateKeyPair.Title": "厞῿°įå¯éĨ寚īŧ",
"Setting.TroubleShooting": "æ
éæé¤",
@@ -673,25 +1027,35 @@
"Setting.TroubleShooting.Doctor.Desc": "æŖæĩįŗģįģä¸ä¸åįį莞įŊŽãīŧä¸čŋį§ģæé´éģčžį¸åīŧ",
"Setting.TroubleShooting.ScanBrokenFiles": "æĢææåæåŧ常įæäģļ",
"Setting.TroubleShooting.ScanBrokenFiles.Desc": "æĢææ°æŽåē䏿ǿŖįĄŽåå¨įæäģļã",
- "SettingTab.Message.AskRebuild": "Your changes require fetching from the remote database. Do you want to proceed?",
+ "SettingService.ConfigurationCouldNotBeDecrypted": "æ æŗč§Ŗå¯į°æé
įŊŽãé
įŊŽå¯éĨæĒæ´æšã",
+ "SettingService.ConfigurationEncryptionSaveFailed": "æ æŗäŋåé
įŊŽå å¯ã蝎å¨éč¯åæŖæĨ厞äŋåį莞įŊŽå莞å¤å¯éĨã",
+ "SettingService.ConfigurationPassphraseCancelled": "é
įŊŽå¯į įč¯įčžå
Ĩ厞åæļæä¸å¯į¨ãå¯å¨åˇ˛åæĸīŧ厞äŋåį莞įŊŽåˇ˛äŋįã",
+ "SettingService.ConfigurationPassphraseDeviceLocalDescription": "æ¤å¯į įč¯åĒäŋį卿¤čŽžå¤ä¸ãåŊå
ļäģ莞å¤éčĻ卿˛Ąæåˇ˛äŋåå¯éĨįæ
åĩä¸č§Ŗå¯åäŋæ¤į莞įŊŽæļīŧ蝎å¨å¯å¨æļčžå
Ĩį¸åįå¯į įč¯ã",
+ "SettingService.ConfigurationPassphraseRetryTitle": "æ æŗč§Ŗå¯é
įŊŽã蝎鿰čžå
Ĩé
įŊŽå¯į įč¯ã",
+ "SettingService.ConfigurationPassphraseRollbackFailed": "åå
Ĩå¤ąč´Ĩåīŧæ æŗæĸå¤é
įŊŽå¯į įč¯ã蝎å¨éč¯åæŖæĨ厞äŋåį莞įŊŽå莞å¤å¯éĨã",
+ "SettingService.ConfigurationPassphraseTitle": "é
įŊŽå¯į įč¯",
+ "SettingTab.Message.AskRebuild": "äŊ įæ´æšéčĻäģčŋ፿°æŽåēčˇåæ°æŽãæ¯åĻįģ§įģīŧ",
+ "Setup Complete: Preparing to Fetch Synchronisation Data": "莞įŊŽåŽæīŧæŖå¨åå¤čˇååæĨæ°æŽ",
+ "Setup Complete: Preparing to Initialise Server": "莞įŊŽåŽæīŧæŖå¨åå¤åå§åæåĄå¨",
"Setup URI dialog cancelled.": "Setup URI å¯šč¯æĄåˇ˛åæļã",
- "Setup.Apply.Buttons.ApplyAndFetch": "Apply and Fetch",
- "Setup.Apply.Buttons.ApplyAndMerge": "Apply and Merge",
- "Setup.Apply.Buttons.ApplyAndRebuild": "Apply and Rebuild",
- "Setup.Apply.Buttons.Cancel": "Discard and Cancel",
- "Setup.Apply.Buttons.OnlyApply": "Only Apply",
- "Setup.Apply.Message": "The new configuration is ready. Let us proceed to apply it.\nThere are several ways to apply this:\n\n- Apply and Fetch\n Configure this device as a new client. After applying, synchronise from the remote server.\n- Apply and Merge\n Configure on a device that already has the file. It processes the local files and transfers the differences. Conflicts may arise.\n- Apply and Rebuild\n Rebuild the remote using local files. This is typically done if the server becomes corrupted or we wish to start from scratch.\n Other devices will be locked and required to re-fetch.\n- Only Apply\n Apply only. Conflicts may arise if a rebuild is required.",
- "Setup.Apply.Title": "Apply new configuration from the ${method}",
- "Setup.Apply.WarningRebuildRecommended": "NOTE: after adjusting the settings, it has been determined that a rebuild is required; Just Import is not recommended.",
- "Setup.Doctor.Buttons.No": "No, please use the settings in the URI as is",
- "Setup.Doctor.Buttons.Yes": "Yes, please consult the doctor",
- "Setup.Doctor.Message": "Self-hosted LiveSync has gradually become longer in history and some recommended settings have changed.\n\nNow, setup is a very good time to do this.\n\nDo you want to run Doctor to check if the imported settings are optimal compared to the latest state?",
- "Setup.Doctor.Title": "Do you want to consult the doctor?",
- "Setup.FetchRemoteConf.Buttons.Fetch": "Yes, please fetch the configuration",
- "Setup.FetchRemoteConf.Buttons.Skip": "No, please use the settings in the URI",
- "Setup.FetchRemoteConf.Message": "If we have already synchronised once with another device, the remote database stores the suitable configuration values between the synchronised devices. The plug-in would like to retrieve them for robust configuration.\n\nHowever, we have to make sure the one thing. Are we currently in a situation where we can access the network safely and retrieve the settings?\n\nNote: Mostly, you are safe to do this, that your remote database is hosted with a SSL certificate, and your network is not compromised.",
- "Setup.FetchRemoteConf.Title": "Fetch configuration from remote database?",
- "Setup.QRCode": "We have generated a QR code to transfer the settings. Please scan the QR code with your phone or other device.\nNote: The QR code is not encrypted, so be careful to open this.\n\n>[!FOR YOUR EYES ONLY]-\n> ${qr_image}
",
+ "Setup-URI": "莞įŊŽ URI",
+ "Setup.Apply.Buttons.ApplyAndFetch": "åēį¨åšļčˇå",
+ "Setup.Apply.Buttons.ApplyAndMerge": "åēį¨åšļååšļ",
+ "Setup.Apply.Buttons.ApplyAndRebuild": "åēį¨åšļéåģē",
+ "Setup.Apply.Buttons.Cancel": "ä¸ĸåŧåšļåæļ",
+ "Setup.Apply.Buttons.OnlyApply": "äģ
åēį¨",
+ "Setup.Apply.Message": "æ°é
įŊŽåˇ˛åå¤å°ąįģĒīŧ莊æäģŦįģ§įģåēį¨åŽã\nåē፿¤é
įŊŽæäģĨä¸å į§æšåŧīŧ\n\n- åēį¨åšļčˇå\n å°æ¤čŽžå¤é
įŊŽä¸ēæ°åŽĸæˇį̝ãåēį¨åīŧäģčŋ፿åĄå¨åæĨã\n- åēį¨åšļååšļ\n å¨åˇ˛ææäģļį莞å¤ä¸čŋčĄé
įŊŽãåŽäŧå¤įæŦå°æäģļåšļäŧ čžåˇŽåŧãå¯čŊäŧåēį°å˛įĒã\n- åēį¨åšļéåģē\n äŊŋ፿Ŧå°æäģļéåģēčŋį¨ãé叏卿åĄå¨æåæå¸æäģ头åŧå§æļäŊŋį¨ã\n å
ļäģ莞å¤å°čĸĢéåŽīŧåšļéčĻéæ°čˇåã\n- äģ
åēį¨\n äģ
åēį¨é
įŊŽãåĻæéčĻéåģēīŧå¯čŊäŧåēį°å˛įĒã",
+ "Setup.Apply.Title": "äģ ${method} åē፿°é
įŊŽ",
+ "Setup.Apply.WarningRebuildRecommended": "æŗ¨æīŧč°æ´čŽžįŊŽåīŧ厞å¤åŽéčĻéåģēīŧ䏿¨čäģ
å¯ŧå
Ĩã",
+ "Setup.Doctor.Buttons.No": "åĻīŧ蝎æåæ ˇäŊŋ፠URI ä¸į莞įŊŽ",
+ "Setup.Doctor.Buttons.Yes": "æ¯īŧ蝎čŋčĄč¯æ",
+ "Setup.Doctor.Message": "Self-hosted LiveSync 厞įģåįģ夿ŦĄæŧčŋīŧä¸ä翍č莞įŊŽäšéäšåįäēååã\n\nį°å¨æŖåŧ莞įŊŽéļæŽĩīŧæ¯čŋčĄæŖæĨįåĨŊæļæēã\n\näŊ čĻčŋčĄč¯æīŧæŖæĨå¯ŧå
Ĩį莞įŊŽį¸å¯šäēææ°įļææ¯åĻæäŧåīŧ",
+ "Setup.Doctor.Title": "čĻčŋčĄč¯æåīŧ",
+ "Setup.FetchRemoteConf.Buttons.Fetch": "æ¯īŧ蝎čˇåé
įŊŽ",
+ "Setup.FetchRemoteConf.Buttons.Skip": "åĻīŧ蝎äŊŋ፠URI ä¸į莞įŊŽ",
+ "Setup.FetchRemoteConf.Message": "åĻææäģŦäšå厞įģä¸åĻä¸å°čŽžå¤åæĨčŋīŧčŋ፿°æŽåēäŧäŋå厞åæĨ莞å¤äšé´éį¨įé
įŊŽåŧãæäģļ叿čˇåčŋäēåŧīŧäģĨäžŋčŋčĄæ´å¯é įé
įŊŽã\n\näŊæä¸įšåŋ
éĄģįĄŽčŽ¤īŧæäģŦåŊ忝åĻå¤äēå¯äģĨåŽå
¨čŽŋéŽįŊįģåšļčˇåčŋäē莞įŊŽįį¯åĸä¸īŧ\n\næŗ¨æīŧé常æ
åĩä¸īŧåĒčĻäŊ įčŋ፿°æŽåēäŊŋį¨äē SSL č¯äšĻīŧä¸įŊįģæĒčĸĢå
Ĩäžĩīŧæ§čĄæ¤æäŊå°ąæ¯åŽå
¨įã",
+ "Setup.FetchRemoteConf.Title": "čĻäģčŋ፿°æŽåēčˇåé
įŊŽåīŧ",
+ "Setup.QRCode": "æäģŦ厞įæį¨äēäŧ čžčŽžįŊŽįäēįģ´į ã蝎äŊŋ፿æēæå
ļäģčŽžå¤æĢæč¯Ĩäēįģ´į ã\næŗ¨æīŧæ¤äēįģ´į æĒå å¯īŧ蝎谍æ
æåŧã\n\n>[!FOR YOUR EYES ONLY]-\n> ${qr_image}
",
"Setup.RemoteE2EE.AdvancedTitle": "éĢįē§",
"Setup.RemoteE2EE.AlgorithmWarning": "æ´æšå å¯įŽæŗäŧå¯ŧč´äšåäŊŋį¨å
ļäģįŽæŗå å¯įæ°æŽæ æŗčŽŋéŽãč¯ˇįĄŽäŋææčŽžå¤éŊé
įŊŽä¸ēäŊŋį¨åä¸įŽæŗīŧäģĨäŋæå¯šæ°æŽįčŽŋéŽčŊåã",
"Setup.RemoteE2EE.ButtonCancel": "åæļ",
@@ -736,22 +1100,42 @@
"Should we prompt you about conflicting files when a file is opened?": "åŊæäģļæåŧæļ,æ¯åĻæį¤ēå˛įĒæäģļīŧ",
"Should we prompt you for every single merge, even if we can safely merge automatcially?": "åŗäŊŋæäģŦå¯äģĨåŽå
¨å°čĒå¨ååšļ,æ¯åĻäšåēč¯Ĩä¸ēæ¯ä¸æŦĄååšļæį¤ēæ¨īŧ",
"Show full banner": "æžį¤ēåŽæ´æ¨Ēåš
",
+ "Show history": "æžį¤ēåå˛čްåŊ",
"Show icon only": "äģ
æžį¤ēåžæ ",
"Show only notifications": "äģ
æžį¤ēéįĨ",
+ "Show password": "æžį¤ēå¯į ",
"Show status as icons only": "äģ
äģĨåžæ æžį¤ēįļæ",
"Show status icon instead of file warnings banner": "æžį¤ēįļæåžæ īŧčéæäģļčĻ忍Ēåš
",
"Show status inside the editor": "å¨įŧčžå¨å
æžį¤ēįļæ",
"Show status on the status bar": "å¨įļææ 䏿žį¤ēįļæ",
"Show verbose log. Please enable if you report an issue.": "æžį¤ēč¯ĻįģæĨåŋãåĻææ¨æĨåéŽéĸ,蝎å¯į¨æ¤é饚 ",
+ "Signaling Server Connection": "äŋĄä줿åĄå¨čŋæĨ",
+ "Signalling Relays": "äŋĄäģ¤ä¸įģ§",
+ "Signalling Status": "äŋĄäģ¤įļæ",
+ "Since you have chosen to overwrite all local files with remote data, **how would you like to handle local files that are not present in the remote database?**\n\n- **${DELETE_ALL}**: Local-only files and remote-deleted files will be removed.\n This option will make your local vault exactly the same as the remote database, but please make sure you have a backup if there is any important data in your vault.\n- **${DELETE_NONE}**: All existing local files will be preserved.\n This option will keep all your local files, but it may cause duplicates if there are files that exist on local but not on remote. You can clean up these duplicates manually after the synchronisation.": "įąäēäŊ éæŠäēį¨čŋ፿°æŽčĻįæææŦå°æäģļīŧ**寚äēčŋ፿°æŽåēä¸ä¸åå¨įæŦå°æäģļīŧäŊ 叿åĻäŊå¤įīŧ**\n\n- **${DELETE_ALL}**īŧäģ
åå¨äēæŦå°įæäģļåå¨čŋį¨åˇ˛čĸĢå é¤įæäģļå°čĸĢį§ģé¤ã\n æ¤é饚äŧ莊äŊ įæŦå°åēä¸čŋ፿°æŽåēåŽå
¨ä¸č´īŧäŊåĻæåē䏿éčĻæ°æŽīŧ蝎åĄåŋ
å
ååĨŊå¤äģŊã\n- **${DELETE_NONE}**īŧäŋįææį°æįæŦå°æäģļã\n æ¤é饚äŧäŋįäŊ ææįæŦå°æäģļīŧäŊåĻææäēæäģļäģ
åå¨äēæŦå°čä¸å¨čŋį¨īŧå¯čŊäŧäē§įé夿äģļãåæĨåŽæåīŧäŊ å¯äģĨæå¨æ¸
įčŋäēé夿äģļã",
+ "Skip and close": "莺čŋåšļå
ŗé",
+ "Snippets": "äģŖį įæŽĩ",
"Some devices have differing progress values (max: ${maxProgress}, min: ${minProgress}).\nThis may indicate that some devices have not completed synchronisation, which could lead to conflicts. Strongly recommend confirming that all devices are synchronised before proceeding.": "æäē莞å¤įčŋåēĻåŧä¸åīŧæå¤§īŧ${maxProgress}īŧæå°īŧ${minProgress}īŧã\nčŋå¯čŊ襨į¤ēæäē莞å¤å°æĒåŽæåæĨīŧäģčå¯čŊåŧåå˛įĒãåŧēįåģē莎å¨įģ§įģäšåå
įĄŽčŽ¤ææčŽžå¤éŊ厞åæĨã",
+ "Some files failed to synchronise. What would you like to do?": "é¨åæäģļåæĨå¤ąč´ĨãäŊ 叿åĻäŊå¤įīŧ",
+ "Start Broadcasting": "åŧå§åšŋæ",
+ "Start change-broadcasting on Connect": "čŋæĨæļåŧå§åšŋæåæ´",
+ "Start Sync & Close": "åŧå§åæĨåšļå
ŗé",
"Starts synchronisation when a file is saved.": "åŊæäģļäŋåæļå¯å¨åæĨ ",
+ "Stat": "įģ莥",
+ "Stats": "įģ莥",
+ "Stop âĄ": "åæĸ âĄ",
+ "Stop Broadcasting": "åæĸåšŋæ",
"Stop reflecting database changes to storage files.": "åæĸå°æ°æŽåēæ´æšåæ å°å卿äģļ ",
"Stop watching for file changes.": "åæĸįč§æäģļæ´æš ",
+ "Storage -> Database": "åå¨ â æ°æŽåē",
+ "Strongly Recommended": "åŧē῍č",
+ "Subject (whoami)": "ä¸ģäŊīŧwhoamiīŧ",
"Suppress notification of hidden files change": "æåéčæäģļæ´æšįéįĨ",
"Suspend database reflecting": "æåæ°æŽåēåæ ",
"Suspend file watching": "æåæäģļįč§",
"Switch to IDB": "åæĸå° IDB",
"Switch to IndexedDB": "åæĸå° IndexedDB",
+ "Sync": "åæĨ",
"Sync after merging file": "ååšļæäģļååæĨ",
"Sync automatically after merging files": "ååšļæäģļåčĒå¨åæĨ",
"Sync Mode": "åæĨæ¨Ąåŧ",
@@ -759,48 +1143,107 @@
"Sync on File Open": "æåŧæäģļæļåæĨ",
"Sync on Save": "äŋåæļåæĨ",
"Sync on Startup": "å¯å¨æļåæĨ",
+ "Sync once": "åæĨ䏿ŦĄ",
+ "Synchronisation Issues Detected": "æŖæĩå°åæĨéŽéĸ",
"Synchronisation utilising journal files. You must have set up an S3/MinIO/R2 compatible object storage.": "éčŋæĨåŋæäģļčŋčĄåæĨãäŊ éčĻäēå
é¨įŊ˛åĨŊå
ŧ厚 S3/MinIO/R2 įå¯ščąĄå卿åĄã",
"Synchronising files": "åæĨæäģļ",
"Syncing": "åæĨä¸",
+ "Syncing...": "åæĨä¸âĻ",
"Target patterns": "įŽæ æ¨Ąåŧ",
+ "Test connection and save": "æĩč¯čŋæĨåšļäŋå",
+ "Test Settings and Continue": "æĩč¯čŽžįŊŽåšļįģ§įģ",
"Testing only - Resolve file conflicts by syncing newer copies of the file, this can overwrite modified files. Be Warned.": "äģ
äžæĩč¯ - éčŋåæĨæäģļįčžæ°å¯æŦæĨč§Ŗåŗæäģļå˛įĒ,čŋå¯čŊäŧčĻįäŋŽæščŋįæäģļãč¯ˇæŗ¨æ ",
+ "The algorithm used for JWT authentication.": "į¨äē JWT 莤č¯įįŽæŗã",
+ "The Application ID for P2P connection. This should be same among your devices. Default is 'self-hosted-livesync' and could not be modified from the UI.": "į¨äē P2P čŋæĨįåē፠IDãææčŽžå¤åēäŋæį¸åãéģ莤åŧä¸ē 'self-hosted-livesync'īŧæ æŗå¨įéĸä¸äŋŽæšã",
+ "The connection test cannot add a signalling relay while P2P is active. Use the active relay settings, or disconnect P2P before testing.": "P2P å¤äēæ´ģå¨įļææļīŧčŋæĨæĩ蝿 æŗæˇģå äŋĄäģ¤ä¸įģ§ã蝎äŊŋį¨åŊåįæįä¸įģ§čŽžįŊŽīŧæå
æåŧ P2P åæĩč¯ã",
+ "The connection to the server has been configured successfully. As the next step,": "䏿åĄå¨įčŋæĨ厞é
įŊŽæåãä¸ä¸æĨīŧ",
+ "The credential/password for the TURN servers.": "TURN æåĄå¨įåæŽ/å¯į ã",
"The delay for consecutive on-demand fetches": "čŋįģæéčˇåįåģļčŋ",
+ "The files in this Vault are almost identical to the server's.": "æŦåēä¸įæäģļ䏿åĄå¨ä¸įå äšä¸č´ã",
"The following accepted nodes are missing its node information:\n- ${missingNodes}\n\nThis indicates that they have not been connected for some time or have been left on an older version.\nIt is preferable to update all devices if possible. If you have any devices that are no longer in use, you can clear all accepted nodes by locking the remote once.": "äģĨä¸åˇ˛æĨåčįšįŧēå°čįšäŋĄæ¯īŧ\n- ${missingNodes}\n\nčŋ襨į¤ēåŽäģŦ厞æä¸æŽĩæļé´æĒčŋæĨīŧæäģåįå¨čžæ§įæŦã\nåĻæå¯čŊīŧåģē莎å
æ´æ°ææčŽžå¤ãåĻææåˇ˛ä¸åäŊŋį¨į莞å¤īŧå¯äģĨå
éåŽä¸æŦĄčŋį¨į̝äģĨæ¸
é¤å
¨é¨åˇ˛æĨåčįšã",
+ "The Group ID and passphrase are used to identify your group of devices. Make sure to use the same Group ID and passphrase on all devices you want to synchronise.": "įģ ID åå¯į įč¯į¨äēæ č¯äŊ į莞å¤įģãč¯ˇįĄŽäŋææčĻåæĨį莞å¤éŊäŊŋį¨į¸åįįģ ID åå¯į įč¯ã",
"The Hash algorithm for chunk IDs": "å ID įåå¸įŽæŗ(åŽéĒæ§)",
+ "The IndexedDB adapter often offers superior performance in certain scenarios, but it has been found to cause memory leaks when used with LiveSync mode. When using LiveSync mode, please use IDB adapter instead.": "IndexedDB éé
å¨å¨æäēå翝䏿§čŊæ´åĨŊīŧäŊ厞åį°ä¸ LiveSync æ¨Ąåŧå፿ļäŧé æå
åæŗæŧãäŊŋ፠LiveSync æ¨Ąåŧæļīŧ蝎æšį¨ IDB éé
å¨ã",
+ "The key (PSK in HSxxx in base64, or private key in ESxxx in PEM) used for JWT authentication.": "į¨äē JWT 莤č¯įå¯éĨīŧbase64 æ ŧåŧį PSKīŧåŊĸåĻ HSxxxīŧæ PEM æ ŧåŧįį§éĨīŧåŊĸåĻ ESxxxīŧã",
+ "The key ID. this should be matched with CouchDB->jwt_keys->ALG:_`kid`.": "å¯éĨ IDãåŽåēä¸ CouchDB->jwt_keys->ALG:_`kid` į¸åšé
ã",
+ "the latest synchronisation data will be downloaded from the server to this device.": "ææ°įåæĨæ°æŽå°äģæåĄå¨ä¸čŊŊå°æŦ莞å¤ã",
+ "the local database, that is to say the synchronisation information, must be reconstituted.": "æŦå°æ°æŽåēīŧåŗåæĨäŋĄæ¯īŧåŋ
éĄģéåģēã",
"The maximum duration for which chunks can be incubated within the document. Chunks exceeding this period will graduate to independent chunks.": "ææĄŖä¸å¯äģĨåĩåįæ°æŽåįæå¤§æįģæļé´ãčļ
čŋæ¤æļé´įæ°æŽåå°æä¸ēįŦįĢæ°æŽå ",
"The maximum number of chunks that can be incubated within the document. Chunks exceeding this number will immediately graduate to independent chunks.": "ææĄŖä¸å¯äģĨåĩåįæ°æŽåįæå¤§æ°éãčļ
čŋæ¤æ°éįæ°æŽåå°įĢåŗæä¸ēįŦįĢæ°æŽå ",
"The maximum total size of chunks that can be incubated within the document. Chunks exceeding this size will immediately graduate to independent chunks.": "ææĄŖä¸å¯äģĨåĩåįæ°æŽåįæå¤§æģ大å°ãčļ
čŋæ¤å¤§å°įæ°æŽåå°įĢåŗæä¸ēįŦįĢæ°æŽå ",
"The minimum interval for automatic synchronisation on event.": "åēäēäēäģļčĒå¨åæĨįæå°é´éã",
+ "The Nostr relay servers to establish connections for P2P connections. Multiple servers can be separated by commas.": "į¨äēåģēįĢ P2P čŋæĨį Nostr ä¸įģ§æåĄå¨ãå¤ä¸ĒæåĄå¨å¯äģĨį¨éåˇåéã",
+ "The Passphrase for P2P connection. This should be same among your devices.": "į¨äē P2P čŋæĨįå¯į įč¯ãäŊ įææčŽžå¤ä¸éŊåēäŊŋį¨į¸åįå¯į įč¯ã",
"The remote database is locked for synchronisation to prevent vault corruption because this device is not marked as 'resolved'. Please back up your vault, reset the local database, and select 'Mark this device as resolved'. This warning will persist until replication confirms the device is resolved.": "ä¸ē鞿ĸæ°æŽäģåēæåīŧįąäēæ¤čŽžå¤å°æĒæ 莰ä¸ēâåˇ˛įĄŽčŽ¤âīŧčŋ፿°æŽåē厞čĸĢéåŽīŧæååæĨã蝎å
å¤äģŊäŊ įäģåēãéįŊŽæŦå°æ°æŽåēīŧįļåéæŠâå°æ¤čŽžå¤æ 莰ä¸ēåˇ˛įĄŽčŽ¤âãå¨å¤åļčŋį¨įĄŽčޤæ¤čŽžå¤åˇ˛åŽæįĄŽčŽ¤äšåīŧæ¤čĻåäŧæįģæžį¤ēã",
+ "The remote is already set up, and the configuration is compatible (or got compatible by this operation).": "čŋįĢ¯åˇ˛čŽžįŊŽåĨŊīŧä¸é
įŊŽå
ŧ厚īŧæį쿤æäŊå厞å
ŧ厚īŧã",
+ "The Room ID for P2P connection. This should be same among your devices.": "į¨äē P2P čŋæĨįæŋé´ IDãææčŽžå¤åēäŊŋį¨į¸åįæŋé´ IDã",
+ "The Rotation duration of token in minutes. Each generated tokens will be valid only within this duration.": "äģ¤įįčŊŽæĸæļéŋīŧåäŊīŧåéīŧãæ¯ä¸Ēįæįäģ¤įäģ
卿¤æļéŋå
ææã",
+ "The Setup-URI does not appear to be valid. Please check that you have copied it correctly.": "莞įŊŽ URI äŧŧäšæ æãč¯ˇæŖæĨæ¯åĻåŽæ´å¤åļã",
+ "The Setup-URI is valid and ready to use.": "莞įŊŽ URI ææīŧå¯äģĨäŊŋį¨ã",
+ "the single, authoritative master copy": "å¯ä¸įæå¨ä¸ģ坿Ŧ",
+ "The subject for JWT authentication. Mostly username.": "JWT 莤č¯įä¸ģäŊīŧé常ä¸ē፿ˇåã",
+ "the synchronisation data on the server will be built based on the current data on this device.": "æåĄå¨ä¸įåæĨæ°æŽå°åēäēæŦ莞å¤įåŊåæ°æŽæåģēã",
+ "The TURN servers to use for P2P connections. Multiple servers can be separated by commas.": "į¨äē P2P čŋæĨį TURN æåĄå¨ãå¤ä¸ĒæåĄå¨å¯į¨éåˇåéã",
+ "The username for the TURN servers.": "TURN æåĄå¨į፿ˇåã",
+ "Themes": "ä¸ģéĸ",
+ "There is a way to resolve this on other devices.": "å¨å
ļäģ莞å¤ä¸æåæŗč§Ŗåŗčŋä¸ĒéŽéĸã",
+ "There may be differences between the files in this Vault and the server.": "æŦåēä¸įæäģļ䏿åĄå¨ä¸å¯čŊåå¨åˇŽåŧã",
+ "Therefore, we ask that you exercise extreme caution when configuring server information manually. If an incorrect passphrase is entered, the data on the server will become corrupted.": "å æ¤īŧæäģŦčĻæąäŊ 卿å¨é
įŊŽæåĄå¨äŋĄæ¯æļæ ŧå¤å°åŋãåĻæčžå
Ĩäēé蝝įå¯į įč¯īŧæåĄå¨ä¸įæ°æŽå°äŧæåã",
+ "This can isolate your connections between devices. Use the same Room ID for the same devices.": "čŋå¯äģĨéįĻģ莞å¤äšé´įčŋæĨãåä¸įģ莞å¤č¯ˇäŊŋį¨į¸åįæŋé´ IDã",
+ "This device": "æŦ莞å¤",
+ "This device name": "æŦ莞å¤åį§°",
"This feature enables direct synchronisation between devices. No server is required, but both devices must be online at the same time for synchronisation to occur, and some features may be limited. Internet connection is only required to signalling (detecting peers) and not for data transfer.": "æ¤åčŊå¯å¨čŽžå¤äšé´į´æĨåæĨīŧæ éæåĄå¨īŧäŊåæĨæļ两å°čŽžå¤åŋ
éĄģåæļå¨įēŋīŧä¸é¨ååčŊå¯čŊåéãäēčįŊčŋæĨäģ
į¨äēäŋĄäģ¤īŧåį°å¯šį̝īŧīŧä¸į¨äēæ°æŽäŧ čžã",
+ "This first setup has several short steps because it confirms encryption, the connection method, and which device provides the initial data. Once it is complete, additional devices can reuse a Setup URI.": "éĻæŦĄčŽžįŊŽåä¸ēå ä¸ĒįŽįæĨéǤīŧå ä¸ēåŽčĻįĄŽčŽ¤å 坿šåŧãčŋæĨæšåŧīŧäģĨåįąåĒå°čŽžå¤æäžåå§æ°æŽãåŽæåīŧå
ļäģ莞å¤å¯äģĨå¤į¨čŽžįŊŽ URIã",
"This is an advanced option for users who do not have a URI or who wish to configure detailed settings.": "čŋæ¯éĸ忞Ąæ URI æå¸ææå¨é
įŊŽč¯Ļįģåæ°įéĢįē§é饚ã",
+ "This is an extremely powerful operation. We strongly recommend that you copy your Vault folder to a safe location.": "čŋæ¯ä¸éĄšåŊąåæå¤§įæäŊãæäģŦåŧēįåģē莎å
æåēæäģļ多å¤åļå°åŽå
¨äŊįŊŽã",
"This is the most suitable synchronisation method for the design. All functions are available. You must have set up a CouchDB instance.": "čŋæ¯æįŦĻååŊå莞莥įåæĨæšåŧīŧææåčŊåå¯į¨ãäŊ éčĻäēå
é¨įŊ˛åĨŊ CouchDB åŽäžã",
+ "This optional check uses Obsidian's internal request API and sends the credentials above to the CouchDB server. Use it only with a server you trust; administrator access may be required.": "æ¤å¯éæŖæĨäŊŋ፠Obsidian įå
é¨č¯ˇæą APIīŧåšļæä¸éĸįåæŽåéå° CouchDB æåĄå¨ã蝎äģ
寚äŊ äŋĄäģģįæåĄå¨äŊŋį¨īŧå¯čŊéčĻįŽĄįåæéã",
"This passphrase will not be copied to another device. It will be set to `Default` until you configure it again.": "æ¤å¯į ä¸äŧå¤åļå°åĻä¸å°čŽžå¤ã卿¨åæŦĄé
įŊŽäšå,åŽå°čŽžįŊŽä¸ē `Default` ",
+ "This password is used to encrypt the connection. Use something long enough.": "æ¤å¯į į¨äēå å¯čŋæĨãč¯ˇčŽžįŊŽåžčļŗå¤éŋã",
+ "This procedure will first delete all existing synchronisation data from the server. Following this, the server data will be completely rebuilt, using the current state of your Vault on this device (including its local database) as": "æ¤æäŊäŧå
å 餿åĄå¨ä¸ææį°æįåæĨæ°æŽãéåīŧæåĄå¨æ°æŽå°čĸĢåŽå
¨éåģēīŧäģĨæŦ莞å¤ä¸äŊ åēįåŊåįļæīŧå
æŦå
ļæŦå°æ°æŽåēīŧäŊä¸ē",
+ "This setting must be the same even when connecting to multiple synchronisation destinations.": "åŗäŊŋčŋæĨå°å¤ä¸ĒåæĨįŽæ īŧæ¤čŽžįŊŽäšåŋ
éĄģäŋæä¸č´ã",
+ "This Vault is empty, or contains only new files that are not on the server.": "æŦåēä¸ēįŠēīŧæåĒå
åĢæåĄå¨ä¸æ˛Ąæįæ°æäģļã",
+ "This will rebuild the local database on this device using the most recent data from the server. This action is designed to resolve synchronisation inconsistencies and restore correct functionality.": "čŋå°į¨æåĄå¨ä¸įææ°æ°æŽéåģēæŦ莞å¤įæŦå°æ°æŽåēãæ¤æäŊæ¨å¨č§ŖåŗåæĨä¸ä¸č´åšļæĸ夿Ŗå¸¸åčŊã",
"This will recreate chunks for all files. If there were missing chunks, this may fix the errors.": "čŋäŧä¸ēæææäģļéæ°įæ chunksãåĻæäšååå¨įŧēå¤ąį chunksīŧčŋå¯čŊäŋŽå¤į¸å
ŗé蝝ã",
+ "To minimise the creation of new conflicts": "äģĨåå°æ°å˛įĒįäē§į",
"To prevent unwanted vault corruption, the remote database has been locked for synchronisation. (This device is marked as 'resolved'.) When all your devices are marked as 'resolved', unlock the database. This warning will continue to appear until replication confirms the device is resolved.": "ä¸ē鞿ĸæå¤įæ°æŽäģåēæåīŧčŋ፿°æŽåē厞čĸĢéåŽīŧæååæĨãīŧæ¤čŽžå¤åˇ˛čĸĢæ 莰ä¸ēâåˇ˛įĄŽčŽ¤âīŧåŊäŊ įææčŽžå¤éŊæ 莰ä¸ēâåˇ˛įĄŽčŽ¤âåīŧåč§Ŗéæ°æŽåēãå¨å¤åļčŋį¨įĄŽčޤæ¤čŽžå¤åˇ˛åŽæįĄŽčŽ¤äšåīŧæ¤čĻåäŧæįģæžį¤ēã",
- "TweakMismatchResolve.Action.Dismiss": "Dismiss",
- "TweakMismatchResolve.Action.UseConfigured": "Use configured settings",
- "TweakMismatchResolve.Action.UseMine": "Update remote database settings",
- "TweakMismatchResolve.Action.UseMineAcceptIncompatible": "Update remote database settings but keep as is",
- "TweakMismatchResolve.Action.UseMineWithRebuild": "Update remote database settings and rebuild again",
- "TweakMismatchResolve.Action.UseRemote": "Apply settings to this device",
- "TweakMismatchResolve.Action.UseRemoteAcceptIncompatible": "Apply settings to this device, but and ignore incompatibility",
- "TweakMismatchResolve.Action.UseRemoteWithRebuild": "Apply settings to this device, and fetch again",
- "TweakMismatchResolve.Message.Main": "\nThe settings in the remote database are as follows. These values are configured by other devices, which are synchronised with this device at least once.\n\nIf you want to use these settings, please select %{TweakMismatchResolve.Action.UseConfigured}.\nIf you want to keep the settings of this device, please select %{TweakMismatchResolve.Action.Dismiss}.\n\n${table}\n\n>[!TIP]\n> If you want to synchronise all settings, please use `Sync settings via markdown` after applying minimal configuration with this feature.\n\n${additionalMessage}",
- "TweakMismatchResolve.Message.MainTweakResolving": "Your configuration has not been matched with the one on the remote server.\n\nFollowing configuration should be matched:\n\n${table}\n\nLet us know your decision.\n\n${additionalMessage}",
- "TweakMismatchResolve.Message.UseRemote.WarningRebuildRecommended": "\n>[!NOTICE]\n> Some changes are compatible but may consume extra storage and transfer volumes. A rebuild is recommended. However, a rebuild may not be performed at present, but may be implemented in future maintenance.\n> ***Please ensure that you have time and are connected to a stable network to apply!***",
- "TweakMismatchResolve.Message.UseRemote.WarningRebuildRequired": "\n>[!WARNING]\n> Some remote configurations are not compatible with the local database of this device. Rebuilding the local database will be required.\n> ***Please ensure that you have time and are connected to a stable network to apply!***",
- "TweakMismatchResolve.Message.WarningIncompatibleRebuildRecommended": "\n>[!NOTICE]\n> We have detected that some of the values are different to make incompatible the local database with the remote database.\n> Some changes are compatible but may consume extra storage and transfer volumes. A rebuild is recommended. However, a rebuild may not be performed at present, but may be implemented in future maintenance.\n> If you want to rebuild, it takes a few minutes or more. **Make sure it is safe to perform it now.**",
- "TweakMismatchResolve.Message.WarningIncompatibleRebuildRequired": "\n>[!WARNING]\n> We have detected that some of the values are different to make incompatible the local database with the remote database.\n> Either local or remote rebuilds are required. Both of them takes a few minutes or more. **Make sure it is safe to perform it now.**",
- "TweakMismatchResolve.Table": "| Value name | This device | On Remote |\n|: --- |: ---- :|: ---- :|\n${rows}\n\n",
+ "Transfer Tweak": "äŧ čžåžŽč°",
+ "TURN Credential": "TURN åæŽ",
+ "TURN server settings are only necessary if you are behind a strict NAT or firewall that prevents direct P2P connections. In most cases, you can leave these fields blank.": "åĒæåŊå¤äēéģæĸį´æĨ P2P čŋæĨįä¸Ĩæ ŧ NAT æé˛įĢåĸäšåæļīŧæéčĻ TURN æåĄå¨čŽžįŊŽã大夿°æ
åĩä¸čŋäēåæŽĩå¯äģĨįįŠēã",
+ "TURN Server URLs (comma-separated)": "TURN æåĄå¨å°åīŧéåˇåéīŧ",
+ "TURN Servers": "TURN æåĄå¨",
+ "TURN Username": "TURN ፿ˇå",
+ "TweakMismatchResolve.Action.DisableAutoAcceptCompatible": "åį¨čĒ卿Ĩå",
+ "TweakMismatchResolve.Action.Dismiss": "åŋŊįĨ",
+ "TweakMismatchResolve.Action.EnableAutoAcceptCompatible": "å¯į¨čĒ卿Ĩå",
+ "TweakMismatchResolve.Action.UseConfigured": "äŊŋį¨åˇ˛é
įŊŽį莞įŊŽ",
+ "TweakMismatchResolve.Action.UseMine": "æ´æ°čŋ፿°æŽåē莞įŊŽ",
+ "TweakMismatchResolve.Action.UseMineAcceptIncompatible": "æ´æ°čŋ፿°æŽåē莞įŊŽīŧäŊåŋŊįĨä¸å
ŧ厚éŽéĸ",
+ "TweakMismatchResolve.Action.UseMineWithRebuild": "æ´æ°čŋ፿°æŽåē莞įŊŽåšļåæŦĄéåģē",
+ "TweakMismatchResolve.Action.UseRemote": "å°čŽžįŊŽåēį¨å°æ¤čŽžå¤",
+ "TweakMismatchResolve.Action.UseRemoteAcceptIncompatible": "å°čŽžįŊŽåēį¨å°æ¤čŽžå¤īŧäŊåŋŊįĨä¸å
ŧ厚éŽéĸ",
+ "TweakMismatchResolve.Action.UseRemoteWithRebuild": "å°čŽžįŊŽåēį¨å°æ¤čŽžå¤īŧåšļéæ°čˇå",
+ "TweakMismatchResolve.Message.AutoAcceptCompatibleUndefined": "\nå莞å¤į莞įŊŽäŧŧäšåå¨åˇŽåŧãäŊ į°å¨å¯äģĨčĒå¨åēį¨å
ŧåŽšįæ´æšå°čŋäēé
įŊŽã\næ¯åĻčĻå¯į¨æ¤ãčĒ卿Ĩåã莞įŊŽīŧ",
+ "TweakMismatchResolve.Message.Main": "\nčŋ፿°æŽåēä¸į莞įŊŽåĻä¸ãčŋäēåŧįąä¸æ¤čŽžå¤čŗå°åæĨčŋ䏿ŦĄįå
ļäģ莞å¤é
įŊŽã\n\nåĻæäŊ æŗäŊŋį¨čŋäē莞įŊŽīŧč¯ˇéæŠ %{TweakMismatchResolve.Action.UseConfigured}ã\nåĻæäŊ æŗäŋįæ¤čŽžå¤į莞įŊŽīŧč¯ˇéæŠ %{TweakMismatchResolve.Action.Dismiss}ã\n\n${table}\n\n>[!TIP]\n> åĻæäŊ æŗåæĨå
¨é¨čŽžįŊŽīŧ蝎å
äŊŋ፿¤åčŊåē፿å°é
įŊŽīŧįļåäŊŋ፠`Sync settings via markdown`ã\n\n${additionalMessage}",
+ "TweakMismatchResolve.Message.MainTweakResolving": "äŊ įé
įŊŽä¸čŋ፿åĄå¨ä¸įé
įŊŽä¸ä¸č´ã\n\näģĨä¸é
įŊŽåēåŊäŋæä¸č´īŧ\n\n${table}\n\n蝎åįĨäŊ įåŗåŽã\n\n${additionalMessage}",
+ "TweakMismatchResolve.Message.mineUpdated": "æŦ莞å¤įé
įŊŽåˇ˛č°æ´ã",
+ "TweakMismatchResolve.Message.remoteUpdated": "čŋį̝åå¨įé
įŊŽåˇ˛æ´æ°ã",
+ "TweakMismatchResolve.Message.UseRemote.WarningRebuildRecommended": "\n>[!NOTICE]\n> é¨åæ´æšæ¯å
ŧ厚įīŧäŊå¯čŊäŧå į¨éĸå¤įåå¨įŠēé´åäŧ čžæĩéãåģē莎čŋčĄéåģēãä¸čŋīŧäšå¯äģĨæä¸éåģēīŧįåž
äģåįģ´æ¤æļåčŋčĄã\n> ***åēį¨åč¯ˇįĄŽäŋäŊ æå
čļŗįæļé´īŧåšļ厞čŋæĨå°į¨ŗåŽįįŊįģīŧ***",
+ "TweakMismatchResolve.Message.UseRemote.WarningRebuildRequired": "\n>[!WARNING]\n> é¨åčŋį¨é
įŊŽä¸æŦ莞å¤įæŦå°æ°æŽåēä¸å
ŧ厚īŧå°éčĻéåģēæŦå°æ°æŽåēã\n> ***åēį¨åč¯ˇįĄŽäŋäŊ æå
čļŗįæļé´īŧåšļ厞čŋæĨå°į¨ŗåŽįįŊįģīŧ***",
+ "TweakMismatchResolve.Message.WarningIncompatibleRebuildRecommended": "\n>[!NOTICE]\n> æäģŦæŖæĩå°é¨ååŧåå¨åˇŽåŧīŧå¯ŧč´æŦå°æ°æŽåēä¸čŋ፿°æŽåēä¸å
ŧ厚ã\n> é¨åæ´æšæ¯å
ŧ厚įīŧäŊå¯čŊäŧå į¨éĸå¤įåå¨įŠēé´åäŧ čžæĩéãåģē莎čŋčĄéåģēãä¸čŋīŧäšå¯äģĨæä¸éåģēīŧįåž
äģåįģ´æ¤æļåčŋčĄã\n> åĻæäŊ čĻéåģēīŧ大įēĻéčĻå åéææ´éŋæļé´ã**蝎å
įĄŽčŽ¤į°å¨æ§čĄæ¯åŽå
¨įã**",
+ "TweakMismatchResolve.Message.WarningIncompatibleRebuildRequired": "\n>[!WARNING]\n> æäģŦæŖæĩå°æäēåŧåå¨åˇŽåŧīŧå¯ŧč´æŦå°æ°æŽåēä¸čŋ፿°æŽåēä¸å
ŧ厚ã\n> éčĻéåģēæŦå°æčŋį¨å
ļä¸äšä¸īŧ两č
éŊéčĻå åéįčŗæ´éŋæļé´ã**č¯ˇįĄŽäŋį°å¨æ§čĄæ¤æäŊæ¯åŽå
¨įã**",
+ "TweakMismatchResolve.Table": "| åŧåį§° | æ¤čŽžå¤ | čŋ፠|\n|: --- |: ---- :|: ---- :|\n${rows}\n\n",
"TweakMismatchResolve.Table.Row": "| ${name} | ${self} | ${remote} |",
- "TweakMismatchResolve.Title": "Configuration Mismatch Detected",
- "TweakMismatchResolve.Title.TweakResolving": "Configuration Mismatch Detected",
- "TweakMismatchResolve.Title.UseRemoteConfig": "Use Remote Configuration",
+ "TweakMismatchResolve.Title": "æŖæĩå°é
įŊŽä¸ä¸č´",
+ "TweakMismatchResolve.Title.AutoAcceptCompatible": "å¯čĒ卿Ĩå",
+ "TweakMismatchResolve.Title.TweakResolving": "æŖæĩå°é
įŊŽä¸ä¸č´",
+ "TweakMismatchResolve.Title.UseRemoteConfig": "äŊŋį¨čŋį¨é
įŊŽ",
+ "Ui.Common.LocalDatabaseInitialisationFailed": "Self-hosted LiveSync æ æŗåæĨã蝎῿ĨåäģĨæĨįč¯ĻįģæĨåŋã",
"Ui.Common.Signal.Caution": "æŗ¨æ",
"Ui.Common.Signal.Danger": "åąéŠ",
"Ui.Common.Signal.Notice": "æį¤ē",
"Ui.Common.Signal.Warning": "čĻå",
+ "Ui.Common.SomeFilesCouldNotBeSynchronised": "åšļéæææäģļéŊčŊåæĨãč¯ˇæŖæĨååŊąåįæäģļãįææĨåäģĨæĨįč¯ĻįģæĨåŋã",
"Ui.Settings.Advanced.LocalDatabaseTweak": "æŦå°æ°æŽåēč°æ´",
"Ui.Settings.Advanced.MemoryCache": "å
åįŧå",
"Ui.Settings.Advanced.TransferTweak": "äŧ čžč°æ´",
@@ -985,6 +1428,28 @@
"Ui.Settings.SyncSettings.Fetch": "čˇå",
"Ui.Settings.SyncSettings.Merge": "ååšļ",
"Ui.Settings.SyncSettings.Overwrite": "čĻį",
+ "Ui.SetupWizard.ApplySettingsInitialisation.ApplyWithoutInitialisation": "ä¸åå§åį´æĨåēį¨",
+ "Ui.SetupWizard.ApplySettingsInitialisation.Back": "æĨįå
ļäģåēį¨čŋäē莞įŊŽįæšåŧ",
+ "Ui.SetupWizard.ApplySettingsInitialisation.BypassGuidance": "äģ
åēį¨čŋäē莞įŊŽå¯čŊäŊŋæŦ莞å¤ä¸å
ļį°æåæĨæ°æŽä¸å
ŧ厚ã蝎äģ
å¨įĄŽčޤæ ééåģēæļäŊŋį¨ã",
+ "Ui.SetupWizard.ApplySettingsInitialisation.BypassTitle": "ä¸åå§åį´æĨåēį¨čŽžįŊŽīŧ",
+ "Ui.SetupWizard.ApplySettingsInitialisation.ContinueFetch": "įģ§įģčˇå",
+ "Ui.SetupWizard.ApplySettingsInitialisation.FetchOption": "éįŊŽæŦ莞å¤įåæĨ",
+ "Ui.SetupWizard.ApplySettingsInitialisation.FetchOptionDesc": "éå¯åīŧį¨åŊåįčŋį̝åæĨæ°æŽéåģēæŦ莞å¤įæŦå°æ°æŽåēãåēä¸įæäģļéåäŧä¸č¯Ĩæ°æŽæ ¸å¯šååšļã",
+ "Ui.SetupWizard.ApplySettingsInitialisation.FetchOptionP2PDesc": "éå¯åéæŠä¸å°å¨įēŋįæē莞å¤ãæŦ莞å¤įæŦå° LiveSync æ°æŽåēå°äģč¯Ĩæē莞å¤éåģēã",
+ "Ui.SetupWizard.ApplySettingsInitialisation.Guidance": "čŋäē莞įŊŽåæ´äŧæšååæĨæ°æŽįč§Ŗéæšåŧã蝎å¨éå¯åčŋååå§åæäŊä¸čĩˇåēį¨ã",
+ "Ui.SetupWizard.ApplySettingsInitialisation.KeepEditing": "įģ§įģįŧčž",
+ "Ui.SetupWizard.ApplySettingsInitialisation.ProceedFetch": "éå¯åšļčˇååæĨæ°æŽ",
+ "Ui.SetupWizard.ApplySettingsInitialisation.ProceedFetchP2P": "éå¯åšļéæŠæē莞å¤",
+ "Ui.SetupWizard.ApplySettingsInitialisation.ProceedRebuild": "éå¯åšļčĻįæåĄå¨æ°æŽ",
+ "Ui.SetupWizard.ApplySettingsInitialisation.ProceedRebuildP2P": "éå¯åšļå夿Ŧ莞å¤",
+ "Ui.SetupWizard.ApplySettingsInitialisation.Question": "éå¯ååēäŊŋį¨åĒäģŊį°ææ°æŽīŧ",
+ "Ui.SetupWizard.ApplySettingsInitialisation.RebuildOption": "፿Ŧ莞å¤įæäģļčĻįæåĄå¨æ°æŽ",
+ "Ui.SetupWizard.ApplySettingsInitialisation.RebuildOptionDesc": "፿Ŧåēä¸åŊåįæäģļéåģēæŦå°åčŋįĢ¯æ°æŽåēãå
ļäģæŖå¨åæĨį莞å¤äšååŋ
éĄģéįŊŽå
ļæŦå°åæĨã",
+ "Ui.SetupWizard.ApplySettingsInitialisation.RebuildOptionP2P": "፿Ŧåēįæäģļå夿Ŧ莞å¤",
+ "Ui.SetupWizard.ApplySettingsInitialisation.RebuildOptionP2PDesc": "፿Ŧåēä¸åŊåįæäģļéåģēæŦ莞å¤įæŦå° LiveSync æ°æŽåēãčŋä¸äŧčĻįå
ļäģ莞å¤ã",
+ "Ui.SetupWizard.ApplySettingsInitialisation.RemoteVerificationGuidance": "æ æŗį¨åŊåįåæŽåå å¯čŽžįŊŽéĒ蝿é
įŊŽįčŋį̝ãįģ§įģå¯čŊå¯ŧč´éå¯åčˇåå¤ąč´Ĩã",
+ "Ui.SetupWizard.ApplySettingsInitialisation.RemoteVerificationTitle": "æ æŗéĒč¯čŋį̝åæĨæ°æŽ",
+ "Ui.SetupWizard.ApplySettingsInitialisation.Title": "åēį¨čŽžįŊŽåšļéæ°åå§ååæĨ",
"Ui.SetupWizard.Common.Back": "ä¸īŧå¸Ļæčŋå",
"Ui.SetupWizard.Common.Cancel": "åæļ",
"Ui.SetupWizard.Common.ProceedSelectOption": "č¯ˇéæŠä¸ä¸Ēé饚åįģ§įģ",
@@ -997,6 +1462,7 @@
"Ui.SetupWizard.Intro.ProceedNew": "æ¯įīŧæčĻåŧå§æ°įåæĨé
įŊŽ",
"Ui.SetupWizard.Intro.Question": "éĻå
īŧč¯ˇéæŠæįŦĻåäŊ åŊåæ
åĩįé饚ã",
"Ui.SetupWizard.Intro.Title": "æŦĸčŋäŊŋ፠Self-hosted LiveSync",
+ "Ui.SetupWizard.Invitation.Start": "åŧå§čŽžįŊŽ",
"Ui.SetupWizard.OutroAskUserMode.CompatibleOption": "čŋį¨į̝厞é
įŊŽåŽæīŧä¸åŊåé
įŊŽå
ŧ厚īŧæåˇ˛éčŋæŦæŦĄæäŊåä¸ēå
ŧ厚īŧã",
"Ui.SetupWizard.OutroAskUserMode.CompatibleOptionDesc": "é¤éäŊ éå¸¸įĄŽåŽīŧåĻåéæŠæ¤éĄšåå¨éŖéŠãåŽååŽæåĄå¨é
įŊŽä¸åŊå莞å¤å
ŧ厚ãåĻæäēåŽåšļéåĻæ¤īŧå¯čŊäŧå¯ŧč´æ°æŽä¸ĸå¤ąãč¯ˇįĄŽčŽ¤äŊ äēč§Ŗåæã",
"Ui.SetupWizard.OutroAskUserMode.ExistingOption": "čŋ፿åĄå¨åˇ˛įģé
įŊŽåŽæīŧææŗčŽŠæ¤čŽžå¤å å
ĨåæĨã",
@@ -1008,12 +1474,25 @@
"Ui.SetupWizard.OutroAskUserMode.ProceedNext": "įģ§įģä¸ä¸æĨ",
"Ui.SetupWizard.OutroAskUserMode.Question": "č¯ˇéæŠäŊ įåŊåæ
åĩã",
"Ui.SetupWizard.OutroAskUserMode.Title": "åŗå°åŽæīŧčŋéčĻååēéæŠ",
+ "Ui.SetupWizard.OutroNewP2PUser.GuidanceNotice": "P2P æ˛Ąæä¸åŋæåĄå¨å¯æŦå¯äžčĻįãæ¤æĨéǤåĒå夿Ŧ莞å¤īŧåŊå
ļäģ莞å¤čˇååå§æ°æŽæļīŧ蝎äŋææŦ莞å¤å¨įēŋã",
+ "Ui.SetupWizard.OutroNewP2PUser.GuidancePrimary": "įšå¯šįščŋæĨ厞é
įŊŽæåãæĨ䏿Ĩīŧå°į¨æŦåēä¸åŊåįæäģļæåģēæŦå° LiveSync æ°æŽåēã",
+ "Ui.SetupWizard.OutroNewP2PUser.Important": "č¯ˇæŗ¨æ",
+ "Ui.SetupWizard.OutroNewP2PUser.Proceed": "éå¯åšļå夿Ŧ莞å¤",
+ "Ui.SetupWizard.OutroNewP2PUser.Question": "蝎įšåģ䏿šæéŽéå¯īŧįļåčŋå
ĨæŦå°åå§åįĄŽčŽ¤ã",
+ "Ui.SetupWizard.OutroNewP2PUser.Title": "莞įŊŽåŽæīŧæŖå¨å夿Ŧ P2P 莞å¤",
"Ui.SetupWizard.OutroNewUser.GuidancePrimary": "æåĄå¨čŋæĨ厞æåé
įŊŽãä¸ä¸æĨå°æ šæŽåŊå莞å¤ä¸įæ°æŽīŧ卿åĄå¨į̝åģēįĢåæĨæ°æŽã",
"Ui.SetupWizard.OutroNewUser.GuidanceWarning": "éå¯åīŧåŊå莞å¤ä¸įæ°æŽäŧäŊä¸ēä¸ģ坿Ŧä¸äŧ å°æåĄå¨ãč¯ˇæŗ¨æīŧæåĄå¨ä¸į°æįééĸææ°æŽå°čĸĢåŽå
¨čĻįã",
"Ui.SetupWizard.OutroNewUser.Important": "éčĻ",
"Ui.SetupWizard.OutroNewUser.Proceed": "éå¯åšļåå§åæåĄå¨",
"Ui.SetupWizard.OutroNewUser.Question": "č¯ˇéæŠä¸æšæéŽīŧéå¯åšļčŋå
ĨæįģįĄŽčŽ¤æĨéǤã",
"Ui.SetupWizard.OutroNewUser.Title": "莞įŊŽåŽæīŧåå¤åå§åæåĄå¨",
+ "Ui.SetupWizard.RebuildEverythingP2P.ConfirmLocalReset": "ææįŊčŋåĒäŧéįŊŽæŦ莞å¤įæŦå°åæĨæ°æŽåēã",
+ "Ui.SetupWizard.RebuildEverythingP2P.ConfirmLocalResetNote": "å°äŊŋ፿Ŧåēä¸åŊåįæäģļæĨéåģēåŽã",
+ "Ui.SetupWizard.RebuildEverythingP2P.ConfirmTitle": "â ī¸ č¯ˇįĄŽčŽ¤äģĨä¸å
厚",
+ "Ui.SetupWizard.RebuildEverythingP2P.Guidance": "æ¤æäŊäŧä¸ĸåŧæŦ莞å¤įæŦå° LiveSync æ°æŽåēīŧåšļ፿Ŧåēä¸åŊåįæäģļéåģēãåŽä¸äŧå 餿čĻįå
ļäģ莞å¤ä¸įæ°æŽã",
+ "Ui.SetupWizard.RebuildEverythingP2P.Note": "åå§åå蝎äŋææŦ莞å¤å¨įēŋīŧäģĨäžŋå
ļäģ莞å¤äģåŽčˇååēã",
+ "Ui.SetupWizard.RebuildEverythingP2P.Proceed": "ææįŊīŧå夿Ŧ莞å¤",
+ "Ui.SetupWizard.RebuildEverythingP2P.Title": "æįģįĄŽčŽ¤īŧä¸ēæŦ莞å¤åå¤ P2P",
"Ui.SetupWizard.SelectExisting.Guidance": "äŊ æŖå¨å°æ¤čŽžå¤å å
Ĩ厞æåæĨé
įŊŽã",
"Ui.SetupWizard.SelectExisting.ManualOption": "æå¨čžå
ĨæåĄå¨äŋĄæ¯",
"Ui.SetupWizard.SelectExisting.ManualOptionDesc": "æå¨éæ°é
įŊŽä¸äŊ å
ļäģ莞å¤į¸åįæåĄå¨äŋĄæ¯ãæ¤æšåŧäģ
éį¨äēéĢįē§į¨æˇã",
@@ -1046,438 +1525,57 @@
"Ui.SetupWizard.SetupRemote.ProceedP2P": "įģ§įģé
įŊŽäģ
įšå¯šįšæ¨Ąåŧ",
"Ui.SetupWizard.SetupRemote.Title": "čžå
ĨæåĄå¨äŋĄæ¯",
"Unique name between all synchronized devices. To edit this setting, please disable customization sync once.": "ææåæĨ莞å¤äšé´įå¯ä¸åį§°ãčĻįŧčžæ¤čŽžįŊŽ,蝎éĻå
įĻį¨čĒåŽäšåæĨ",
+ "Unless you are certain, selecting this options is bit dangerous. It assumes that the server configuration is compatible with this device. If this is not the case, data loss may occur. Please ensure you know what you are doing.": "é¤éäŊ éå¸¸įĄŽåŽīŧåĻåéæŠæ¤é饚æäēåąéŠãåŽååŽæåĄå¨é
įŊŽä¸æŦ莞å¤å
ŧ厚ãčĨä¸å
ŧ厚īŧå¯čŊäŧåįæ°æŽä¸ĸå¤ąãč¯ˇįĄŽäŋäŊ æ¸
æĨčĒåˇąå¨åäģäšã",
+ "Updating list...": "æŖå¨æ´æ°å襨âĻ",
+ "URL": "URL",
"Use a custom passphrase": "äŊŋį¨čĒåŽäšå¯į įč¯",
"Use a Setup URI (Recommended)": "äŊŋ፠Setup URIīŧæ¨čīŧ",
"Use Custom HTTP Handler": "äŊŋį¨čĒåŽäš HTTP å¤įį¨åē",
+ "Use Diagnostic RTCPeerConnection for statistics": "äŊŋį¨č¯æį¨ RTCPeerConnection æļéįģ莥äŋĄæ¯",
"Use dynamic iteration count": "äŊŋį¨å¨æčŋäģŖæŦĄæ°",
+ "Use internal API": "äŊŋį¨å
é¨ API",
+ "Use Internal API": "äŊŋį¨å
é¨ API",
+ "Use JWT Authentication": "äŊŋ፠JWT čēĢäģŊéĒč¯",
+ "Use JWT instead of Basic Authentication": "äŊŋ፠JWT čé Basic 莤č¯",
+ "Use Only Local Chunks": "äģ
äŊŋ፿Ŧå°æ°æŽå",
+ "Use Path-Style Access": "äŊŋį¨čˇ¯åžéŖæ ŧčŽŋéŽ",
+ "Use Random Number": "äŊŋį¨éæēæ°",
+ "Use Request API to avoid `inevitable` CORS problem": "äŊŋ፠Request API äģĨéŋå
`inevitable` CORS éŽéĸ",
"Use Segmented-splitter": "äŊŋį¨åæŽĩåå˛å¨",
"Use splitting-limit-capped chunk splitter": "äŊŋį¨åå˛éåļä¸éįååå˛å¨",
+ "Use the detailed flow": "äŊŋį¨č¯Ļįģæĩį¨",
"Use the trash bin": "äŊŋį¨åæļįĢ",
"Use timeouts instead of heartbeats": "äŊŋį¨čļ
æļč䏿¯åŋ莺",
+ "Use vrtmrz's relay": "äŊŋ፠vrtmrz įä¸įģ§",
"username": "፿ˇå",
"Username": "፿ˇå",
"Verbose Log": "č¯ĻįģæĨåŋ",
"Verify all": "å
¨é¨æ ĄéĒ",
"Verify and repair all files": "æ ĄéĒåšļäŋŽå¤æææäģļ",
"Warning! This will have a serious impact on performance. And the logs will not be synchronised under the default name. Please be careful with logs; they often contain your confidential information.": "čĻåīŧčŋå°ä¸ĨéåŊąåæ§čŊãåšļ䏿Ĩåŋä¸äŧäģĨéģ莤åį§°åæĨã蝎å°åŋå¤įæĨåŋīŧåŽäģŦé常å
åĢæ¨įææäŋĄæ¯ ",
+ "WATCHING": "įč§ä¸",
+ "We are about to retrieve the remote data.\n\nFirstly, how shall we handle the data retrieved from this remote source?\n\n- **${NEWER_WINS}**: Compares the modified time of files and takes the newer one.\n If you have been using Self-hosted LiveSync and have made changes on multiple devices, this option may be suitable for you as it tries to merge changes based on modified time.\n- **${REMOTE_WINS}**: Remote data is the source of truth.\n If you are new to using Self-hosted LiveSync. This option may be easiest to understand and get started with.\n It will overwrite all your local files with the remote data, so please make sure you have a backup if there is any important data in your vault.\n- **${DETAILED}**: Opens the detailed setup wizard.\n If you want to have more control over the synchronisation process, or want to review the changes before applying, you can choose this option to use the detailed flow.": "åŗå°čˇåčŋ፿°æŽã\n\néĻå
īŧåēåĻäŊå¤įä쿤čŋ፿Ĩæēčˇåįæ°æŽīŧ\n\n- **${NEWER_WINS}**īŧæ¯čžæäģļįäŋŽæšæļé´īŧåšļéį¨čžæ°įéŖä¸ä¸Ēã\n åĻæäŊ ä¸į´å¨äŊŋ፠Self-hosted LiveSyncīŧåšļä¸å¨å¤å°čŽžå¤ä¸åčŋæ´æšīŧæ¤é饚å¯čŊéåäŊ īŧå ä¸ēåŽäŧå°č¯æ šæŽäŋŽæšæļé´ååšļæ´æšã\n- **${REMOTE_WINS}**īŧäģĨčŋ፿°æŽä¸ēåã\n åĻæäŊ æ¯ Self-hosted LiveSync ῰፿ˇīŧæ¤é饚å¯čŊæåŽšæįč§ŖīŧäšæåŽšæä¸æã\n åŽäŧį¨čŋ፿°æŽčĻįäŊ ææįæŦå°æäģļīŧå æ¤åĻæåē䏿éčĻæ°æŽīŧ蝎åĄåŋ
å
å¤äģŊã\n- **${DETAILED}**īŧæåŧč¯Ļįģ莞įŊŽåå¯ŧã\n åĻæäŊ 叿æ´åĨŊå°æ§åļåæĨčŋį¨īŧæå¸æå¨åēį¨åæŖæĨæ´æšīŧå¯äģĨéæŠæ¤é饚äŊŋį¨č¯Ļįģæĩį¨ã",
+ "We can not use \"/\" to the device name": "莞å¤åį§°ä¸ä¸čŊäŊŋį¨ã/ã",
+ "We can use only Secure (HTTPS) connections on Obsidian Mobile.": "Obsidian į§ģå¨į̝åĒčŊäŊŋį¨åŽå
¨īŧHTTPSīŧčŋæĨã",
"We cannot change the device name while this feature is enabled. Please disable this feature to change the device name.": "å¯į¨æ¤åčŊæļæ æŗæ´æščŽžå¤åį§°ãåĻéäŋŽæščŽžå¤åį§°īŧ蝎å
įĻ፿¤åčŊã",
+ "We have to configure the device name": "éčĻå
é
įŊŽčŽžå¤åį§°",
+ "We recommend that you copy your Vault folder to a safe location. This will provide a safeguard in case a large number of conflicts arise, or if you accidentally synchronise with an incorrect destination.": "æäģŦåģē莎äŊ æåēæäģļ多å¤åļå°åŽå
¨äŊįŊŽãčŋæ ˇå¨åēį°å¤§éå˛įĒīŧæä¸å°åŋåæĨå°éč¯¯įŽæ æļīŧčŊå¤ä¸åąäŋéã",
"We will now guide you through a few questions to simplify the synchronisation setup.": "æĨ䏿ĨæäģŦäŧéčŋå ä¸ĒéŽéĸīŧåŧå¯ŧäŊ æ´čŊģæžå°åŽæåæĨ莞įŊŽã",
"We will now proceed with the server configuration.": "æĨ䏿Ĩå°įģ§įģčŋčĄæåĄå¨é
įŊŽã",
"Welcome to Self-hosted LiveSync": "æŦĸčŋäŊŋ፠Self-hosted LiveSync",
"When you save a file in the editor, start a sync automatically": "åŊæ¨å¨įŧčžå¨ä¸äŋåæäģļæļ,čĒå¨åŧå§åæĨ",
+ "While enabled, it causes very performance impact but debugging replication testing and other features will be enabled. Please disable this if you have not read the source code. Requires restart of Obsidian. Sometimes there is no implementation.": "å¯į¨åäŧä¸ĨéåŊąåæ§čŊīŧäŊäŧåŧå¯å¤åļč°č¯æĩč¯įåčŊãåĻæäŊ æ˛Ąæé
č¯ģčŋæēäģŖį īŧ蝎å
ŗéæ¤éĄšãéčĻéå¯ Obsidianãé¨ååčŊå¯čŊå°æĒåŽį°ã",
"Write credentials in the file": "å°åæŽåå
Ĩæäģļ",
"Write logs into the file": "å°æĨåŋåå
Ĩæäģļ",
"xxhash32 (Fast but less collision resistance)": "xxhash32īŧéåēĻåŋĢīŧäŊæįĸ°æčŊåčžåŧąīŧ",
"xxhash64 (Fastest)": "xxhash64īŧæåŋĢīŧ",
+ "xxhash64 is the current default.": "xxhash64 æ¯åŊåįéģ莤åŧã",
"Yes, I want to add this device to my existing synchronisation": "æ¯įīŧæčĻæčŋå°čŽžå¤å å
Ĩį°æåæĨ",
"Yes, I want to set up a new synchronisation": "æ¯įīŧæčĻé
įŊŽæ°įåæĨ",
"You are adding this device to an existing synchronisation setup.": "äŊ æŖå¨å°æ¤čŽžå¤å å
Ĩå°į°æåæĨé
įŊŽä¸ã",
- "On": "åŧå¯",
- "Fix": "äŋŽå¤",
- "NEW": "æ°åĸ",
- "Off": "å
ŗé",
- "Rev": "äŋŽčŽĸ",
- "URL": "URL",
- "đĄ On": "đĄ åŧå¯",
- "Date": "æĨæ",
- "Deny": "æįģ",
- "Diff": "åˇŽåŧ",
- "live": "åŽæļ",
- "Name": "åį§°",
- "Path": "莝åž",
- "Same": "į¸å",
- "Stat": "įģ莥",
- "Sync": "åæĨ",
- "đĄ Off": "đĄ å
ŗé",
- "Close": "å
ŗé",
- "Mixed": "æˇˇå",
- "Peers": "å¯šįæš",
- "Stats": "įģ莥",
- "â SYNC": "â åæĨ",
- "Accept": "æĨå",
- "Action": "æäŊ",
- "Chunks": "å",
- "DENIED": "厞æįģ",
- "Ignore": "åŋŊįĨ",
- "lang-he": "å¸äŧ¯æĨč¯",
- "Reload": "éæ°å čŊŊ",
- "Revoke": "æ¤é",
- "Stop âĄ": "åæĸ âĄ",
- "Themes": "ä¸ģéĸ",
- "+1 week": "+1 å¨",
- "â WATCH": "â įč§",
- "đĩī¸ Diag": "đĩī¸ č¯æ",
- "Closed:": "厞å
ŗéīŧ",
- "Command": "åŊäģ¤",
- "Connect": "čŋæĨ",
- "Dismiss": "åŋŊįĨ",
- "Enabled": "厞å¯į¨",
- "Failed:": "å¤ąč´Ĩīŧ",
- "JWT Key": "JWT å¯éĨ",
- "Not now": "æä¸",
- "Plugins": "æäģļ",
- "Proceed": "įģ§įģ",
- "Refresh": "县°",
- "Room ID": "æŋé´ ID",
- "SENDING": "åéä¸",
- "SESSION": "äŧč¯",
- "â SELECT": "â éæŠ",
- "đ Delete": "đ å é¤",
- "ACCEPTED": "厞æĨå",
- "Advanced": "éĢįē§",
- "Devices:": "莞å¤īŧ",
- "FETCHING": "čˇåä¸",
- "Group ID": "įģ ID",
- "INVERTED": "厞åčŊŦ",
- "Peer ID:": "å¯šįæš IDīŧ",
- "Snippets": "äģŖį įæŽĩ",
- "WATCHING": "įč§ä¸",
- "(Missing)": "īŧįŧēå¤ąīŧ",
- "Automatic": "čĒå¨",
- "Different": "ä¸å",
- "IMPORTANT": "éčĻ",
- "Incoming:": "æĨæļīŧ",
- "No Items.": "æ 饚įŽã",
- "PERMANENT": "æ°¸äš
",
- "Selective": "éæŠæ§",
- "Setup-URI": "莞įŊŽ URI",
- "Sync once": "åæĨ䏿ŦĄ",
- "Connected:": "厞čŋæĨīŧ",
- "Disconnect": "æåŧčŋæĨ",
- "Local only": "äģ
æŦå°",
- "NO PREVIEW": "æ éĸč§",
- "P2P Status": "P2P įļæ",
- "Syncing...": "åæĨä¸âĻ",
- "đĸ Connected": "đĸ 厞čŋæĨ",
- "Connection:": "čŋæĨīŧ",
- "device name": "莞å¤åį§°",
- "obsidianLiveSyncSettingTab.titleExtraMenus": "æŠåąčå",
- "Other files": "å
ļäģæäģļ",
- "PLEASE NOTE": "č¯ˇæŗ¨æ",
- "Remote only": "äģ
čŋį̝",
- "Replicating": "å¤åļä¸",
- "This device": "æŦ莞å¤",
- "Ui.SetupWizard.Invitation.Start": "åŧå§čŽžįŊŽ",
- "Ui.SetupWizard.OutroNewP2PUser.Important": "č¯ˇæŗ¨æ",
- "â
: Use newer": "â
īŧäŊŋį¨čžæ°įæŦ",
- "Auto Connect": "čĒå¨čŋæĨ",
- "Compare file": "æ¯čžæäģļ",
- "Data to Copy": "čĻå¤åļįæ°æŽ",
- "Deselect all": "åæļå
¨é",
- "Memory cache": "å
åįŧå",
- "Scan changes": "æĢæåæ´",
- "Scan QR Code": "æĢæäēįģ´į ",
- "Show history": "æžį¤ēåå˛čްåŊ",
- "Ui.SetupWizard.ApplySettingsInitialisation.KeepEditing": "įģ§įģįŧčž",
- "Access Key ID": "čŽŋéŽå¯éĨ ID",
- "Broadcasting?": "æŖå¨åšŋæīŧ",
- "Communicating": "éäŋĄä¸",
- "Configuration": "é
įŊŽ",
- "Delete All of": "å
¨é¨å é¤",
- "Folder Prefix": "æäģļ多åįŧ",
- "Hide password": "éčå¯į ",
- "JWT Algorithm": "JWT įŽæŗ",
- "No Connection": "æĒčŋæĨ",
- "Replicate now": "įĢåŗå¤åļ",
- "Show password": "æžį¤ēå¯į ",
- "TURN Username": "TURN ፿ˇå",
- "đ´ Disconnected": "đ´ 厞æåŧ",
- "Custom Headers": "čĒåŽäšč¯ˇæąå¤´",
- "Detected Peers": "æŖæĩå°įå¯šįæš",
- "Device Peer ID": "莞å¤å¯šįæš ID",
- "File integrity": "æäģļåŽæ´æ§",
- "Highlight diff": "éĢäēŽåˇŽåŧ",
- "Initial Action": "åå§æäŊ",
- "moduleCheckRemoteSize.optionReview": "æĨįé饚",
- "Not configured": "æĒé
įŊŽ",
- "obsidianLiveSyncSettingTab.titleExtraFeaturesGroup": "æŠåąåčŊ",
- "Relay settings": "ä¸įģ§čŽžįŊŽ",
- "Replicating...": "å¤åļä¸âĻ",
- "Revert changes": "čŋåæ´æš",
- "Save and Apply": "äŋååšļåēį¨",
- "Skip and close": "莺čŋåšļå
ŗé",
- "Transfer Tweak": "äŧ čžåžŽč°",
- "Available Peers": "å¯į¨įå¯šįæš",
- "Continue anyway": "äģįļįģ§įģ",
- "Deny in session": "æŦæŦĄäŧč¯å
æįģ",
- "Enter Setup URI": "čžå
Ĩ莞įŊŽ URI",
- "Newer (${diff})": "čžæ°īŧ${diff}īŧ",
- "obsidianLiveSyncSettingTab.titleSynchronisation": "åæĨ",
- "Older (${diff})": "čžæ§īŧ${diff}īŧ",
- "Open connection": "æåŧčŋæĨ",
- "Room ID suffix:": "æŋé´ ID åįŧīŧ",
- "TURN Credential": "TURN åæŽ",
- "Allow in session": "æŦæŦĄäŧč¯å
å
莸",
- "Deny permanently": "æ°¸äš
æįģ",
- "Document History": "ææĄŖåå˛čްåŊ",
- "JWT Key ID (kid)": "JWT å¯éĨ IDīŧkidīŧ",
- "Maintenance mode": "įģ´æ¤æ¨Ąåŧ",
- "Select All Shiny": "å
¨é Shiny",
- "This device name": "æŦ莞å¤åį§°",
- "Updating list...": "æŖå¨æ´æ°å襨âĻ",
- "Use internal API": "äŊŋį¨å
é¨ API",
- "Use Internal API": "äŊŋį¨å
é¨ API",
- "Accept in session": "æŦæŦĄäŧč¯å
æĨå",
- "Advanced Settings": "éĢįē§čŽžįŊŽ",
- "Allow permanently": "æ°¸äš
å
莸",
- "Close this dialog": "å
ŗéæ¤å¯šč¯æĄ",
- "Create P2P remote": "ååģē P2P čŋį̝",
- "Flagged Selective": "厞æ 莰ä¸ēéæŠæ§",
- "JWT Subject (sub)": "JWT ä¸ģäŊīŧsubīŧ",
- "obsidianLiveSyncSettingTab.titleAdvancedSettings": "éĢįē§čŽžįŊŽ",
- "Open P2P Setup...": "æåŧ P2P 莞įŊŽâĻ",
- "P2P Configuration": "P2P é
įŊŽ",
- "Secret Access Key": "į§æčŽŋéŽå¯éĨ",
- "Signalling Status": "äŋĄäģ¤įļæ",
- "Stop Broadcasting": "åæĸåšŋæ",
- "Use Random Number": "äŊŋį¨éæēæ°",
- "â: Overwrite Local": "âīŧčĻįæŦå°",
- "Apply All Selected": "åē፿æåˇ˛é饚",
- "Apply the settings": "åēį¨čŽžįŊŽ",
- "Close & Disconnect": "å
ŗéåšļæåŧčŋæĨ",
- "Fetching status...": "æŖå¨čˇåįļæâĻ",
- "Generate Random ID": "įæéæē ID",
- "Same or local only": "į¸åæäģ
æŦå°",
- "Start Broadcasting": "åŧå§åšŋæ",
- "Start Sync & Close": "åŧå§åæĨåšļå
ŗé",
- "TweakMismatchResolve.Action.EnableAutoAcceptCompatible": "å¯į¨čĒ卿Ĩå",
- "Use vrtmrz's relay": "äŊŋ፠vrtmrz įä¸įģ§",
- "â: Overwrite Remote": "âīŧčĻįčŋį̝",
- "â ī¸ Important Notice": "â ī¸ éčĻæį¤ē",
- "Connection Settings": "čŋæĨ莞įŊŽ",
- "Database -> Storage": "æ°æŽåē â åå¨",
- "DENIED (in session)": "厞æįģīŧæŦæŦĄäŧč¯īŧ",
- "Enter TURN username": "čžå
Ĩ TURN ፿ˇå",
- "Enter your password": "čžå
ĨäŊ įå¯į ",
- "Enter your username": "čžå
ĨäŊ į፿ˇå",
- "Recovery and Repair": "æĸå¤ä¸äŋŽå¤",
- "Storage -> Database": "åå¨ â æ°æŽåē",
- "TweakMismatchResolve.Action.DisableAutoAcceptCompatible": "åį¨čĒ卿Ĩå",
- "Ui.SetupWizard.ApplySettingsInitialisation.ContinueFetch": "įģ§įģčˇå",
- "Encryption Algorithm": "å å¯įŽæŗ",
- "File to view History": "čĻæĨįåå˛čްåŊįæäģļ",
- "Issue detection log:": "éŽéĸæŖæĩæĨåŋīŧ",
- "Local Database Tweak": "æŦå°æ°æŽåē垎č°",
- "Maintenance Commands": "įģ´æ¤åŊäģ¤",
- "Obfuscate Properties": "æˇˇæˇåąæ§",
- "obsidianLiveSyncSettingTab.titleHelpAndInformation": "帎åŠä¸äŋĄæ¯",
- "Select Flagged Shiny": "éæŠåˇ˛æ 莰į Shiny",
- "Select P2P remote...": "éæŠ P2P čŋį̝âĻ",
- "Strongly Recommended": "åŧē῍č",
- "ACCEPTED (in session)": "厞æĨåīŧæŦæŦĄäŧč¯īŧ",
- "CouchDB Configuration": "CouchDB é
įŊŽ",
- "Enable P2P Replicator": "å¯į¨ P2P å¤åļå¨",
- "Enter TURN credential": "čžå
Ĩ TURN åæŽ",
- "Enter your JWT Key ID": "čžå
ĨäŊ į JWT å¯éĨ ID",
- "Enter your passphrase": "čžå
ĨäŊ įå¯į įč¯",
- "Experimental Settings": "åŽéĒæ§čŽžįŊŽ",
- "TweakMismatchResolve.Title.AutoAcceptCompatible": "å¯čĒ卿Ĩå",
- "Use Path-Style Access": "äŊŋį¨čˇ¯åžéŖæ ŧčŽŋéŽ",
- "Enter your Bucket Name": "čžå
ĨäŊ įå卿Ąļåį§°",
- "Restart and Fetch Data": "éå¯åšļčˇåæ°æŽ",
- "Use JWT Authentication": "äŊŋ፠JWT čēĢäģŊéĒč¯",
- "you wanted(Thank you)!": "äŊ čĻæąįæäŊīŧč°ĸč°ĸīŧīŧ",
- "Passphrase is required.": "å¯į į蝿¯åŋ
åĄĢ饚ã",
- "Peer to Peer Replicator": "įšå¯šįšå¤åļå¨",
- "Save without connecting": "ä¸čŋæĨį´æĨäŋå",
- "Enter your Access Key ID": "čžå
ĨäŊ įčŽŋéŽå¯éĨ ID",
- "Enter your database name": "čžå
ĨäŊ įæ°æŽåēåį§°",
- "Gathering information...": "æŖå¨æļéäŋĄæ¯âĻ",
- "obsidianLiveSyncSettingTab.titleHelpAndTroubleshooting": "帎åŠä¸įéžč§Ŗį",
- "obsidianLiveSyncSettingTab.titleMaintenanceAndRecovery": "įģ´æ¤ä¸æĸå¤",
- "Select active P2P remote": "éæŠįæį P2P čŋį̝",
- "Test connection and save": "æĩč¯čŋæĨåšļäŋå",
- "Auto Start P2P Connection": "čĒå¨å¯å¨ P2P čŋæĨ",
- "Check server requirements": "æŖæĨæåĄå¨čĻæą",
- "Hide not applicable items": "éčä¸éį¨éĄš",
- "Proceed to the next step.": "įģ§įģä¸ä¸æĨã",
- "S3/MinIO/R2 Configuration": "S3/MinIO/R2 é
įŊŽ",
- "Failed to parse Setup-URI.": "æ æŗč§ŖæčŽžįŊŽ URIã",
- "Just for a minute, please!": "蝎į¨įä¸åéīŧ",
- "Test Settings and Continue": "æĩč¯čŽžįŊŽåšļįģ§įģ",
- "${count} issue(s) detected!": "æŖæĩå° ${count} ä¸ĒéŽéĸīŧ",
- "Pick a file to show history": "éæŠä¸ä¸ĒæäģļäģĨæžį¤ēåå˛čްåŊ",
- "Signaling Server Connection": "äŋĄä줿åĄå¨čŋæĨ",
- "All the same or non-existent": "å
¨é¨į¸åæä¸åå¨",
- "Enter your Secret Access Key": "čžå
ĨäŊ įį§æčŽŋéŽå¯éĨ",
- "Lower (${local} < ${remote})": "čžäŊīŧ${local} < ${remote}īŧ",
- "Ui.SetupWizard.ApplySettingsInitialisation.ApplyWithoutInitialisation": "ä¸åå§åį´æĨåēį¨",
- "Higher (${local} > ${remote})": "čžéĢīŧ${local} > ${remote}īŧ",
- "Please select your situation.": "č¯ˇéæŠäŊ įæ
åĩã",
- "Restart and Initialise Server": "éå¯åšļåå§åæåĄå¨",
- "I Understand, Overwrite Server": "ææįŊīŧčĻįæåĄå¨",
- "â ī¸ Please Confirm the Following": "â ī¸ č¯ˇįĄŽčŽ¤äģĨä¸å
厚",
- "All checks passed successfully!": "æææŖæĨå厞éčŋīŧ",
- "Ui.SetupWizard.ApplySettingsInitialisation.ProceedRebuildP2P": "éå¯åšļå夿Ŧ莞å¤",
- "Ui.SetupWizard.OutroNewP2PUser.Proceed": "éå¯åšļå夿Ŧ莞å¤",
- "Ui.SetupWizard.RebuildEverythingP2P.ConfirmTitle": "â ī¸ č¯ˇįĄŽčŽ¤äģĨä¸å
厚",
- "Enter a folder prefix (optional)": "čžå
Ĩæäģļ多åįŧīŧå¯éīŧ",
- "Reset and Resume Synchronisation": "éįŊŽåšļæĸå¤åæĨ",
- "JWT Expiration Duration (minutes)": "JWT æææīŧåéīŧ",
- "Ui.SetupWizard.ApplySettingsInitialisation.ProceedRebuild": "éå¯åšļčĻįæåĄå¨æ°æŽ",
- "Ui.SetupWizard.RebuildEverythingP2P.Proceed": "ææįŊīŧå夿Ŧ莞å¤",
- "Mostly Complete: Decision Required": "åēæŦåŽæīŧéčĻäŊ åŗåŽ",
- "No checks have been performed yet.": "尿ǿ§čĄäģģäŊæŖæĨã",
- "TURN Server URLs (comma-separated)": "TURN æåĄå¨å°åīŧéåˇåéīŧ",
- "Ui.SetupWizard.ApplySettingsInitialisation.ProceedFetchP2P": "éå¯åšļéæŠæē莞å¤",
- "Checking connection... Please wait.": "æŖå¨æŖæĨčŋæĨâĻ蝎į¨åã",
- "Enter a complete HTTP or HTTPS URL.": "čžå
ĨåŽæ´į HTTP æ HTTPS å°åã",
- "Ui.SetupWizard.ApplySettingsInitialisation.RebuildOptionP2P": "፿Ŧåēįæäģļå夿Ŧ莞å¤",
- "Enter your JWT secret or private key": "čžå
ĨäŊ į JWT å¯éĨæį§éĨ",
- "I have created a backup of my Vault.": "æåˇ˛å¤äģŊæįåēã",
- "On the source device, open Obsidian.": "卿ē莞å¤ä¸æåŧ Obsidianã",
- "Start change-broadcasting on Connect": "čŋæĨæļåŧå§åšŋæåæ´",
- "Ui.SetupWizard.ApplySettingsInitialisation.FetchOption": "éįŊŽæŦ莞å¤įåæĨ",
- "We have to configure the device name": "éčĻå
é
įŊŽčŽžå¤åį§°",
- "Failed to create replicator instance.": "ååģēå¤åļå¨åŽäžå¤ąč´Ĩã",
- "the single, authoritative master copy": "å¯ä¸įæå¨ä¸ģ坿Ŧ",
- "We can not use \"/\" to the device name": "莞å¤åį§°ä¸ä¸čŊäŊŋį¨ã/ã",
- "Delete remote configuration '${name}'?": "å é¤čŋį̝é
įŊŽã${name}ãīŧ",
- "Ui.SetupWizard.ApplySettingsInitialisation.BypassTitle": "ä¸åå§åį´æĨåēį¨čŽžįŊŽīŧ",
- "Ui.SetupWizard.ApplySettingsInitialisation.ProceedFetch": "éå¯åšļčˇååæĨæ°æŽ",
- "Diagnostic RTCPeerConnection is enabled": "厞å¯į¨č¯æį¨ RTCPeerConnection",
- "Error during connection test: ${reason}": "čŋæĨæĩč¯åēéīŧ${reason}",
- "The Setup-URI is valid and ready to use.": "莞įŊŽ URI ææīŧå¯äģĨäŊŋį¨ã",
- "Connect to existing database and continue": "čŋæĨå°į°ææ°æŽåēåšļįģ§įģ",
- "Enter your JWT Subject (CouchDB Username)": "čžå
ĨäŊ į JWT ä¸ģäŊīŧCouchDB ፿ˇåīŧ",
- "To minimise the creation of new conflicts": "äģĨåå°æ°å˛įĒįäē§į",
- "Ui.SetupWizard.OutroNewP2PUser.Title": "莞įŊŽåŽæīŧæŖå¨å夿Ŧ P2P 莞å¤",
- "Create or connect to database and continue": "ååģēæčŋæĨå°æ°æŽåēåšļįģ§įģ",
- "Error during testAndFixSettings: ${reason}": "testAndFixSettings åēéīŧ${reason}",
- "Failed to connect to the server: ${reason}": "æ æŗčŋæĨå°æåĄå¨īŧ${reason}",
- "Prevent fetching configuration from server": "éģæĸäģæåĄå¨čˇåé
įŊŽ",
- "Ui.SetupWizard.ApplySettingsInitialisation.Back": "æĨįå
ļäģåēį¨čŋäē莞įŊŽįæšåŧ",
- "I am unable to create a backup of my Vault.": "ææ æŗå¤äģŊæįåēã",
- "Have you created a backup before proceeding?": "įģ§įģäšåīŧäŊ å¤äģŊäēåīŧ",
- "I am unable to create a backup of my Vaults.": "ææ æŗå¤äģŊæįåēã",
- "On this device, please keep this Vault open.": "蝎卿Ŧ莞å¤ä¸äŋææ¤åēå¤äēæåŧįļæã",
- "TweakMismatchResolve.Message.mineUpdated": "æŦ莞å¤įé
įŊŽåˇ˛č°æ´ã",
- "Setup Complete: Preparing to Initialise Server": "莞įŊŽåŽæīŧæŖå¨åå¤åå§åæåĄå¨",
- "Ui.SetupWizard.ApplySettingsInitialisation.RebuildOption": "፿Ŧ莞å¤įæäģļčĻįæåĄå¨æ°æŽ",
- "(e.g., after editing many files whilst offline)": "īŧäžåĻīŧįĻģįēŋæé´įŧčžäē大éæäģļäšåīŧ",
- "Ui.SetupWizard.ApplySettingsInitialisation.Title": "åēį¨čŽžįŊŽåšļéæ°åå§ååæĨ",
- "Ui.SetupWizard.RebuildEverythingP2P.Title": "æįģįĄŽčŽ¤īŧä¸ēæŦ莞å¤åå¤ P2P",
- "Use Diagnostic RTCPeerConnection for statistics": "äŊŋį¨č¯æį¨ RTCPeerConnection æļéįģ莥äŋĄæ¯",
- "Enter your Region (e.g., us-east-1, auto for R2)": "čžå
ĨäŊ įåēåīŧäžåĻ us-east-1īŧR2 åĄĢ autoīŧ",
- "There is a way to resolve this on other devices.": "å¨å
ļäģ莞å¤ä¸æåæŗč§Ŗåŗčŋä¸ĒéŽéĸã",
- "Ui.SetupWizard.ApplySettingsInitialisation.Question": "éå¯ååēäŊŋį¨åĒäģŊį°ææ°æŽīŧ",
- "Ui.SetupWizard.ApplySettingsInitialisation.RemoteVerificationTitle": "æ æŗéĒč¯čŋį̝åæĨæ°æŽ",
- "Please enter the CouchDB server information below.": "蝎å¨ä¸æšåĄĢå CouchDB æåĄå¨äŋĄæ¯ã",
- "Please understand that this is intended behaviour.": "č¯ˇæŗ¨æčŋæ¯éĸæčĄä¸ēã",
"You can configure in the Obsidian Plugin Settings.": "äŊ å¯äģĨå¨ Obsidian æäģļ莞įŊŽä¸é
įŊŽã",
- "TweakMismatchResolve.Message.remoteUpdated": "čŋį̝åå¨įé
įŊŽåˇ˛æ´æ°ã",
- "Failed to connect to the signalling relay: ${reason}": "æ æŗčŋæĨå°äŋĄäģ¤ä¸įģ§īŧ${reason}",
- "Connected to Signaling Server (as Peer ID: ${peerId})": "厞čŋæĨå°äŋĄä줿åĄå¨īŧå¯šįæš IDīŧ${peerId}īŧ",
- "Of course, we can back up the data before proceeding.": "åŊįļīŧæäģŦå¯äģĨå
å¤äģŊæ°æŽåįģ§įģã",
- "Please configure your end-to-end encryption settings.": "蝎é
įŊŽäŊ įį̝å°į̝å å¯čŽžįŊŽã",
- "obsidianLiveSyncSettingTab.logServerConfigurationCheck": "æåĄå¨é
įŊŽæŖæĨ",
- "Setup Complete: Preparing to Fetch Synchronisation Data": "莞įŊŽåŽæīŧæŖå¨åå¤čˇååæĨæ°æŽ",
- "I understand the risks and will proceed without a backup.": "ææįŊéŖéŠīŧå°å¨æ˛Ąæå¤äģŊįæ
åĩä¸įģ§įģã",
- "Ui.SetupWizard.RebuildEverythingP2P.ConfirmLocalResetNote": "å°äŊŋ፿Ŧåēä¸åŊåįæäģļæĨéåģēåŽã",
- "Failed to connect to the server. Please check your settings.": "æ æŗčŋæĨå°æåĄå¨ãč¯ˇæŖæĨäŊ į莞įŊŽã",
- "moduleCheckRemoteSize.noticeNotConfigured": "æĒé
įŊŽčŋį̝åå¨äŊ᧝æéã{HERE}",
- "I understand that this action is irreversible once performed.": "ææįŊæ¤æäŊ䏿Ͽ§čĄäžŋä¸å¯æ¤éã",
- "No devices available. Waiting for other devices to connect...": "æ˛Ąæå¯į¨į莞å¤ãæŖå¨įåž
å
ļäģ莞å¤čŋæĨâĻ",
- "The files in this Vault are almost identical to the server's.": "æŦåēä¸įæäģļ䏿åĄå¨ä¸įå äšä¸č´ã",
- "We can use only Secure (HTTPS) connections on Obsidian Mobile.": "Obsidian į§ģå¨į̝åĒčŊäŊŋį¨åŽå
¨īŧHTTPSīŧčŋæĨã",
- "by resetting the remote, you will be informed on other devices.": "éįŊŽčŋį̝åīŧå
ļäģ莞å¤äŧæļå°éįĨã",
- "My remote server is already set up. I want to join this device.": "æįčŋį̝æåĄå¨åˇ˛čŽžįŊŽåĨŊīŧææŗčŽŠæŦ莞å¤å å
Ĩã",
- "Please enter the Peer-to-Peer Synchronisation information below.": "蝎å¨ä¸æšåĄĢåįšå¯šįšåæĨäŋĄæ¯ã",
- "If you understand the risks and still wish to proceed, select so.": "åĻæäŊ æįŊéŖéŠäŊäģ叿įģ§įģīŧč¯ˇéæŠæ¤éĄšã",
- "Final Confirmation: Overwrite Server Data with This Device's Files": "æįģįĄŽčŽ¤īŧ፿Ŧ莞å¤įæäģļčĻįæåĄå¨æ°æŽ",
- "There may be differences between the files in this Vault and the server.": "æŦåēä¸įæäģļ䏿åĄå¨ä¸å¯čŊåå¨åˇŽåŧã",
- "Please follow the steps below to import settings from your existing device.": "蝎æäģĨ䏿ĨéǤäģäŊ į°æį莞å¤å¯ŧå
Ĩ莞įŊŽã",
- "This password is used to encrypt the connection. Use something long enough.": "æ¤å¯į į¨äēå å¯čŋæĨãč¯ˇčŽžįŊŽåžčļŗå¤éŋã",
- "This Vault is empty, or contains only new files that are not on the server.": "æŦåēä¸ēįŠēīŧæåĒå
åĢæåĄå¨ä¸æ˛Ąæįæ°æäģļã",
- "Please select an active P2P remote configuration to change P2P sync targets.": "č¯ˇéæŠä¸ä¸Ēįæį P2P čŋį̝é
įŊŽäģĨæ´æš P2P åæĨįŽæ ã",
- "However, This should not be enabled if you want to increase your secrecy more.": "ä¸čŋīŧåĻæäŊ 叿čŋ䏿ĨæéĢäŋ坿§īŧåä¸åēå¯į¨æ¤éĄšã",
- "Please select the button below to restart and proceed to the final confirmation.": "蝎įšåģ䏿šæéŽéå¯īŧįļåčŋå
ĨæįģįĄŽčŽ¤ã",
- "The connection to the server has been configured successfully. As the next step,": "䏿åĄå¨įčŋæĨ厞é
įŊŽæåãä¸ä¸æĨīŧ",
- "Ui.SetupWizard.RebuildEverythingP2P.ConfirmLocalReset": "ææįŊčŋåĒäŧéįŊŽæŦ莞å¤įæŦå°åæĨæ°æŽåēã",
+ "You can enable this setting to process the files with size mismatches, these files can be created by some APIs or integrations.": "å¯į¨æ¤čŽžįŊŽåå¯äģĨå¤į大å°ä¸åšé
įæäģļīŧčŋäēæäģļå¯čŊįąæäē API æéæååģēã",
"You should create a new synchronisation destination and rebuild your data there.": "äŊ åēåŊååģēä¸ä¸Ēæ°įåæĨįŽæ īŧåšļå¨éŖééåģēæ°æŽã",
- "CouchDB validates the database name when you connect. The name must not be empty.": "CouchDB äŧå¨čŋæĨæļæ ĄéĒæ°æŽåēåį§°ãåį§°ä¸čŊä¸ēįŠēã",
- "the latest synchronisation data will be downloaded from the server to this device.": "ææ°įåæĨæ°æŽå°äģæåĄå¨ä¸čŊŊå°æŦ莞å¤ã",
- "I am setting up a new server for the first time / I want to reset my existing server.": "ææ¯įŦŦ䏿ŦĄčŽžįŊŽæ°æåĄå¨īŧææŗéįŊŽį°ææåĄå¨ã",
- "(e.g., setting up for the first time on a new smartphone, starting from a clean slate)": "īŧäžåĻīŧ卿°ææēä¸éĻæŦĄčŽžįŊŽīŧäģéļåŧå§īŧ",
- "Ui.Common.LocalDatabaseInitialisationFailed": "Self-hosted LiveSync æ æŗåæĨã蝎῿ĨåäģĨæĨįč¯ĻįģæĨåŋã",
- "Please select the button below to restart and proceed to the data fetching confirmation.": "蝎įšåģ䏿šæéŽéå¯īŧįļåčŋå
Ĩæ°æŽčˇåįĄŽčŽ¤ã",
- "(e.g., immediately after restoring on another computer, or having recovered from a backup)": "īŧäžåĻīŧå¨åĻä¸å°įĩčä¸čŋåäšåīŧæäģå¤äģŊæĸå¤äšåīŧ",
- "the local database, that is to say the synchronisation information, must be reconstituted.": "æŦå°æ°æŽåēīŧåŗåæĨäŋĄæ¯īŧåŋ
éĄģéåģēã",
- "The Setup-URI does not appear to be valid. Please check that you have copied it correctly.": "莞įŊŽ URI äŧŧäšæ æãč¯ˇæŖæĨæ¯åĻåŽæ´å¤åļã",
- "I understand that all changes made on other smartphones or computers possibly could be lost.": "ææįŊå¨å
ļäģææēæįĩč䏿åįæ´æšå¯čŊäŧä¸ĸå¤ąã",
- "This setting must be the same even when connecting to multiple synchronisation destinations.": "åŗäŊŋčŋæĨå°å¤ä¸ĒåæĨįŽæ īŧæ¤čŽžįŊŽäšåŋ
éĄģäŋæä¸č´ã",
- "On the source device, from the command palette, run the 'Show settings as a QR code' command.": "卿ē莞å¤ä¸īŧäģåŊäģ¤éĸæŋčŋčĄãShow settings as a QR codeãåŊäģ¤ã",
- "This can isolate your connections between devices. Use the same Room ID for the same devices.": "čŋå¯äģĨéįĻģ莞å¤äšé´įčŋæĨãåä¸įģ莞å¤č¯ˇäŊŋį¨į¸åįæŋé´ IDã",
- "the synchronisation data on the server will be built based on the current data on this device.": "æåĄå¨ä¸įåæĨæ°æŽå°åēäēæŦ莞å¤įåŊåæ°æŽæåģēã",
- "Ui.SetupWizard.OutroNewP2PUser.Question": "蝎įšåģ䏿šæéŽéå¯īŧįļåčŋå
ĨæŦå°åå§åįĄŽčŽ¤ã",
- "On this device, switch to the camera app or use a QR code scanner to scan the displayed QR code.": "卿Ŧ莞å¤ä¸åæĸå°į¸æēåēį¨īŧæäŊŋį¨äēįģ´į æĢæå¨æĢææžį¤ēįäēįģ´į ã",
- "Ui.SetupWizard.RebuildEverythingP2P.Note": "åå§åå蝎äŋææŦ莞å¤å¨įēŋīŧäģĨäžŋå
ļäģ莞å¤äģåŽčˇååēã",
- "After that, synchronise to a brand new vault on each other device with the new remote one by one.": "äšåīŧå¨å
ļäģ莞å¤ä¸éä¸Ē፿°įčŋį̝åæĨå°ä¸ä¸Ēå
¨æ°įåēã",
- "Please enter the details required to connect to your S3/MinIO/R2 compatible object storage service.": "蝎åĄĢåčŋæĨå°äŊ į S3/MinIO/R2 å
ŧåŽšå¯ščąĄåå¨æåĄæéįäŋĄæ¯ã",
- "Note that the Group ID is not limited to the generated format; you can use any string as the Group ID.": "č¯ˇæŗ¨æīŧįģ ID ä¸éäēįæįæ ŧåŧīŧäŊ å¯äģĨäŊŋį¨äģģæåįŦĻ串äŊä¸ēįģ IDã",
- "The remote is already set up, and the configuration is compatible (or got compatible by this operation).": "čŋįĢ¯åˇ˛čŽžįŊŽåĨŊīŧä¸é
įŊŽå
ŧ厚īŧæį쿤æäŊå厞å
ŧ厚īŧã",
- "moduleCheckRemoteSize.noticeExceeded": "čŋį̝åå¨äŊ᧝ä¸ē ${measuredSize}īŧ厞čļ
čŋé
įŊŽįæééåŧ ${notifySize}ã{HERE}",
- "AddOn Module (ConfigSync) has not been loaded. This is very unexpected situation. Please report this issue.": "éå æ¨ĄåīŧConfigSyncīŧæĒčŊå čŊŊãčŋæ¯é常æå¤įæ
åĩã蝎æĨ忤éŽéĸã",
- "Ui.Common.SomeFilesCouldNotBeSynchronised": "åšļéæææäģļéŊčŊåæĨãč¯ˇæŖæĨååŊąåįæäģļãįææĨåäģĨæĨįč¯ĻįģæĨåŋã",
- "Initialise journal sent history. On the next sync, every item except this device received will be sent again.": "åå§åæĨåŋį厞åéåå˛ã䏿ŦĄåæĨæļīŧ餿Ŧ莞å¤åˇ˛æĨæļį饚å¤īŧå
ļäŊéŊäŧéæ°åéã",
- "AddOn Module (HiddenFileSync) has not been loaded. This is very unexpected situation. Please report this issue.": "éå æ¨ĄåīŧHiddenFileSyncīŧæĒčŊå čŊŊãčŋæ¯é常æå¤įæ
åĩã蝎æĨ忤éŽéĸã",
- "It is strongly advised to create a backup before proceeding. Continuing without a backup may lead to data loss.": "åŧēįåģē莎å¨įģ§įģäšåå
åå¤äģŊãæ˛Ąæå¤äģŊįģ§įģå¯čŊå¯ŧč´æ°æŽä¸ĸå¤ąã",
- "This is an extremely powerful operation. We strongly recommend that you copy your Vault folder to a safe location.": "čŋæ¯ä¸éĄšåŊąåæå¤§įæäŊãæäģŦåŧēįåģē莎å
æåēæäģļ多å¤åļå°åŽå
¨äŊįŊŽã",
- "Initialise journal received history. On the next sync, every item except this device sent will be downloaded again.": "åå§åæĨåŋį厞æĨæļåå˛ã䏿ŦĄåæĨæļīŧ餿Ŧ莞å¤åˇ˛åéį饚å¤īŧå
ļäŊéŊäŧéæ°ä¸čŊŊã",
- "Hidden file synchronization have been temporarily disabled. Please enable them after the fetching, if you need them.": "éčæäģļåæĨ厞čĸĢ临æļåį¨ãčˇååŽæååĻéäŊŋį¨īŧ蝎鿰å¯į¨ã",
- "If \"Auto Start P2P Connection\" is enabled, the P2P connection will be started automatically when the plug-in launches.": "åĻæå¯į¨äēãčĒå¨å¯å¨ P2P čŋæĨãīŧæäģļå¯å¨æļäŧčĒå¨åģēįĢ P2P čŋæĨã",
- "(Select this if you already have another synchronising device.) This option adds this device to the same synchronisation.": "īŧåĻæäŊ 厞æåĻä¸å°æŖå¨åæĨį莞å¤īŧč¯ˇéæŠæ¤éĄšãīŧæ¤é饚äŧææŦ莞å¤å å
Ĩåä¸ä¸ĒåæĨã",
- "Ui.SetupWizard.ApplySettingsInitialisation.FetchOptionP2PDesc": "éå¯åéæŠä¸å°å¨įēŋįæē莞å¤ãæŦ莞å¤įæŦå° LiveSync æ°æŽåēå°äģč¯Ĩæē莞å¤éåģēã",
- "If you reached the payload size limit when using IBM Cloudant, please decrease batch size and batch limit to a lower value.": "åĻæå¨äŊŋ፠IBM Cloudant æļčžžå°čŊŊčˇå¤§å°ä¸éīŧč¯ˇææšé大å°åæšéä¸éč°äŊã",
- "Note that you can generate a new Setup URI by running the \"Copy settings as a new Setup URI\" command in the command palette.": "č¯ˇæŗ¨æīŧäŊ å¯äģĨå¨åŊäģ¤éĸæŋä¸čŋčĄãCopy settings as a new Setup URIãåŊä줿Ĩįææ°į莞įŊŽ URIã",
- "Ui.SetupWizard.ApplySettingsInitialisation.RebuildOptionP2PDesc": "፿Ŧåēä¸åŊåįæäģļéåģēæŦ莞å¤įæŦå° LiveSync æ°æŽåēãčŋä¸äŧčĻįå
ļäģ莞å¤ã",
- "Device name to identify the device. Please use shorter one for the stable peer detection, i.e., \"iphone-16\" or \"macbook-2021\".": "į¨äēæ č¯čŽžå¤įåį§°ãä¸ēäēå¯šįæšæŖæĩæ´į¨ŗåŽīŧ蝎äŊŋį¨čžįįååīŧäžåĻãiphone-16ãæãmacbook-2021ãã",
- "Please enter the Setup URI that was generated during server installation or on another device, along with the vault passphrase.": "蝎čžå
Ĩ卿åĄå¨åŽčŖ
æļæå¨å
ļäģ莞å¤ä¸įæį莞įŊŽ URIīŧäģĨååēįå¯į įč¯ã",
- "I understand that other devices will no longer be able to synchronise, and will need to be reset the synchronisation information.": "ææįŊå
ļäģ莞å¤å°æ æŗįģ§įģåæĨīŧéčĻéįŊŽåæĨäŋĄæ¯ã",
- "For HS256/HS512 algorithms, provide the shared secret key. For ES256/ES512 algorithms, provide the pkcs8 PEM-formatted private key.": "寚äē HS256/HS512 įŽæŗīŧ蝎æäžå
ąäēĢå¯éĨã寚äē ES256/ES512 įŽæŗīŧ蝎æäž pkcs8 PEM æ ŧåŧįį§éĨã",
- "moduleLog.pathComponentTooLong": "æ¤čˇ¯åžä¸å
åĢčļ
čŋ ${maxBytes} ä¸Ē UTF-8 åčįæäģļææäģļ多åã卿äē Android å Linux æäģļįŗģįģä¸å¯čŊæ æŗæŖå¸¸åˇĨäŊã",
- "The connection test cannot add a signalling relay while P2P is active. Use the active relay settings, or disconnect P2P before testing.": "P2P å¤äēæ´ģå¨įļææļīŧčŋæĨæĩ蝿 æŗæˇģå äŋĄäģ¤ä¸įģ§ã蝎äŊŋį¨åŊåįæįä¸įģ§čŽžįŊŽīŧæå
æåŧ P2P åæĩč¯ã",
- "Ui.SetupWizard.ApplySettingsInitialisation.Guidance": "čŋäē莞įŊŽåæ´äŧæšååæĨæ°æŽįč§Ŗéæšåŧã蝎å¨éå¯åčŋååå§åæäŊä¸čĩˇåēį¨ã",
- "liveSyncReplicator.mismatchedTweakDetected": "æŖæĩå°čŽžå¤äšé´įé
įŊŽåå¨ä¸ä¸č´ãæ§čĄä¸æŦĄæå¨å¤åļå°å°č¯č§Ŗåŗæ¤éŽéĸã",
- "Saving without a successful connection test keeps this profile, but automatic synchronisation may fail until the connection is corrected.": "æĒéčŋčŋæĨæĩč¯å°ąäŋåäŧäŋįæ¤æĄŖæĄīŧäŊå¨čŋæĨäŋŽæŖäšåīŧčĒå¨åæĨå¯čŊäŧå¤ąč´Ĩã",
- "If you want to use `LiveSync`, you should broadcast changes. All `watching` peers which detects this will start the replication for fetching.": "åĻæčĻäŊŋ፠`LiveSync`īŧäŊ åēåŊåšŋæåæ´ãæææŖæĩå°åæ´į `watching` å¯šįæšéŊäŧå¯å¨å¤åļäģĨčˇåæ°æŽã",
- "Ui.SetupWizard.OutroNewP2PUser.GuidanceNotice": "P2P æ˛Ąæä¸åŋæåĄå¨å¯æŦå¯äžčĻįãæ¤æĨéǤåĒå夿Ŧ莞å¤īŧåŊå
ļäģ莞å¤čˇååå§æ°æŽæļīŧ蝎äŋææŦ莞å¤å¨įēŋã",
- "Ui.SetupWizard.ApplySettingsInitialisation.RemoteVerificationGuidance": "æ æŗį¨åŊåįåæŽåå å¯čŽžįŊŽéĒ蝿é
įŊŽįčŋį̝ãįģ§įģå¯čŊå¯ŧč´éå¯åčˇåå¤ąč´Ĩã",
- "Ui.SetupWizard.OutroNewP2PUser.GuidancePrimary": "įšå¯šįščŋæĨ厞é
įŊŽæåãæĨ䏿Ĩīŧå°į¨æŦåēä¸åŊåįæäģļæåģēæŦå° LiveSync æ°æŽåēã",
- "Restarting Obsidian is strongly recommended. Until restart, some changes may not take effect, and display may be inconsistent. Are you sure to restart now?": "åŧēįåģē莎éå¯ Obsidianãå¨éå¯äšåīŧé¨åæ´æšå¯čŊä¸äŧįæīŧæžį¤ēäšå¯čŊä¸ä¸č´ãįĄŽåŽį°å¨éå¯åīŧ",
- "Ui.SetupWizard.ApplySettingsInitialisation.RebuildOptionDesc": "፿Ŧåēä¸åŊåįæäģļéåģēæŦå°åčŋįĢ¯æ°æŽåēãå
ļäģæŖå¨åæĨį莞å¤äšååŋ
éĄģéįŊŽå
ļæŦå°åæĨã",
- "Ui.SetupWizard.ApplySettingsInitialisation.FetchOptionDesc": "éå¯åīŧį¨åŊåįčŋį̝åæĨæ°æŽéåģēæŦ莞å¤įæŦå°æ°æŽåēãåēä¸įæäģļéåäŧä¸č¯Ĩæ°æŽæ ¸å¯šååšļã",
- "The Group ID and passphrase are used to identify your group of devices. Make sure to use the same Group ID and passphrase on all devices you want to synchronise.": "įģ ID åå¯į įč¯į¨äēæ č¯äŊ į莞å¤įģãč¯ˇįĄŽäŋææčĻåæĨį莞å¤éŊäŊŋį¨į¸åįįģ ID åå¯į įč¯ã",
- "Changing this setting requires migrating existing data (a bit time may be taken) and restarting Obsidian. Please make sure to back up your data before proceeding.": "æ´æšæ¤čŽžįŊŽéčĻčŋį§ģį°ææ°æŽīŧå¯čŊéčĻä¸äēæļé´īŧåšļéå¯ Obsidianã蝎åĄåŋ
å¨įģ§įģäšåå¤äģŊæ°æŽã",
- "Furthermore, if conflicts are already present in the server data, they will be synchronised to this device as they are, and you will need to resolve them locally.": "æ¤å¤īŧåĻææåĄå¨æ°æŽä¸åˇ˛åå¨å˛įĒīŧåŽäģŦäŧåæ ˇåæĨå°æŦ莞å¤īŧäŊ éčĻ卿Ŧ莞å¤ä¸č§Ŗåŗã",
- "Selecting this option will result in this device joining the existing server. You need to fetching the existing synchronisation data from the server to this device.": "éæŠæ¤é饚åīŧæŦ莞å¤å°å å
Ĩį°ææåĄå¨ãäŊ éčĻææåĄå¨ä¸åˇ˛æįåæĨæ°æŽčˇåå°æŦ莞å¤ã",
- "In most cases, you should stick with the default algorithm (${algorithm}), This setting is only required if you have an existing Vault encrypted in a different format.": "大夿°æ
åĩä¸īŧäŊ åēåŊæ˛ŋį¨éģčŽ¤įŽæŗīŧ${algorithm}īŧãåĒæåŊäŊ æäģĨå
ļäģæ ŧåŧå å¯įį°æåēæļīŧæéčĻæ¤éĄščŽžįŊŽã",
- "Selecting this option will result in the current data on this device being used to initialise the server. Any existing data on the server will be completely overwritten.": "éæŠæ¤é饚åīŧå°į¨æŦ莞å¤įåŊåæ°æŽæĨåå§åæåĄå¨ãæåĄå¨ä¸į°æįæ°æŽå°čĸĢåŽå
¨čĻįã",
- "TURN server settings are only necessary if you are behind a strict NAT or firewall that prevents direct P2P connections. In most cases, you can leave these fields blank.": "åĒæåŊå¤äēéģæĸį´æĨ P2P čŋæĨįä¸Ĩæ ŧ NAT æé˛įĢåĸäšåæļīŧæéčĻ TURN æåĄå¨čŽžįŊŽã大夿°æ
åĩä¸čŋäēåæŽĩå¯äģĨįįŠēã",
- "If you want to store the data in a specific folder within the bucket, you can specify a folder prefix here. Otherwise, leave it blank to store data at the root of the bucket.": "åĻææŗææ°æŽåæžå¨å卿Ąļå
įæåŽæäģļ多ä¸īŧå¯äģĨ卿¤æåŽæäģļ多åįŧãåĻåįįŠēīŧæ°æŽå°åæžå¨å卿Ąļæ šįŽåŊã",
- ", please select the option that best describes the current state of your Vault. The application will then check your files in the most appropriate way based on your selection.": "īŧč¯ˇéæŠæįŦĻåäŊ åēåŊåįļæįé饚ãį¨åēäŧæ šæŽäŊ įéæŠīŧ፿åéįæšåŧæŖæĨäŊ įæäģļã",
- "Ui.SetupWizard.ApplySettingsInitialisation.BypassGuidance": "äģ
åēį¨čŋäē莞įŊŽå¯čŊäŊŋæŦ莞å¤ä¸å
ļį°æåæĨæ°æŽä¸å
ŧ厚ã蝎äģ
å¨įĄŽčޤæ ééåģēæļäŊŋį¨ã",
- "Ui.SetupWizard.RebuildEverythingP2P.Guidance": "æ¤æäŊäŧä¸ĸåŧæŦ莞å¤įæŦå° LiveSync æ°æŽåēīŧåšļ፿Ŧåēä¸åŊåįæäģļéåģēãåŽä¸äŧå 餿čĻįå
ļäģ莞å¤ä¸įæ°æŽã",
- "Also, please note that if you are using Peer-to-Peer synchronization, this configuration will be used when you switch to other methods and connect to a remote server in the future.": "åĻå¤č¯ˇæŗ¨æīŧåĻæäŊ æŖå¨äŊŋį¨įšå¯šįšåæĨīŧåŊäŊ æĨååæĸå°å
ļäģæšåŧåšļčŋæĨčŋį̝æåĄå¨æļīŧå°äŊŋ፿¤é
įŊŽã",
- "Therefore, we ask that you exercise extreme caution when configuring server information manually. If an incorrect passphrase is entered, the data on the server will become corrupted.": "å æ¤īŧæäģŦčĻæąäŊ 卿å¨é
įŊŽæåĄå¨äŋĄæ¯æļæ ŧå¤å°åŋãåĻæčžå
Ĩäēé蝝įå¯į įč¯īŧæåĄå¨ä¸įæ°æŽå°äŧæåã",
- "TweakMismatchResolve.Message.AutoAcceptCompatibleUndefined": "\nå莞å¤į莞įŊŽäŧŧäšåå¨åˇŽåŧãäŊ į°å¨å¯äģĨčĒå¨åēį¨å
ŧåŽšįæ´æšå°čŋäēé
įŊŽã\næ¯åĻčĻå¯į¨æ¤ãčĒ卿Ĩåã莞įŊŽīŧ",
- "After restarting, the database on this device will be rebuilt using data from the server. If there are any unsynchronised files in this vault, conflicts may occur with the server data.": "éå¯åīŧå°į¨æåĄå¨ä¸įæ°æŽéåģēæŦ莞å¤įæ°æŽåēãåĻææŦåē䏿æĒåæĨįæäģļīŧå¯čŊäŧ䏿åĄå¨æ°æŽäē§įå˛įĒã",
- "If you have unsynchronised changes in your Vault on this device, they will likely diverge from the server's versions after the reset. This may result in a large number of file conflicts.": "åĻææŦ莞å¤įåē䏿æĒåæĨįæ´æšīŧéįŊŽååŽäģŦåžå¯čŊ䏿åĄå¨ä¸įįæŦäē§įåæ§ãčŋå¯čŊå¯ŧč´å¤§éæäģļå˛įĒã",
- "Please be aware that the End-to-End Encryption passphrase is not validated until the synchronisation process actually commences. This is a security measure designed to protect your data.": "č¯ˇæŗ¨æīŧį̝å°į̝å å¯å¯į įč¯čĻįå°åæĨčŋį¨įæŖåŧå§åæäŧčĸĢæ ĄéĒãčŋæ¯ä¸ēäŋæ¤äŊ įæ°æŽč莞莥įåŽå
¨æĒæŊã",
- "This optional check uses Obsidian's internal request API and sends the credentials above to the CouchDB server. Use it only with a server you trust; administrator access may be required.": "æ¤å¯éæŖæĨäŊŋ፠Obsidian įå
é¨č¯ˇæą APIīŧåšļæä¸éĸįåæŽåéå° CouchDB æåĄå¨ã蝎äģ
寚äŊ äŋĄäģģįæåĄå¨äŊŋį¨īŧå¯čŊéčĻįŽĄįåæéã",
- "After restarting, the data on this device will be uploaded to the server as the 'master copy'. Please be aware that any unintended data currently on the server will be completely overwritten.": "éå¯åīŧæŦ莞å¤ä¸įæ°æŽå°äŊä¸ēãä¸ģ坿Ŧãä¸äŧ å°æåĄå¨ãč¯ˇæŗ¨æīŧæåĄå¨ä¸į°æįäģģäŊééĸææ°æŽéŊäŧčĸĢåŽå
¨čĻįã",
- "This will rebuild the local database on this device using the most recent data from the server. This action is designed to resolve synchronisation inconsistencies and restore correct functionality.": "čŋå°į¨æåĄå¨ä¸įææ°æ°æŽéåģēæŦ莞å¤įæŦå°æ°æŽåēãæ¤æäŊæ¨å¨č§ŖåŗåæĨä¸ä¸č´åšļæĸ夿Ŗå¸¸åčŊã",
- "In this scenario, Self-hosted LiveSync will recreate metadata for every file and deliberately generate conflicts. Where the file content is identical, these conflicts will be resolved automatically.": "卿¤æ
åŊĸä¸īŧSelf-hosted LiveSync äŧä¸ēæ¯ä¸Ēæäģļéåģēå
æ°æŽåšļææåļé å˛įĒãčĨæäģļå
厚åŽå
¨į¸åīŧčŋäēå˛įĒå°čĸĢčĒå¨č§Ŗåŗã",
- "This first setup has several short steps because it confirms encryption, the connection method, and which device provides the initial data. Once it is complete, additional devices can reuse a Setup URI.": "éĻæŦĄčŽžįŊŽåä¸ēå ä¸ĒįŽįæĨéǤīŧå ä¸ēåŽčĻįĄŽčŽ¤å 坿šåŧãčŋæĨæšåŧīŧäģĨåįąåĒå°čŽžå¤æäžåå§æ°æŽãåŽæåīŧå
ļäģ莞å¤å¯äģĨå¤į¨čŽžįŊŽ URIã",
- "We recommend that you copy your Vault folder to a safe location. This will provide a safeguard in case a large number of conflicts arise, or if you accidentally synchronise with an incorrect destination.": "æäģŦåģē莎äŊ æåēæäģļ多å¤åļå°åŽå
¨äŊįŊŽãčŋæ ˇå¨åēį°å¤§éå˛įĒīŧæä¸å°åŋåæĨå°éč¯¯įŽæ æļīŧčŊå¤ä¸åąäŋéã",
- "The IndexedDB adapter often offers superior performance in certain scenarios, but it has been found to cause memory leaks when used with LiveSync mode. When using LiveSync mode, please use IDB adapter instead.": "IndexedDB éé
å¨å¨æäēå翝䏿§čŊæ´åĨŊīŧäŊ厞åį°ä¸ LiveSync æ¨Ąåŧå፿ļäŧé æå
åæŗæŧãäŊŋ፠LiveSync æ¨Ąåŧæļīŧ蝎æšį¨ IDB éé
å¨ã",
- "Changing the encryption algorithm will prevent access to any data previously encrypted with a different algorithm. Ensure that all your devices are configured to use the same algorithm to maintain access to your data.": "æ´æšå å¯įŽæŗå°å¯ŧč´æ æŗčŽŋéŽæ¤åį¨å
ļäģįŽæŗå å¯įæ°æŽãč¯ˇįĄŽäŋææčŽžå¤éŊé
įŊŽä¸ēäŊŋį¨į¸åįŽæŗīŧäģĨäŋæå¯šæ°æŽįčŽŋéŽã",
- "Unless you are certain, selecting this options is bit dangerous. It assumes that the server configuration is compatible with this device. If this is not the case, data loss may occur. Please ensure you know what you are doing.": "é¤éäŊ éå¸¸įĄŽåŽīŧåĻåéæŠæ¤é饚æäēåąéŠãåŽååŽæåĄå¨é
įŊŽä¸æŦ莞å¤å
ŧ厚ãčĨä¸å
ŧ厚īŧå¯čŊäŧåįæ°æŽä¸ĸå¤ąãč¯ˇįĄŽäŋäŊ æ¸
æĨčĒåˇąå¨åäģäšã",
- "This procedure will first delete all existing synchronisation data from the server. Following this, the server data will be completely rebuilt, using the current state of your Vault on this device (including its local database) as": "æ¤æäŊäŧå
å 餿åĄå¨ä¸ææį°æįåæĨæ°æŽãéåīŧæåĄå¨æ°æŽå°čĸĢåŽå
¨éåģēīŧäģĨæŦ莞å¤ä¸äŊ åēįåŊåįļæīŧå
æŦå
ļæŦå°æ°æŽåēīŧäŊä¸ē",
- "If you cannot avoid CORS issues, you might want to try this option. It uses Obsidian's internal API to communicate with the S3 server. Not compliant with web standards, but works. Note that this might break in future Obsidian versions.": "åĻææ æŗéŋåŧ CORS éŽéĸīŧå¯äģĨč¯č¯æ¤é饚ãåŽäŊŋ፠Obsidian įå
é¨ API ä¸ S3 æåĄå¨éäŋĄãä¸įŦĻå Web æ åīŧäŊå¯į¨ãč¯ˇæŗ¨æīŧæĒæĨį Obsidian įæŦå¯čŊäŧäŊŋå
ļå¤ąæã",
- "If you cannot avoid CORS issues, you might want to try this option. It uses Obsidian's internal API to communicate with the CouchDB server. Not compliant with web standards, but works. Note that this might break in future Obsidian versions.": "åĻææ æŗéŋåŧ CORS éŽéĸīŧå¯äģĨč¯č¯æ¤é饚ãåŽäŊŋ፠Obsidian įå
é¨ API ä¸ CouchDB æåĄå¨éäŋĄãä¸įŦĻå Web æ åīŧäŊå¯į¨ãč¯ˇæŗ¨æīŧæĒæĨį Obsidian įæŦå¯čŊäŧäŊŋå
ļå¤ąæã",
"You should perform this operation only in exceptional circumstances, such as when the server data is completely corrupted, when changes on all other devices are no longer needed, or when the database size has become unusually large in comparison to the Vault size.": "äŊ åĒåēå¨įšæŽæ
åĩ䏿§čĄæ¤æäŊīŧäžåĻæåĄå¨æ°æŽåŽå
¨æåãå
ļäģææčŽžå¤ä¸įæ´æšéŊä¸åéčĻīŧææ°æŽåēäŊ᧝į¸å¯šäēåēäŊ᧝åŧ常åē大æļã",
- "JWT (JSON Web Token) authentication allows you to securely authenticate with the CouchDB server using tokens. Ensure that your CouchDB server is configured to accept JWTs and that the provided key and settings match the server's configuration. Incidentally, I have not verified it very thoroughly.": "JWTīŧJSON Web TokenīŧčēĢäģŊéĒč¯čŽŠäŊ å¯äģĨį¨äģ¤įåŽå
¨å°éčŋ CouchDB æåĄå¨éĒč¯čēĢäģŊãč¯ˇįĄŽäŋäŊ į CouchDB æåĄå¨åˇ˛é
įŊŽä¸ēæĨå JWTīŧ䏿æäžįå¯éĨå莞įŊŽä¸æåĄå¨é
įŊŽį¸įŦĻãéĄēå¸Ļ䏿īŧæåšļæ˛Ąæé常åŊģåēå°éĒč¯čŋåŽã",
- "Enabling end-to-end encryption ensures that your data is encrypted on your device before being sent to the remote server. This means that even if someone gains access to the server, they won't be able to read your data without the passphrase. Make sure to remember your passphrase, as it will be required to decrypt your data on other devices.": "å¯į¨į̝å°į̝å å¯å¯įĄŽäŋæ°æŽå¨åéå°čŋį̝æåĄå¨äšå厞卿Ŧ莞å¤ä¸å å¯ãčŋæåŗįåŗäŊŋæäēēčˇåžäēæåĄå¨įčŽŋéŽæéīŧæ˛Ąæå¯į įč¯äšæ æŗč¯ģåäŊ įæ°æŽã蝎åĄåŋ
莰äŊäŊ įå¯į įč¯īŧå ä¸ēå¨å
ļäģ莞å¤ä¸č§Ŗå¯æ°æŽæļéčĻį¨å°åŽã",
- "Obfuscating properties (e.g., path of file, size, creation and modification dates) adds an additional layer of security by making it harder to identify the structure and names of your files and folders on the remote server. This helps protect your privacy and makes it more difficult for unauthorized users to infer information about your data.": "æˇˇæˇåąæ§īŧäžåĻæäģļ莝åžã大å°ãååģēåäŋŽæšæĨæīŧéčŋ莊čŋį̝æåĄå¨ä¸æ´éžč¯åĢäŊ æäģļåæäģļ多įįģæä¸åį§°īŧåĸå äēä¸åąéĸå¤įåŽå
¨äŋéãčŋæåŠäēäŋæ¤äŊ įéį§īŧäščŽŠæĒææį¨æˇæ´éžæ¨æåēå
ŗäēäŊ æ°æŽįäŋĄæ¯ã"
+ "you wanted(Thank you)!": "äŊ čĻæąįæäŊīŧč°ĸč°ĸīŧīŧ"
}
diff --git a/src/common/messagesYAML/en.yaml b/src/common/messagesYAML/en.yaml
index 40b46387..cc25bba9 100644
--- a/src/common/messagesYAML/en.yaml
+++ b/src/common/messagesYAML/en.yaml
@@ -2479,3 +2479,156 @@ You should perform this operation only in exceptional circumstances, such as whe
"Synchronisation Issues Detected": "Synchronisation Issues Detected"
"Use the detailed flow": "Use the detailed flow"
"We are about to retrieve the remote data.\n\nFirstly, how shall we handle the data retrieved from this remote source?\n\n- **${NEWER_WINS}**: Compares the modified time of files and takes the newer one.\n If you have been using Self-hosted LiveSync and have made changes on multiple devices, this option may be suitable for you as it tries to merge changes based on modified time.\n- **${REMOTE_WINS}**: Remote data is the source of truth.\n If you are new to using Self-hosted LiveSync. This option may be easiest to understand and get started with.\n It will overwrite all your local files with the remote data, so please make sure you have a backup if there is any important data in your vault.\n- **${DETAILED}**: Opens the detailed setup wizard.\n If you want to have more control over the synchronisation process, or want to review the changes before applying, you can choose this option to use the detailed flow.": "We are about to retrieve the remote data.\n\nFirstly, how shall we handle the data retrieved from this remote source?\n\n- **${NEWER_WINS}**: Compares the modified time of files and takes the newer one.\n If you have been using Self-hosted LiveSync and have made changes on multiple devices, this option may be suitable for you as it tries to merge changes based on modified time.\n- **${REMOTE_WINS}**: Remote data is the source of truth.\n If you are new to using Self-hosted LiveSync. This option may be easiest to understand and get started with.\n It will overwrite all your local files with the remote data, so please make sure you have a backup if there is any important data in your vault.\n- **${DETAILED}**: Opens the detailed setup wizard.\n If you want to have more control over the synchronisation process, or want to review the changes before applying, you can choose this option to use the detailed flow."
+SettingService.ConfigurationPassphraseDeviceLocalDescription:
+ This passphrase stays on this device. When another device needs to decrypt
+ protected settings without a saved key, enter the same passphrase at
+ start-up.
+SettingService.ConfigurationPassphraseTitle: Configuration passphrase
+SettingService.ConfigurationPassphraseRetryTitle: Configuration could not be
+ decrypted. Enter the configuration passphrase again.
+SettingService.ConfigurationPassphraseCancelled: Configuration passphrase entry
+ was cancelled or unavailable. Start-up was stopped; saved settings were
+ retained.
+SettingService.ConfigurationEncryptionSaveFailed: Configuration encryption could
+ not be saved. Verify the saved settings and device key before retrying.
+SettingService.ConfigurationPassphraseRollbackFailed: The configuration
+ passphrase could not be restored after a write failure. Verify the saved
+ settings and device key before retrying.
+SettingService.ConfigurationCouldNotBeDecrypted: Existing configuration could
+ not be decrypted. The configuration key was not changed.
+Active Remote Type: Active Remote Type
+Application ID: Application ID
+Auto-accept compatible tweak mismatches: Auto-accept compatible tweak mismatches
+Automatically accepts mismatches that are compatible but potentially lossy by comparing tweak modification times.:
+ Automatically accepts mismatches that are compatible but potentially lossy
+ by comparing tweak modification times.
+Automatically broadcast changes to connected peers: Automatically broadcast changes to connected peers
+Automatically start P2P connection on launch: Automatically start P2P connection on launch
+Chunk Splitter: Chunk Splitter
+Chunk revisions are always derived from their content. This stored key is retained only for compatibility.:
+ Chunk revisions are always derived from their content. This stored key is
+ retained only for compatibility.
+Content-derived chunk revisions (obsolete setting): Content-derived chunk revisions (obsolete setting)
+"Custom headers for requesting the CouchDB. e.g. `x-custom-header1: value1\n x-custom-header2: value2`": |-
+ Custom headers for requesting the CouchDB. e.g. `x-custom-header1: value1
+ x-custom-header2: value2`
+"Custom headers for requesting the bucket. e.g. `x-custom-header1: value1\n x-custom-header2: value2`": |-
+ Custom headers for requesting the bucket. e.g. `x-custom-header1: value1
+ x-custom-header2: value2`
+Desktop only; uses more battery and network.: Desktop only; uses more battery and network.
+Effectively a directory. Should end with `/`. e.g., `vault-name/`.: Effectively a directory. Should end with `/`. e.g., `vault-name/`.
+Enable Developers' Debug Tools (If available).: Enable Developers' Debug Tools (If available).
+Enable P2P Synchronization: Enable P2P Synchronization
+Enable forcePathStyle: Enable forcePathStyle
+Enable per-file customization sync: Enable per-file customization sync
+Encrypt internal file Properties: Encrypt internal file Properties
+Encrypt file properties (paths, times, sizes, and Chunk references) used by Hidden File Sync and Customisation Sync. This affects future writes; manually rebuild the remote database to protect existing properties. Requires E2EE V2, Property Encryption, and compatible clients on every device.:
+ Encrypt file properties (paths, times, sizes, and Chunk references) used by
+ Hidden File Sync and Customisation Sync. This affects future writes;
+ manually rebuild the remote database to protect existing properties.
+ Requires E2EE V2, Property Encryption, and compatible clients on every
+ device.
+Encryption passphrase. If changed, you should overwrite the server's database with the new (encrypted) files.:
+ Encryption passphrase. If changed, you should overwrite the server's
+ database with the new (encrypted) files.
+End-to-End Encryption Algorithm: End-to-End Encryption Algorithm
+File prefix on the bucket: File prefix on the bucket
+Files with modification times greater than this value (in seconds since the Unix epoch) will not have their events reflected. Set to 0 to disable this limit.:
+ Files with modification times greater than this value (in seconds since the
+ Unix epoch) will not have their events reflected. Set to 0 to disable this
+ limit.
+If enabled, changes will be automatically broadcasted to all connected peers. Notified peers will start fetching the changes.:
+ If enabled, changes will be automatically broadcasted to all connected
+ peers. Notified peers will start fetching the changes.
+If enabled, efficient per-file customization sync will be used. A minor migration is required when enabling this feature, and all devices must be updated to v0.23.18. Enabling this feature will result in losing compatibility with older versions.:
+ If enabled, efficient per-file customization sync will be used. A minor
+ migration is required when enabling this feature, and all devices must be
+ updated to v0.23.18. Enabling this feature will result in losing
+ compatibility with older versions.
+If enabled, the P2P connection will be automatically started when the application launches.:
+ If enabled, the P2P connection will be automatically started when the
+ application launches.
+If enabled, the file properties will be encrypted in the remote database. This is useful for protecting sensitive information in file paths, sizes, and IDs of its chunks. If you are using V1 E2EE, this only obfuscates the file path.:
+ If enabled, the file properties will be encrypted in the remote database.
+ This is useful for protecting sensitive information in file paths, sizes,
+ and IDs of its chunks. If you are using V1 E2EE, this only obfuscates the
+ file path.
+If enabled, the forcePathStyle option will be used for bucket operations.: If enabled, the forcePathStyle option will be used for bucket operations.
+If enabled, the plugin will not attempt to connect to the remote database even if the chunk was not found locally.:
+ If enabled, the plugin will not attempt to connect to the remote database
+ even if the chunk was not found locally.
+If enabled, the request API will be used to avoid `inevitable` CORS problems. This is a workaround and may not work in all cases. PLEASE READ THE DOCUMENTATION BEFORE USING THIS OPTION. This is a less-secure option.:
+ If enabled, the request API will be used to avoid `inevitable` CORS
+ problems. This is a workaround and may not work in all cases. PLEASE READ
+ THE DOCUMENTATION BEFORE USING THIS OPTION. This is a less-secure option.
+If this enabled, JWT will be used for authentication.: If this enabled, JWT will be used for authentication.
+Internal timestamp for resolving compatible tweak mismatches.: Internal timestamp for resolving compatible tweak mismatches.
+Keep replication active in the background: Keep replication active in the background
+Key ID: Key ID
+Keypair or pre-shared key: Keypair or pre-shared key
+Last tweak modified timestamp: Last tweak modified timestamp
+Longest chunk line threshold value (Not Configurable from the UI Now).: Longest chunk line threshold value (Not Configurable from the UI Now).
+MB per request: MB per request
+Maximum file modification time for reflected file events: Maximum file modification time for reflected file events
+Maximum request size for manually resending chunks: Maximum request size for manually resending chunks
+Minimum Chunk Size (Not Configurable from the UI Now).: Minimum Chunk Size (Not Configurable from the UI Now).
+Move remotely deleted files to the trash, instead of deleting. (This setting is ineffective from Obsidian v1.7.2 onwards, as deletion always respects your Obsidian preferences.):
+ Move remotely deleted files to the trash, instead of deleting. (This setting
+ is ineffective from Obsidian v1.7.2 onwards, as deletion always respects
+ your Obsidian preferences.)
+Now we can choose how to split the chunks; V3 is the most efficient. If you have troubled, please make this Default or Legacy.:
+ Now we can choose how to split the chunks; V3 is the most efficient. If you
+ have troubled, please make this Default or Legacy.
+Please use V2, V1 is deprecated and will be removed in the future, It was not a very appropriate algorithm. Only for compatibility V1 is kept.:
+ Please use V2, V1 is deprecated and will be removed in the future, It was
+ not a very appropriate algorithm. Only for compatibility V1 is kept.
+Process files even if seems to be corrupted: Process files even if seems to be corrupted
+Rotation Duration: Rotation Duration
+Signalling Relays: Signalling Relays
+Subject (whoami): Subject (whoami)
+TURN Servers: TURN Servers
+The Application ID for P2P connection. This should be same among your devices. Default is 'self-hosted-livesync' and could not be modified from the UI.:
+ The Application ID for P2P connection. This should be same among your
+ devices. Default is 'self-hosted-livesync' and could not be modified from
+ the UI.
+The Nostr relay servers to establish connections for P2P connections. Multiple servers can be separated by commas.:
+ The Nostr relay servers to establish connections for P2P connections.
+ Multiple servers can be separated by commas.
+The Passphrase for P2P connection. This should be same among your devices.: The Passphrase for P2P connection. This should be same among your devices.
+The Room ID for P2P connection. This should be same among your devices.: The Room ID for P2P connection. This should be same among your devices.
+The Rotation duration of token in minutes. Each generated tokens will be valid only within this duration.:
+ The Rotation duration of token in minutes. Each generated tokens will be
+ valid only within this duration.
+The TURN servers to use for P2P connections. Multiple servers can be separated by commas.:
+ The TURN servers to use for P2P connections. Multiple servers can be
+ separated by commas.
+The algorithm used for JWT authentication.: The algorithm used for JWT authentication.
+The credential/password for the TURN servers.: The credential/password for the TURN servers.
+The key (PSK in HSxxx in base64, or private key in ESxxx in PEM) used for JWT authentication.:
+ The key (PSK in HSxxx in base64, or private key in ESxxx in PEM) used for
+ JWT authentication.
+The key ID. this should be matched with CouchDB->jwt_keys->ALG:_`kid`.: The key ID. this should be matched with CouchDB->jwt_keys->ALG:_`kid`.
+The subject for JWT authentication. Mostly username.: The subject for JWT authentication. Mostly username.
+The username for the TURN servers.: The username for the TURN servers.
+Use JWT instead of Basic Authentication: Use JWT instead of Basic Authentication
+Use Only Local Chunks: Use Only Local Chunks
+Use Request API to avoid `inevitable` CORS problem: Use Request API to avoid `inevitable` CORS problem
+While enabled, it causes very performance impact but debugging replication testing and other features will be enabled. Please disable this if you have not read the source code. Requires restart of Obsidian. Sometimes there is no implementation.:
+ While enabled, it causes very performance impact but debugging replication
+ testing and other features will be enabled. Please disable this if you have
+ not read the source code. Requires restart of Obsidian. Sometimes there is
+ no implementation.
+You can enable this setting to process the files with size mismatches, these files can be created by some APIs or integrations.:
+ You can enable this setting to process the files with size mismatches, these
+ files can be created by some APIs or integrations.
+xxhash64 is the current default.: xxhash64 is the current default.
+Allow sleep during synchronisation: Allow sleep during synchronisation
+Allow the operating system to sleep while finite synchronisation operations are in progress.:
+ Allow the operating system to sleep while finite synchronisation operations
+ are in progress.
+Allow sleep during synchronisation on the desktop: Allow sleep during synchronisation on the desktop
+Desktop only. Allow sleep on this device even when the general option is disabled.:
+ Desktop only. Allow sleep on this device even when the general option is
+ disabled.
+Enable advanced mode: Enable advanced mode
+Enable power user mode: Enable power user mode
diff --git a/src/common/messagesYAML/zh.yaml b/src/common/messagesYAML/zh.yaml
index ff2cfb05..3eee2c4a 100644
--- a/src/common/messagesYAML/zh.yaml
+++ b/src/common/messagesYAML/zh.yaml
@@ -229,19 +229,19 @@ Doctor:
## ${name}
- | Current | Ideal |
+ | åŊååŧ | įæŗåŧ |
|:---:|:---:|
| ${current} | ${ideal} |
- **Recommendation Level:** ${level}
+ **åģē莎įē§åĢīŧ** ${level}
- ### Why this has been detected?
+ ### ä¸ēäŊæŖæĩå°æ¤éŽéĸīŧ
${reason}
${note}
- Fix this to the ideal value?
+ æ¯åĻå°å
ļäŋŽæŖä¸ēįæŗåŧīŧ
Title: Self-hosted LiveSync é
įŊŽč¯æ
TitleAlmostDone: å
¨é¨åŽæ!
TitleFix: äŋŽå¤éŽéĸ ${current}/${total}
@@ -257,13 +257,8 @@ Doctor:
SomeSkipped: æäģŦå°æäēéŽéĸįįģäēäģĨåå¤įãæ¯åĻčĻå¨ä¸æŦĄå¯å¨æļåæŦĄč¯ĸéŽæ¨īŧ
RULES:
E2EE_V02500:
- REASON: The End-to-End Encryption has got now more robust and faster. Also
- because, the previous E2EE was found to be compromised in a re-conducted
- code review. It should be applied as soon as possible. Really apologises
- for your inconvenience. And, this setting is not forward compatible. All
- synchronised devices must be updated to v0.25.0 or higher. Rebuilds are
- not required and will be converted from the new transfer to the new
- format, However, it is recommended to rebuild whenever possible.
+ REASON: į̝å°į̝å å¯į°å¨ååžæ´å ፺åĨãæ´åŋĢãåĻå¤īŧäšåįį̝å°į̝å å¯å¨éæ°čŋčĄįäģŖį åŽĄæĨä¸čĸĢåį°åå¨åŽå
¨æŧæ´īŧå æ¤åēå°ŊåŋĢå¯į¨ã寚æ¤įģäŊ å¸ĻæĨįä¸äžŋīŧæäģŦæˇąčĄ¨ææãæ¤å¤īŧč¯Ĩ莞įŊŽä¸ååå
ŧ厚īŧææåˇ˛åæĨį莞å¤éŊåŋ
éĄģæ´æ°å°
+ v0.25.0 ææ´éĢįæŦãæ ééåģēīŧæ°æŽäŧäģæ°įäŧ čžæšåŧčŊŦæĸä¸ēæ°æ ŧåŧīŧä¸čŋäģåģē莎å°Ŋå¯čŊčŋčĄéåģēã
Document History: ææĄŖåå˛čްåŊ
Duplicate: å¤åļ
Duplicate remote: å¤åļčŋį̝é
įŊŽ
@@ -438,9 +433,9 @@ K:
Peer: Peer
ScanCustomization: æĢæčĒåŽäš
short_p2p_sync: P2PåæĨ(%{exp})
- title_p2p_sync: Peer-to-PeeråæĨ
+ title_p2p_sync: Peer-to-Peer åæĨ
Keep empty folder: äŋįįŠēæäģļ多
-lang_def: Default
+lang_def: éģ莤
lang-de: Deutsche
lang-def: "%{lang_def}"
lang-es: EspaÃąol
@@ -866,7 +861,7 @@ obsidianLiveSyncSettingTab:
okCorsOriginMatched: â CORS æēæŖå¸¸
okCorsOrigins: â cors.origins 莞įŊŽæŖįĄŽ
okEnableCors: â httpd.enable_cors 莞įŊŽæŖįĄŽ
- okEnableCorsChttpd: â chttpd.enable_cors is ok.
+ okEnableCorsChttpd: â chttpd.enable_cors 莞įŊŽæŖįĄŽ
okMaxDocumentSize: â couchdb.max_document_size 莞įŊŽæŖįĄŽ
okMaxRequestSize: â chttpd.max_http_request_size 莞įŊŽæŖįĄŽ
okRequireValidUser: â chttpd.require_valid_user 莞įŊŽæŖįĄŽ
@@ -985,15 +980,14 @@ P2P:
some bugs, security issues, and other issues. Please use this feature at
your own risk. Please contribute to the development of this feature.
Summary: What is this feature? (and some important notes, please read once)
- NotEnabled: "%{title_p2p_sync} is not enabled. We cannot open a new connection."
- P2PReplication: "%{P2P} Replication"
+ NotEnabled: "%{title_p2p_sync} æĒå¯į¨īŧæ æŗåģēįĢæ°čŋæĨã"
+ P2PReplication: "%{P2P} å¤åļ"
PaneTitle: "%{long_p2p_sync}"
- ReplicatorInstanceMissing: P2P Sync replicator is not found, possibly not have
- been configured or enabled.
- SeemsOffline: Peer ${name} seems offline, skipped.
- SyncAlreadyRunning: P2P Sync is already running.
- SyncCompleted: P2P Sync completed.
- SyncStartedWith: P2P Sync with ${name} have been started.
+ ReplicatorInstanceMissing: æĒæžå° P2P åæĨå¤åļå¨īŧå¯čŊå°æĒé
įŊŽææĒå¯į¨ã
+ SeemsOffline: å¯šįæš ${name} äŧŧäšå¤äēįĻģįēŋįļæīŧ厞莺čŋã
+ SyncAlreadyRunning: P2P åæĨ厞å¨čŋčĄã
+ SyncCompleted: P2P åæĨåˇ˛åŽæã
+ SyncStartedWith: 厞åŧå§ä¸ ${name} į P2P åæĨã
paneMaintenance:
markDeviceResolvedAfterBackup: 蝎å¨åŽæå¤äģŊåå°æ¤čŽžå¤æ 莰ä¸ē厞å¤įã
remoteLockedAndDeviceNotAccepted: čŋįĢ¯æ°æŽåē厞éåŽīŧ䏿¤čŽžå¤å°æĒčĸĢæĨåã
@@ -1070,89 +1064,50 @@ Recreate missing chunks for all files: ä¸ēæææäģļéåģēįŧēå¤ąį chunks
RedFlag:
Fetch:
Method:
- Desc: >-
- How do you want to fetch?
+ Desc: |-
+ äŊ 叿åĻäŊčˇåīŧ
+ - %{RedFlag.Fetch.Method.FetchSafer}ã
+ **äŊæĩé**īŧ**éĢ CPU**īŧ**äŊéŖéŠ**
+ å¨äģĨ䏿
åĩ䏿¨čäŊŋį¨īŧ
+ - æäģļå¯čŊä¸ä¸č´
+ - æäģļæ°éä¸å¤
+ - %{RedFlag.Fetch.Method.FetchSmoother}ã
+ **äŊæĩé**īŧ**ä¸į CPU**īŧ**äŊå°ä¸įéŖéŠ**
+ å¨äģĨ䏿
åĩ䏿¨čäŊŋį¨īŧ
+ - æäģļåēæŦä¸č´
+ - äŊ æå¤§éæäģļã
+ - %{RedFlag.Fetch.Method.FetchTraditional}ã
+ **éĢæĩé**īŧ**äŊ CPU**īŧ**äŊå°ä¸įéŖéŠ**
- - %{RedFlag.Fetch.Method.FetchSafer}.
- **Low Traffic**, **High CPU**, **Low Risk**
- Recommended if ...
- - Files possibly inconsistent
- - Files were not so much
- - %{RedFlag.Fetch.Method.FetchSmoother}.
- **Low Traffic**, **Moderate CPU**, **Low to Moderate Risk**
- Recommended if ...
- - Files probably consistent
- - You have a lot of files.
- - %{RedFlag.Fetch.Method.FetchTraditional}.
- **High Traffic**, **Low CPU**, **Low to Moderate Risk**
-
- >[!INFO]- Details
-
- > ## %{RedFlag.Fetch.Method.FetchSafer}.
-
- > **Low Traffic**, **High CPU**, **Low Risk**
-
- > This option first creates a local database using existing local files
- before fetching data from the remote source.
-
- > If matching files exist both locally and remotely, only the
- differences between them will be transferred.
-
- > However, files present in both locations will initially be handled as
- conflicted files. They will be resolved automatically if they are not
- actually conflicted, but this process may take time.
-
- > This is generally the safest method, minimizing data loss risk.
-
- > ## %{RedFlag.Fetch.Method.FetchSmoother}.
-
- > **Low Traffic**, **Moderate CPU**, **Low to Moderate Risk** (depending
- operation)
-
- > This option first creates chunks from local files for the database,
- then fetches data. Consequently, only chunks missing locally are
- transferred. However, all metadata is taken from the remote source.
-
- > Local files are then compared against this metadata at launch. The
- content considered newer will overwrite the older one (by modified
- time). This outcome is then synchronised back to the remote database.
-
- > This is generally safe if local files are genuinely the latest
- timestamp. However, it can cause problems if a file has a newer
- timestamp but older content (like the initial `welcome.md`).
-
- > This uses less CPU and faster than
- "%{RedFlag.Fetch.Method.FetchSafer}", but it may lead to data loss if
- not used carefully.
-
- > ## %{RedFlag.Fetch.Method.FetchTraditional}.
-
- > **High Traffic**, **Low CPU**, **Low to Moderate Risk** (depending
- operation)
-
- > All things will be fetched from the remote.
-
- > Similar to the %{RedFlag.Fetch.Method.FetchSmoother}, but all chunks
- are fetched from the remote source.
-
- > This is the most traditional way to fetch, typically consuming the
- most network traffic and time. It also carries a similar risk of
- overwriting remote files to the '%{RedFlag.Fetch.Method.FetchSmoother}'
- option.
-
- > However, it is often considered the most stable method because it is
- the longest-established and most straightforward approach.
- FetchSafer: Create a local database once before fetching
- FetchSmoother: Create local file chunks before fetching
- FetchTraditional: Fetch everything from the remote
- Title: How do you want to fetch?
+ >[!INFO]- č¯Ļæ
+ > ## %{RedFlag.Fetch.Method.FetchSafer}ã
+ > **äŊæĩé**īŧ**éĢ CPU**īŧ**äŊéŖéŠ**
+ > æ¤é饚äŧå
åŠį¨į°æįæŦå°æäģļååģēæŦå°æ°æŽåēīŧįļååäģčŋ፿ēčˇåæ°æŽã
+ > åĻææŦå°åčŋį¨åå¨į¸åįæäģļīŧååĒäŧäŧ čžä¸¤č
äšé´įåˇŽåŧã
+ > äŊæ¯īŧ两螚éŊåå¨įæäģļæåäŧčĸĢč§ä¸ēå˛įĒæäģļãåĻæåŽäģŦåŽé
ä¸åšļæ å˛įĒīŧå°äŧčĸĢčĒå¨č§Ŗåŗīŧä¸čŋčŋä¸čŋį¨å¯čŊéčĻä¸äēæļé´ã
+ > čŋéå¸¸æ¯æåŽå
¨įæšæŗīŧčŊå°æ°æŽä¸ĸå¤ąéŖéŠéå°æäŊã
+ > ## %{RedFlag.Fetch.Method.FetchSmoother}ã
+ > **äŊæĩé**īŧ**ä¸į CPU**īŧ**äŊå°ä¸įéŖéŠ**īŧč§å
ˇäŊæäŊčåŽīŧ
+ > æ¤é饚äŧå
æ šæŽæŦå°æäģļä¸ēæ°æŽåēååģēæ°æŽåīŧįļååčˇåæ°æŽãå æ¤īŧåĒäŧäŧ čžæŦå°įŧēå°įæ°æŽåãäŊæ¯īŧææå
æ°æŽéŊåčĒčŋ፿ēã
+ > å¯å¨æļīŧįŗģįģäŧå°æŦå°æäģļä¸čŋäēå
æ°æŽčŋčĄæ¯čžãčĸĢ莤ä¸ēæ´æ°įå
厚īŧæäŋŽæšæļé´å¤æīŧå°čĻįčžæ§įįæŦãéåīŧčŋä¸įģæäŧåæĨåčŋ፿°æŽåēã
+ > åĻææŦå°æäģļįæļé´æŗįĄŽåŽæ¯ææ°įīŧčŋé常æ¯åŽå
¨įãäŊåĻææä¸Ēæäģļįæļé´æŗčžæ°ãå
厚å´čžæ§īŧäžåĻæåį `welcome.md`īŧīŧå°ąå¯čŊåŧåéŽéĸã
+ > čŋį§æšåŧæ¯â%{RedFlag.Fetch.Method.FetchSafer}âå ፿´å°į CPUãéåēĻæ´åŋĢīŧäŊåĻæäŊŋį¨ä¸åŊīŧå¯čŊäŧå¯ŧč´æ°æŽä¸ĸå¤ąã
+ > ## %{RedFlag.Fetch.Method.FetchTraditional}ã
+ > **éĢæĩé**īŧ**äŊ CPU**īŧ**äŊå°ä¸įéŖéŠ**īŧč§å
ˇäŊæäŊčåŽīŧ
+ > ææå
厚éŊå°äģčŋį¨čˇåã
+ > ä¸ %{RedFlag.Fetch.Method.FetchSmoother} į¸äŧŧīŧäŊæææ°æŽåéŊäģčŋ፿ēčˇåã
+ > čŋæ¯æäŧ įģįčˇåæšåŧīŧé常æļčæå¤įįŊįģæĩéåæļé´ãä¸â%{RedFlag.Fetch.Method.FetchSmoother}âé饚įąģäŧŧīŧåŽåæ ˇåå¨čĻįčŋ፿äģļįéŖéŠã
+ > ä¸čŋīŧåŽé常čĸĢ莤ä¸ēæ¯æį¨ŗåŽįæšæŗīŧå ä¸ēåŽæ¯å垿æ äš
ãæį´æĨįåæŗã
+ FetchSafer: čˇååå
ååģē䏿ŦĄæŦå°æ°æŽåē
+ FetchSmoother: čˇååå
ååģēæŦå°æäģļįæ°æŽå
+ FetchTraditional: äģčŋį¨čˇåå
¨é¨å
厚
+ Title: äŊ æŗåĻäŊčˇåīŧ
FetchRemoteConfig:
Buttons:
- Cancel: No, use local settings
- Fetch: Yes, fetch and apply remote settings
- Message: Do you want to fetch and apply remotely stored preference settings to
- the device?
- Title: Fetch Remote Configuration
+ Cancel: åĻīŧäŊŋ፿Ŧå°čŽžįŊŽ
+ Fetch: æ¯īŧčˇååšļåēį¨čŋį¨čŽžįŊŽ
+ Message: æ¯åĻčĻčˇåčŋį¨äŋåįååĨŊ莞įŊŽīŧåšļå°å
ļåēį¨å°æ¤čŽžå¤īŧ
+ Title: čˇåčŋį¨é
įŊŽ
Reduces storage space by discarding all non-latest revisions. This requires the same amount of free space on the remote server and the local client.: éčŋä¸ĸåŧææéææ°įæŦæĨåå°åå¨įŠēé´ãčŋéčĻčŋ፿åĄå¨åæŦå°åŽĸæˇį̝å
ˇå¤į¸åæ°éįå¯į¨įŠēé´ã
Reducing the frequency with which on-disk changes are reflected into the DB: éäŊå°įŖįä¸įæ´æšåæ å°æ°æŽåēä¸įéĸį
Refresh: 县°
@@ -1178,9 +1133,9 @@ Replicator:
Dialogue:
Locked:
Action:
- Dismiss: Cancel for reconfirmation
- Fetch: Reset Synchronisation on This Device
- Unlock: Unlock the remote database
+ Dismiss: åæļåšļéæ°įĄŽčŽ¤
+ Fetch: éįŊŽæ¤čŽžå¤ä¸įåæĨ
+ Unlock: č§Ŗéčŋ፿°æŽåē
Message:
_value: >
Remote database is locked. This is due to a rebuild on one of the
@@ -1199,14 +1154,14 @@ Replicator:
This method can only be used if we are already reliably synchronised by other replication methods. This does not simply mean that we have the same files. If you are not sure, you should avoid it.
- %{Replicator.Dialogue.Locked.Action.Dismiss}
This will cancel the operation. And we will asked again on next request.
- Fetch: Fetch all has been scheduled. Plug-in will be restarted to perform it.
- Unlocked: The remote database has been unlocked. Please retry the operation.
- Title: Locked
+ Fetch: åˇ˛åŽæčˇåå
¨é¨å
厚ãæäģļå°éå¯äģĨæ§čĄč¯ĨæäŊã
+ Unlocked: čŋ፿°æŽåē厞觪éã蝎éč¯č¯ĨæäŊã
+ Title: 厞éåŽ
Message:
- Cleaned: Database cleaning up is in process. replication has been cancelled
- InitialiseFatalError: No replicator is available, this is the fatal error.
- Pending: Some file events are pending. Replication has been cancelled.
- SomeModuleFailed: Replication has been cancelled by some module failure
+ Cleaned: æ°æŽå翏
įæŖå¨čŋčĄä¸īŧå¤åļ厞åæļã
+ InitialiseFatalError: æ˛Ąæå¯į¨įå¤åļå¨īŧčŋæ¯ä¸ä¸Ēč´åŊé蝝ã
+ Pending: é¨åæäģļäēäģļäģå¨įåž
å¤įãå¤åļ厞åæļã
+ SomeModuleFailed: įąäēæä¸Ēæ¨Ąååēéīŧå¤åļ厞åæļã
VersionUpFlash: An update has been detected. Please open the Settings dialogue
and check the Change Log. Replication has been cancelled.
Requires restart of Obsidian: éčĻéå¯ Obsidian
@@ -1270,7 +1225,7 @@ Scram!: į´§æĨå¤įŊŽ
Seconds, 0 to disable: į§,0ä¸ēįĻį¨
Seconds. Saving to the local database will be delayed until this value after we stop typing or saving.: "į§ã卿äģŦåæĸčžå
Ĩæäŋåå,äŋåå°æŦå°æ°æŽåēå°åģļčŋæ¤åŧ "
Secret Access Key: į§æčŽŋéŽå¯éĨ
-Secret Key: Secret Key
+Secret Key: į§æå¯éĨ
Select active P2P remote: éæŠįæį P2P čŋį̝
Select All Shiny: å
¨é Shiny
Select Flagged Shiny: éæŠåˇ˛æ 莰į Shiny
@@ -1329,8 +1284,7 @@ Setting:
Desc: æĢææ°æŽåē䏿ǿŖįĄŽåå¨įæäģļã
SettingTab:
Message:
- AskRebuild: Your changes require fetching from the remote database. Do you want
- to proceed?
+ AskRebuild: äŊ įæ´æšéčĻäģčŋ፿°æŽåēčˇåæ°æŽãæ¯åĻįģ§įģīŧ
"Setup Complete: Preparing to Fetch Synchronisation Data": 莞įŊŽåŽæīŧæŖå¨åå¤čˇååæĨæ°æŽ
"Setup Complete: Preparing to Initialise Server": 莞įŊŽåŽæīŧæŖå¨åå¤åå§åæåĄå¨
Setup URI dialog cancelled.: Setup URI å¯šč¯æĄåˇ˛åæļã
@@ -1338,71 +1292,53 @@ Setup-URI: 莞įŊŽ URI
Setup:
Apply:
Buttons:
- ApplyAndFetch: Apply and Fetch
- ApplyAndMerge: Apply and Merge
- ApplyAndRebuild: Apply and Rebuild
- Cancel: Discard and Cancel
- OnlyApply: Only Apply
- Message: >-
- The new configuration is ready. Let us proceed to apply it.
+ ApplyAndFetch: åēį¨åšļčˇå
+ ApplyAndMerge: åēį¨åšļååšļ
+ ApplyAndRebuild: åēį¨åšļéåģē
+ Cancel: ä¸ĸåŧåšļåæļ
+ OnlyApply: äģ
åēį¨
+ Message: |-
+ æ°é
įŊŽåˇ˛åå¤å°ąįģĒīŧ莊æäģŦįģ§įģåēį¨åŽã
+ åē፿¤é
įŊŽæäģĨä¸å į§æšåŧīŧ
- There are several ways to apply this:
-
-
- - Apply and Fetch
- Configure this device as a new client. After applying, synchronise from the remote server.
- - Apply and Merge
- Configure on a device that already has the file. It processes the local files and transfers the differences. Conflicts may arise.
- - Apply and Rebuild
- Rebuild the remote using local files. This is typically done if the server becomes corrupted or we wish to start from scratch.
- Other devices will be locked and required to re-fetch.
- - Only Apply
- Apply only. Conflicts may arise if a rebuild is required.
- Title: Apply new configuration from the ${method}
- WarningRebuildRecommended: "NOTE: after adjusting the settings, it has been
- determined that a rebuild is required; Just Import is not recommended."
+ - åēį¨åšļčˇå
+ å°æ¤čŽžå¤é
įŊŽä¸ēæ°åŽĸæˇį̝ãåēį¨åīŧäģčŋ፿åĄå¨åæĨã
+ - åēį¨åšļååšļ
+ å¨åˇ˛ææäģļį莞å¤ä¸čŋčĄé
įŊŽãåŽäŧå¤įæŦå°æäģļåšļäŧ čžåˇŽåŧãå¯čŊäŧåēį°å˛įĒã
+ - åēį¨åšļéåģē
+ äŊŋ፿Ŧå°æäģļéåģēčŋį¨ãé叏卿åĄå¨æåæå¸æäģ头åŧå§æļäŊŋį¨ã
+ å
ļäģ莞å¤å°čĸĢéåŽīŧåšļéčĻéæ°čˇåã
+ - äģ
åēį¨
+ äģ
åēį¨é
įŊŽãåĻæéčĻéåģēīŧå¯čŊäŧåēį°å˛įĒã
+ Title: äģ ${method} åē፿°é
įŊŽ
+ WarningRebuildRecommended: æŗ¨æīŧč°æ´čŽžįŊŽåīŧ厞å¤åŽéčĻéåģēīŧ䏿¨čäģ
å¯ŧå
Ĩã
Doctor:
Buttons:
- No: No, please use the settings in the URI as is
- Yes: Yes, please consult the doctor
- Message: >-
- Self-hosted LiveSync has gradually become longer in history and some
- recommended settings have changed.
+ No: åĻīŧ蝎æåæ ˇäŊŋ፠URI ä¸į莞įŊŽ
+ Yes: æ¯īŧ蝎čŋčĄč¯æ
+ Message: |-
+ Self-hosted LiveSync 厞įģåįģ夿ŦĄæŧčŋīŧä¸ä翍č莞įŊŽäšéäšåįäēååã
+ į°å¨æŖåŧ莞įŊŽéļæŽĩīŧæ¯čŋčĄæŖæĨįåĨŊæļæēã
- Now, setup is a very good time to do this.
-
-
- Do you want to run Doctor to check if the imported settings are optimal
- compared to the latest state?
- Title: Do you want to consult the doctor?
+ äŊ čĻčŋčĄč¯æīŧæŖæĨå¯ŧå
Ĩį莞įŊŽį¸å¯šäēææ°įļææ¯åĻæäŧåīŧ
+ Title: čĻčŋčĄč¯æåīŧ
FetchRemoteConf:
Buttons:
- Fetch: Yes, please fetch the configuration
- Skip: No, please use the settings in the URI
- Message: >-
- If we have already synchronised once with another device, the remote
- database stores the suitable configuration values between the synchronised
- devices. The plug-in would like to retrieve them for robust configuration.
+ Fetch: æ¯īŧ蝎čˇåé
įŊŽ
+ Skip: åĻīŧ蝎äŊŋ፠URI ä¸į莞įŊŽ
+ Message: |-
+ åĻææäģŦäšå厞įģä¸åĻä¸å°čŽžå¤åæĨčŋīŧčŋ፿°æŽåēäŧäŋå厞åæĨ莞å¤äšé´éį¨įé
įŊŽåŧãæäģļ叿čˇåčŋäēåŧīŧäģĨäžŋčŋčĄæ´å¯é įé
įŊŽã
+ äŊæä¸įšåŋ
éĄģįĄŽčŽ¤īŧæäģŦåŊ忝åĻå¤äēå¯äģĨåŽå
¨čŽŋéŽįŊįģåšļčˇåčŋäē莞įŊŽįį¯åĸä¸īŧ
- However, we have to make sure the one thing. Are we currently in a
- situation where we can access the network safely and retrieve the
- settings?
-
-
- Note: Mostly, you are safe to do this, that your remote database is hosted
- with a SSL certificate, and your network is not compromised.
- Title: Fetch configuration from remote database?
- QRCode: >-
- We have generated a QR code to transfer the settings. Please scan the QR
- code with your phone or other device.
-
- Note: The QR code is not encrypted, so be careful to open this.
-
+ æŗ¨æīŧé常æ
åĩä¸īŧåĒčĻäŊ įčŋ፿°æŽåēäŊŋį¨äē SSL č¯äšĻīŧä¸įŊįģæĒčĸĢå
Ĩäžĩīŧæ§čĄæ¤æäŊå°ąæ¯åŽå
¨įã
+ Title: čĻäģčŋ፿°æŽåēčˇåé
įŊŽåīŧ
+ QRCode: |-
+ æäģŦ厞įæį¨äēäŧ čžčŽžįŊŽįäēįģ´į ã蝎äŊŋ፿æēæå
ļäģčŽžå¤æĢæč¯Ĩäēįģ´į ã
+ æŗ¨æīŧæ¤äēįģ´į æĒå å¯īŧ蝎谍æ
æåŧã
>[!FOR YOUR EYES ONLY]-
-
> ${qr_image}
RemoteE2EE:
AdvancedTitle: éĢįē§
@@ -1564,110 +1500,79 @@ TURN Username: TURN ፿ˇå
TweakMismatchResolve:
Action:
DisableAutoAcceptCompatible: åį¨čĒ卿Ĩå
- Dismiss: Dismiss
+ Dismiss: åŋŊįĨ
EnableAutoAcceptCompatible: å¯į¨čĒ卿Ĩå
- UseConfigured: Use configured settings
- UseMine: Update remote database settings
- UseMineAcceptIncompatible: Update remote database settings but keep as is
- UseMineWithRebuild: Update remote database settings and rebuild again
- UseRemote: Apply settings to this device
- UseRemoteAcceptIncompatible: Apply settings to this device, but and ignore incompatibility
- UseRemoteWithRebuild: Apply settings to this device, and fetch again
+ UseConfigured: äŊŋį¨åˇ˛é
įŊŽį莞įŊŽ
+ UseMine: æ´æ°čŋ፿°æŽåē莞įŊŽ
+ UseMineAcceptIncompatible: æ´æ°čŋ፿°æŽåē莞įŊŽīŧäŊåŋŊįĨä¸å
ŧ厚éŽéĸ
+ UseMineWithRebuild: æ´æ°čŋ፿°æŽåē莞įŊŽåšļåæŦĄéåģē
+ UseRemote: å°čŽžįŊŽåēį¨å°æ¤čŽžå¤
+ UseRemoteAcceptIncompatible: å°čŽžįŊŽåēį¨å°æ¤čŽžå¤īŧäŊåŋŊįĨä¸å
ŧ厚éŽéĸ
+ UseRemoteWithRebuild: å°čŽžįŊŽåēį¨å°æ¤čŽžå¤īŧåšļéæ°čˇå
Message:
AutoAcceptCompatibleUndefined: |-
å莞å¤į莞įŊŽäŧŧäšåå¨åˇŽåŧãäŊ į°å¨å¯äģĨčĒå¨åēį¨å
ŧåŽšįæ´æšå°čŋäēé
įŊŽã
æ¯åĻčĻå¯į¨æ¤ãčĒ卿Ĩåã莞įŊŽīŧ
- Main: >-
+ Main: |-
- The settings in the remote database are as follows. These values are
- configured by other devices, which are synchronised with this device at
- least once.
-
-
- If you want to use these settings, please select
- %{TweakMismatchResolve.Action.UseConfigured}.
-
- If you want to keep the settings of this device, please select
- %{TweakMismatchResolve.Action.Dismiss}.
+ čŋ፿°æŽåēä¸į莞įŊŽåĻä¸ãčŋäēåŧįąä¸æ¤čŽžå¤čŗå°åæĨčŋ䏿ŦĄįå
ļäģ莞å¤é
įŊŽã
+ åĻæäŊ æŗäŊŋį¨čŋäē莞įŊŽīŧč¯ˇéæŠ %{TweakMismatchResolve.Action.UseConfigured}ã
+ åĻæäŊ æŗäŋįæ¤čŽžå¤į莞įŊŽīŧč¯ˇéæŠ %{TweakMismatchResolve.Action.Dismiss}ã
${table}
-
>[!TIP]
-
- > If you want to synchronise all settings, please use `Sync settings via
- markdown` after applying minimal configuration with this feature.
-
+ > åĻæäŊ æŗåæĨå
¨é¨čŽžįŊŽīŧ蝎å
äŊŋ፿¤åčŊåē፿å°é
įŊŽīŧįļåäŊŋ፠`Sync settings via markdown`ã
${additionalMessage}
MainTweakResolving: |-
- Your configuration has not been matched with the one on the remote server.
+ äŊ įé
įŊŽä¸čŋ፿åĄå¨ä¸įé
įŊŽä¸ä¸č´ã
- Following configuration should be matched:
+ äģĨä¸é
įŊŽåēåŊäŋæä¸č´īŧ
${table}
- Let us know your decision.
+ 蝎åįĨäŊ įåŗåŽã
${additionalMessage}
mineUpdated: æŦ莞å¤įé
įŊŽåˇ˛č°æ´ã
remoteUpdated: čŋį̝åå¨įé
įŊŽåˇ˛æ´æ°ã
UseRemote:
- WarningRebuildRecommended: >-
+ WarningRebuildRecommended: |-
>[!NOTICE]
-
- > Some changes are compatible but may consume extra storage and transfer
- volumes. A rebuild is recommended. However, a rebuild may not be
- performed at present, but may be implemented in future maintenance.
-
- > ***Please ensure that you have time and are connected to a stable
- network to apply!***
- WarningRebuildRequired: >-
+ > é¨åæ´æšæ¯å
ŧ厚įīŧäŊå¯čŊäŧå į¨éĸå¤įåå¨įŠēé´åäŧ čžæĩéãåģē莎čŋčĄéåģēãä¸čŋīŧäšå¯äģĨæä¸éåģēīŧįåž
äģåįģ´æ¤æļåčŋčĄã
+ > ***åēį¨åč¯ˇįĄŽäŋäŊ æå
čļŗįæļé´īŧåšļ厞čŋæĨå°į¨ŗåŽįįŊįģīŧ***
+ WarningRebuildRequired: |-
>[!WARNING]
-
- > Some remote configurations are not compatible with the local database
- of this device. Rebuilding the local database will be required.
-
- > ***Please ensure that you have time and are connected to a stable
- network to apply!***
- WarningIncompatibleRebuildRecommended: >-
+ > é¨åčŋį¨é
įŊŽä¸æŦ莞å¤įæŦå°æ°æŽåēä¸å
ŧ厚īŧå°éčĻéåģēæŦå°æ°æŽåēã
+ > ***åēį¨åč¯ˇįĄŽäŋäŊ æå
čļŗįæļé´īŧåšļ厞čŋæĨå°į¨ŗåŽįįŊįģīŧ***
+ WarningIncompatibleRebuildRecommended: |-
>[!NOTICE]
-
- > We have detected that some of the values are different to make
- incompatible the local database with the remote database.
-
- > Some changes are compatible but may consume extra storage and transfer
- volumes. A rebuild is recommended. However, a rebuild may not be performed
- at present, but may be implemented in future maintenance.
-
- > If you want to rebuild, it takes a few minutes or more. **Make sure it
- is safe to perform it now.**
- WarningIncompatibleRebuildRequired: >-
+ > æäģŦæŖæĩå°é¨ååŧåå¨åˇŽåŧīŧå¯ŧč´æŦå°æ°æŽåēä¸čŋ፿°æŽåēä¸å
ŧ厚ã
+ > é¨åæ´æšæ¯å
ŧ厚įīŧäŊå¯čŊäŧå į¨éĸå¤įåå¨įŠēé´åäŧ čžæĩéãåģē莎čŋčĄéåģēãä¸čŋīŧäšå¯äģĨæä¸éåģēīŧįåž
äģåįģ´æ¤æļåčŋčĄã
+ > åĻæäŊ čĻéåģēīŧ大įēĻéčĻå åéææ´éŋæļé´ã**蝎å
įĄŽčŽ¤į°å¨æ§čĄæ¯åŽå
¨įã**
+ WarningIncompatibleRebuildRequired: |-
>[!WARNING]
-
- > We have detected that some of the values are different to make
- incompatible the local database with the remote database.
-
- > Either local or remote rebuilds are required. Both of them takes a few
- minutes or more. **Make sure it is safe to perform it now.**
+ > æäģŦæŖæĩå°æäēåŧåå¨åˇŽåŧīŧå¯ŧč´æŦå°æ°æŽåēä¸čŋ፿°æŽåēä¸å
ŧ厚ã
+ > éčĻéåģēæŦå°æčŋį¨å
ļä¸äšä¸īŧ两č
éŊéčĻå åéįčŗæ´éŋæļé´ã**č¯ˇįĄŽäŋį°å¨æ§čĄæ¤æäŊæ¯åŽå
¨įã**
Table:
_value: |+
- | Value name | This device | On Remote |
+ | åŧåį§° | æ¤čŽžå¤ | čŋ፠|
|: --- |: ---- :|: ---- :|
${rows}
Row: "| ${name} | ${self} | ${remote} |"
Title:
- _value: Configuration Mismatch Detected
+ _value: æŖæĩå°é
įŊŽä¸ä¸č´
AutoAcceptCompatible: å¯čĒ卿Ĩå
- TweakResolving: Configuration Mismatch Detected
- UseRemoteConfig: Use Remote Configuration
+ TweakResolving: æŖæĩå°é
įŊŽä¸ä¸č´
+ UseRemoteConfig: äŊŋį¨čŋį¨é
įŊŽ
Ui:
Common:
LocalDatabaseInitialisationFailed: Self-hosted LiveSync æ æŗåæĨã蝎῿ĨåäģĨæĨįč¯ĻįģæĨåŋã
@@ -2028,3 +1933,131 @@ You can configure in the Obsidian Plugin Settings.: äŊ å¯äģĨå¨ Obsidian æäģļ
You should create a new synchronisation destination and rebuild your data there.: äŊ åēåŊååģēä¸ä¸Ēæ°įåæĨįŽæ īŧåšļå¨éŖééåģēæ°æŽã
You should perform this operation only in exceptional circumstances, such as when the server data is completely corrupted, when changes on all other devices are no longer needed, or when the database size has become unusually large in comparison to the Vault size.: äŊ åĒåēå¨įšæŽæ
åĩ䏿§čĄæ¤æäŊīŧäžåĻæåĄå¨æ°æŽåŽå
¨æåãå
ļäģææčŽžå¤ä¸įæ´æšéŊä¸åéčĻīŧææ°æŽåēäŊ᧝į¸å¯šäēåēäŊ᧝åŧ常åē大æļã
you wanted(Thank you)!: äŊ čĻæąįæäŊīŧč°ĸč°ĸīŧīŧ
+Cancel all and reboot: å
¨é¨åæļåšļéå¯
+Compare time and take newer: æ¯čžæļé´åšļéį¨čžæ°įæŦ
+Conflict & Deletion Options: å˛įĒä¸å é¤é饚
+Data retrieval scheduled: åˇ˛åŽææ°æŽčˇå
+Delete local files if deleted on remote: čĨčŋį¨åˇ˛å é¤īŧåå 餿Ŧå°æäģļ
+Delete local files if not on remote: čĨčŋį¨ä¸åå¨īŧåå 餿Ŧå°æäģļ
+Finalise the process and resume normal operation: åŽææĩį¨åšļæĸ夿Ŗå¸¸čŋčĄ
+"How should files that were deleted on other devices be handled?\n\n- **${NEWER_CLEANUP}**: Delete local files if they were deleted on remote.\n This is useful if you want to keep your vault clean and consistent across devices, but please make sure you have a backup if there is already any important data in your vault.\n- **${NEWER_SYNC_ALL}**: Recreate remote files even if they were deleted on remote.\n This option will keep all your local files, but it may cause duplicates if there are files that exist on local but not on remote. You can clean up these duplicates manually after the synchronisation.": |-
+ å
ļäģ莞å¤ä¸å é¤įæäģļåēåĻäŊå¤įīŧ
+
+ - **${NEWER_CLEANUP}**īŧåĻææäģļå¨čŋį¨čĸĢå é¤īŧåå 餿Ŧå°æäģļã
+ åĻæäŊ å¸æčŽŠå莞å¤ä¸įåēäŋææ´æ´ä¸č´īŧčŋäŧåžæį¨ãäŊåĻæåēä¸åˇ˛æéčĻæ°æŽīŧ蝎åĄåŋ
å
ååĨŊå¤äģŊã
+ - **${NEWER_SYNC_ALL}**īŧåŗäŊŋæäģļå¨čŋį¨åˇ˛čĸĢå é¤īŧäšéæ°ååģēčŋ፿äģļã
+ æ¤é饚äŧäŋįäŊ ææįæŦå°æäģļīŧäŊåĻææäēæäģļåĒåå¨äēæŦå°čä¸åå¨äēčŋį¨īŧå¯čŊäŧäē§įé夿äģļãåæĨåŽæåīŧäŊ å¯äģĨæå¨æ¸
įčŋäēé夿äģļã
+How to handle extra existing local files?: åĻäŊå¤įæŦå°åˇ˛æįå¤äŊæäģļīŧ
+Keep local files even if deleted on remote: åŗäŊŋčŋį¨åˇ˛å é¤īŧäšäŋįæŦå°æäģļ
+Keep local files even if not on remote: åŗäŊŋčŋį¨ä¸åå¨īŧäšäŋįæŦå°æäģļ
+Overwrite all with remote files: å
¨é¨į¨čŋ፿äģļčĻį
+Reboot to re-run the process: éå¯äģĨéæ°čŋčĄæ¤æĩį¨
+"Since you have chosen to overwrite all local files with remote data, **how would you like to handle local files that are not present in the remote database?**\n\n- **${DELETE_ALL}**: Local-only files and remote-deleted files will be removed.\n This option will make your local vault exactly the same as the remote database, but please make sure you have a backup if there is any important data in your vault.\n- **${DELETE_NONE}**: All existing local files will be preserved.\n This option will keep all your local files, but it may cause duplicates if there are files that exist on local but not on remote. You can clean up these duplicates manually after the synchronisation.": |-
+ įąäēäŊ éæŠäēį¨čŋ፿°æŽčĻįæææŦå°æäģļīŧ**寚äēčŋ፿°æŽåēä¸ä¸åå¨įæŦå°æäģļīŧäŊ 叿åĻäŊå¤įīŧ**
+
+ - **${DELETE_ALL}**īŧäģ
åå¨äēæŦå°įæäģļåå¨čŋį¨åˇ˛čĸĢå é¤įæäģļå°čĸĢį§ģé¤ã
+ æ¤é饚äŧ莊äŊ įæŦå°åēä¸čŋ፿°æŽåēåŽå
¨ä¸č´īŧäŊåĻæåē䏿éčĻæ°æŽīŧ蝎åĄåŋ
å
ååĨŊå¤äģŊã
+ - **${DELETE_NONE}**īŧäŋįææį°æįæŦå°æäģļã
+ æ¤é饚äŧäŋįäŊ ææįæŦå°æäģļīŧäŊåĻææäēæäģļäģ
åå¨äēæŦå°čä¸å¨čŋį¨īŧå¯čŊäŧäē§įé夿äģļãåæĨåŽæåīŧäŊ å¯äģĨæå¨æ¸
įčŋäēé夿äģļã
+Some files failed to synchronise. What would you like to do?: é¨åæäģļåæĨå¤ąč´ĨãäŊ 叿åĻäŊå¤įīŧ
+Synchronisation Issues Detected: æŖæĩå°åæĨéŽéĸ
+Use the detailed flow: äŊŋį¨č¯Ļįģæĩį¨
+"We are about to retrieve the remote data.\n\nFirstly, how shall we handle the data retrieved from this remote source?\n\n- **${NEWER_WINS}**: Compares the modified time of files and takes the newer one.\n If you have been using Self-hosted LiveSync and have made changes on multiple devices, this option may be suitable for you as it tries to merge changes based on modified time.\n- **${REMOTE_WINS}**: Remote data is the source of truth.\n If you are new to using Self-hosted LiveSync. This option may be easiest to understand and get started with.\n It will overwrite all your local files with the remote data, so please make sure you have a backup if there is any important data in your vault.\n- **${DETAILED}**: Opens the detailed setup wizard.\n If you want to have more control over the synchronisation process, or want to review the changes before applying, you can choose this option to use the detailed flow.": |-
+ åŗå°čˇåčŋ፿°æŽã
+
+ éĻå
īŧåēåĻäŊå¤įä쿤čŋ፿Ĩæēčˇåįæ°æŽīŧ
+
+ - **${NEWER_WINS}**īŧæ¯čžæäģļįäŋŽæšæļé´īŧåšļéį¨čžæ°įéŖä¸ä¸Ēã
+ åĻæäŊ ä¸į´å¨äŊŋ፠Self-hosted LiveSyncīŧåšļä¸å¨å¤å°čŽžå¤ä¸åčŋæ´æšīŧæ¤é饚å¯čŊéåäŊ īŧå ä¸ēåŽäŧå°č¯æ šæŽäŋŽæšæļé´ååšļæ´æšã
+ - **${REMOTE_WINS}**īŧäģĨčŋ፿°æŽä¸ēåã
+ åĻæäŊ æ¯ Self-hosted LiveSync ῰፿ˇīŧæ¤é饚å¯čŊæåŽšæįč§ŖīŧäšæåŽšæä¸æã
+ åŽäŧį¨čŋ፿°æŽčĻįäŊ ææįæŦå°æäģļīŧå æ¤åĻæåē䏿éčĻæ°æŽīŧ蝎åĄåŋ
å
å¤äģŊã
+ - **${DETAILED}**īŧæåŧč¯Ļįģ莞įŊŽåå¯ŧã
+ åĻæäŊ 叿æ´åĨŊå°æ§åļåæĨčŋį¨īŧæå¸æå¨åēį¨åæŖæĨæ´æšīŧå¯äģĨéæŠæ¤é饚äŊŋį¨č¯Ļįģæĩį¨ã
+SettingService.ConfigurationPassphraseDeviceLocalDescription: æ¤å¯į įč¯åĒäŋį卿¤čŽžå¤ä¸ãåŊå
ļäģ莞å¤éčĻ卿˛Ąæåˇ˛äŋåå¯éĨįæ
åĩä¸č§Ŗå¯åäŋæ¤į莞įŊŽæļīŧ蝎å¨å¯å¨æļčžå
Ĩį¸åįå¯į įč¯ã
+SettingService.ConfigurationPassphraseTitle: é
įŊŽå¯į įč¯
+SettingService.ConfigurationPassphraseRetryTitle: æ æŗč§Ŗå¯é
įŊŽã蝎鿰čžå
Ĩé
įŊŽå¯į įč¯ã
+SettingService.ConfigurationPassphraseCancelled: é
įŊŽå¯į įč¯įčžå
Ĩ厞åæļæä¸å¯į¨ãå¯å¨åˇ˛åæĸīŧ厞äŋåį莞įŊŽåˇ˛äŋįã
+SettingService.ConfigurationEncryptionSaveFailed: æ æŗäŋåé
įŊŽå å¯ã蝎å¨éč¯åæŖæĨ厞äŋåį莞įŊŽå莞å¤å¯éĨã
+SettingService.ConfigurationPassphraseRollbackFailed: åå
Ĩå¤ąč´Ĩåīŧæ æŗæĸå¤é
įŊŽå¯į įč¯ã蝎å¨éč¯åæŖæĨ厞äŋåį莞įŊŽå莞å¤å¯éĨã
+SettingService.ConfigurationCouldNotBeDecrypted: æ æŗč§Ŗå¯į°æé
įŊŽãé
įŊŽå¯éĨæĒæ´æšã
+Active Remote Type: åŊåäŊŋį¨įčŋį¨įąģå
+Application ID: åē፠ID
+Auto-accept compatible tweak mismatches: čĒ卿Ĩåå
ŧ厚įč°æ´åŧä¸ä¸č´
+Automatically accepts mismatches that are compatible but potentially lossy by comparing tweak modification times.: éčŋæ¯čžč°æ´åŧįäŋŽæšæļé´īŧčĒ卿Ĩåå
ŧ厚äŊå¯čŊææįé
įŊŽä¸ä¸č´ã
+Automatically broadcast changes to connected peers: čĒå¨å°æ´æšåšŋæįģ厞čŋæĨįå¯šįæš
+Automatically start P2P connection on launch: å¯å¨æļčĒå¨å¯å¨ P2P čŋæĨ
+Chunk Splitter: æ°æŽååå˛å¨
+Chunk revisions are always derived from their content. This stored key is retained only for compatibility.: æ°æŽåäŋŽčŽĸįæŦå§įģįąå
ļå
厚æ¨å¯ŧåžåēãæ¤åˇ˛åå¨į莞įŊŽéĄšäģ
ä¸ēå
ŧ厚čäŋįã
+Content-derived chunk revisions (obsolete setting): åēäēå
åŽšįæ°æŽåäŋŽčŽĸīŧ厞čŋæļį莞įŊŽīŧ
+"Custom headers for requesting the CouchDB. e.g. `x-custom-header1: value1\n x-custom-header2: value2`": |-
+ į¨äēč¯ˇæą CouchDB įčĒåŽäšč¯ˇæąå¤´ãäžåĻīŧ`x-custom-header1: value1
+ x-custom-header2: value2`
+"Custom headers for requesting the bucket. e.g. `x-custom-header1: value1\n x-custom-header2: value2`": |-
+ į¨äē蝎æąå卿ĄļįčĒåŽäšč¯ˇæąå¤´ãäžåĻīŧ`x-custom-header1: value1
+ x-custom-header2: value2`
+Desktop only; uses more battery and network.: äģ
éæĄéĸį̝īŧäŧæļčæ´å¤įĩéåįŊįģæĩéã
+Effectively a directory. Should end with `/`. e.g., `vault-name/`.: åŽé
䏿¯ä¸ä¸ĒįŽåŊīŧåēäģĨ `/` įģå°žīŧäžåĻ `vault-name/`ã
+Enable Developers' Debug Tools (If available).: å¯į¨åŧåč
č°č¯åˇĨå
ˇīŧåĻå¯į¨īŧã
+Enable P2P Synchronization: å¯į¨ P2P åæĨ
+Enable forcePathStyle: å¯į¨ forcePathStyle
+Enable per-file customization sync: å¯į¨éæäģļčĒåŽäšåæĨ
+Encrypt internal file Properties: å å¯å
鍿äģļåąæ§
+Encrypt file properties (paths, times, sizes, and Chunk references) used by Hidden File Sync and Customisation Sync. This affects future writes; manually rebuild the remote database to protect existing properties. Requires E2EE V2, Property Encryption, and compatible clients on every device.: å å¯éčæäģļåæĨåčĒåŽäšåæĨæäŊŋį¨įæäģļåąæ§īŧ莝åžãæļé´ã大å°äģĨåæ°æŽååŧį¨īŧãæ¤čŽžįŊŽåŊąåäšåįåå
ĨīŧåĻéäŋæ¤åˇ˛æåąæ§īŧ蝎æå¨éåģēčŋ፿°æŽåēãéčĻ E2EE
+ V2 ååąæ§å å¯īŧåšļ䏿¯å°čŽžå¤ä¸įåŽĸæˇį̝éŊéå
ŧ厚ã
+Encryption passphrase. If changed, you should overwrite the server's database with the new (encrypted) files.: å å¯å¯į įč¯ãåĻææ´æšīŧäŊ åēåŊ፿°įīŧå å¯åįīŧæäģļčĻįæåĄå¨ä¸įæ°æŽåēã
+End-to-End Encryption Algorithm: į̝å°į̝å å¯įŽæŗ
+File prefix on the bucket: å卿Ąļä¸įæäģļåįŧ
+Files with modification times greater than this value (in seconds since the Unix epoch) will not have their events reflected. Set to 0 to disable this limit.: äŋŽæšæļé´å¤§ä翤åŧīŧUnix æļé´æŗīŧåäŊä¸ēį§īŧįæäģļīŧå
ļäēäģļå°ä¸äŧčĸĢåæ ã莞ä¸ē 0 å¯įĻ፿¤éåļã
+If enabled, changes will be automatically broadcasted to all connected peers. Notified peers will start fetching the changes.: å¯į¨åīŧæ´æšå°čĒå¨åšŋæį쿿厞čŋæĨįå¯šįæšãæļå°éįĨįå¯šįæšå°åŧå§čˇåčŋäēæ´æšã
+If enabled, efficient per-file customization sync will be used. A minor migration is required when enabling this feature, and all devices must be updated to v0.23.18. Enabling this feature will result in losing compatibility with older versions.: å¯į¨åīŧå°äŊŋ፿´éĢæįéæäģļčĒåŽäšåæĨãå¯į¨æ¤åčŊéčĻčŋčĄä¸æŦĄå°č§æ¨Ąčŋį§ģīŧåšļ䏿æčŽžå¤éŊåŋ
éĄģæ´æ°å° v0.23.18ãå¯į¨æ¤åčŊåīŧå°å¤ąåģ䏿§įæŦįå
ŧ厚æ§ã
+If enabled, the P2P connection will be automatically started when the application launches.: å¯į¨åīŧåēį¨å¯å¨æļå°čĒå¨åŧå§ P2P čŋæĨã
+If enabled, the file properties will be encrypted in the remote database. This is useful for protecting sensitive information in file paths, sizes, and IDs of its chunks. If you are using V1 E2EE, this only obfuscates the file path.: å¯į¨åīŧæäģļåąæ§å°å¨čŋ፿°æŽåēä¸å å¯ãčŋæåŠäēäŋæ¤æäģļ莝åžã大å°äģĨåå
ļæ°æŽå ID įææäŋĄæ¯ãåĻæäŊ äŊŋį¨įæ¯ V1 E2EEīŧååĒäŧæˇˇæˇæäģļ莝åžã
+If enabled, the forcePathStyle option will be used for bucket operations.: å¯į¨åīŧå卿ĄļæäŊå°äŊŋ፠forcePathStyle é饚ã
+If enabled, the plugin will not attempt to connect to the remote database even if the chunk was not found locally.: å¯į¨åīŧåŗäŊŋæŦ尿ǿžå°æ°æŽåīŧæäģļäšä¸äŧå°č¯čŋæĨčŋ፿°æŽåēã
+If enabled, the request API will be used to avoid `inevitable` CORS problems. This is a workaround and may not work in all cases. PLEASE READ THE DOCUMENTATION BEFORE USING THIS OPTION. This is a less-secure option.: å¯į¨åīŧå°äŊŋ፠Request API æĨéŋå
`inevitable` CORS
+ éŽéĸãčŋåĒæ¯ä¸į§åéæšæŗīŧåšļé卿ææ
åĩä¸éŊææã蝎åĄåŋ
å¨äŊŋ፿¤é饚åé
č¯ģææĄŖãčŋæ¯ä¸ä¸ĒåŽå
¨æ§čžäŊįé饚ã
+If this enabled, JWT will be used for authentication.: å¯į¨åå°äŊŋ፠JWT 莤č¯ã
+Internal timestamp for resolving compatible tweak mismatches.: į¨äēč§Ŗåŗå
ŧ厚įč°æ´åŧä¸ä¸č´éŽéĸįå
鍿ļé´æŗã
+Keep replication active in the background: å¨åå°äŋæå¤åļčŋčĄ
+Key ID: å¯éĨ ID
+Keypair or pre-shared key: å¯éĨ寚æéĸå
ąäēĢå¯éĨ
+Last tweak modified timestamp: č°æ´åŧæåäŋŽæšæļé´æŗ
+Longest chunk line threshold value (Not Configurable from the UI Now).: æéŋæ°æŽåčĄįéåŧīŧįŽåæ æŗå¨įéĸä¸é
įŊŽīŧã
+MB per request: æ¯æŦĄč¯ˇæąį MB æ°
+Maximum file modification time for reflected file events: å¯åæ æäģļäēäģļįæå¤§äŋŽæšæļé´
+Maximum request size for manually resending chunks: æå¨éæ°åéæ°æŽåæļįæå¤§č¯ˇæąå¤§å°
+Minimum Chunk Size (Not Configurable from the UI Now).: æå°æ°æŽå大å°īŧįŽåæ æŗå¨įéĸä¸é
įŊŽīŧã
+Move remotely deleted files to the trash, instead of deleting. (This setting is ineffective from Obsidian v1.7.2 onwards, as deletion always respects your Obsidian preferences.): å°čŋį¨å é¤įæäģļį§ģčŗåæļįĢīŧč䏿¯į´æĨå é¤ãīŧæ¤čŽžįŊŽčĒ Obsidian v1.7.2 čĩˇä¸åįæīŧå ä¸ēå 餿äŊå§įģéĩåžĒäŊ å¨ Obsidian
+ ä¸įååĨŊ莞įŊŽãīŧ
+Now we can choose how to split the chunks; V3 is the most efficient. If you have troubled, please make this Default or Legacy.: į°å¨å¯äģĨéæŠæ°æŽåįå垿šåŧīŧV3 æįæéĢãåĻæéå°éŽéĸīŧ蝎尿¤éĄščŽžä¸ē Default æ Legacyã
+Please use V2, V1 is deprecated and will be removed in the future, It was not a very appropriate algorithm. Only for compatibility V1 is kept.: 蝎äŊŋ፠V2ãV1 厞čĸĢåŧį¨īŧå°æĨäŧčĸĢį§ģé¤īŧåŽåšļ䏿¯é常åéįįŽæŗãäģ
åēäēå
ŧ厚æ§čäŋį V1ã
+Process files even if seems to be corrupted: åŗäŊŋæäģļįäŧŧæåäščŋčĄå¤į
+Rotation Duration: äģ¤įčŊŽæĸæļéŋ
+Signalling Relays: äŋĄäģ¤ä¸įģ§
+Subject (whoami): ä¸ģäŊīŧwhoamiīŧ
+TURN Servers: TURN æåĄå¨
+The Application ID for P2P connection. This should be same among your devices. Default is 'self-hosted-livesync' and could not be modified from the UI.: į¨äē P2P čŋæĨįåē፠IDãææčŽžå¤åēäŋæį¸åãéģ莤åŧä¸ē 'self-hosted-livesync'īŧæ æŗå¨įéĸä¸äŋŽæšã
+The Nostr relay servers to establish connections for P2P connections. Multiple servers can be separated by commas.: į¨äēåģēįĢ P2P čŋæĨį Nostr ä¸įģ§æåĄå¨ãå¤ä¸ĒæåĄå¨å¯äģĨį¨éåˇåéã
+The Passphrase for P2P connection. This should be same among your devices.: į¨äē P2P čŋæĨįå¯į įč¯ãäŊ įææčŽžå¤ä¸éŊåēäŊŋį¨į¸åįå¯į įč¯ã
+The Room ID for P2P connection. This should be same among your devices.: į¨äē P2P čŋæĨįæŋé´ IDãææčŽžå¤åēäŊŋį¨į¸åįæŋé´ IDã
+The Rotation duration of token in minutes. Each generated tokens will be valid only within this duration.: äģ¤įįčŊŽæĸæļéŋīŧåäŊīŧåéīŧãæ¯ä¸Ēįæįäģ¤įäģ
卿¤æļéŋå
ææã
+The TURN servers to use for P2P connections. Multiple servers can be separated by commas.: į¨äē P2P čŋæĨį TURN æåĄå¨ãå¤ä¸ĒæåĄå¨å¯į¨éåˇåéã
+The algorithm used for JWT authentication.: į¨äē JWT 莤č¯įįŽæŗã
+The credential/password for the TURN servers.: TURN æåĄå¨įåæŽ/å¯į ã
+The key (PSK in HSxxx in base64, or private key in ESxxx in PEM) used for JWT authentication.: į¨äē JWT 莤č¯įå¯éĨīŧbase64 æ ŧåŧį PSKīŧåŊĸåĻ HSxxxīŧæ PEM æ ŧåŧįį§éĨīŧåŊĸåĻ ESxxxīŧã
+The key ID. this should be matched with CouchDB->jwt_keys->ALG:_`kid`.: å¯éĨ IDãåŽåēä¸ CouchDB->jwt_keys->ALG:_`kid` į¸åšé
ã
+The subject for JWT authentication. Mostly username.: JWT 莤č¯įä¸ģäŊīŧé常ä¸ē፿ˇåã
+The username for the TURN servers.: TURN æåĄå¨į፿ˇåã
+Use JWT instead of Basic Authentication: äŊŋ፠JWT čé Basic 莤č¯
+Use Only Local Chunks: äģ
äŊŋ፿Ŧå°æ°æŽå
+Use Request API to avoid `inevitable` CORS problem: äŊŋ፠Request API äģĨéŋå
`inevitable` CORS éŽéĸ
+While enabled, it causes very performance impact but debugging replication testing and other features will be enabled. Please disable this if you have not read the source code. Requires restart of Obsidian. Sometimes there is no implementation.: å¯į¨åäŧä¸ĨéåŊąåæ§čŊīŧäŊäŧåŧå¯å¤åļč°č¯æĩč¯įåčŊãåĻæäŊ æ˛Ąæé
č¯ģčŋæēäģŖį īŧ蝎å
ŗéæ¤éĄšãéčĻéå¯ Obsidianãé¨ååčŊå¯čŊå°æĒåŽį°ã
+You can enable this setting to process the files with size mismatches, these files can be created by some APIs or integrations.: å¯į¨æ¤čŽžįŊŽåå¯äģĨå¤į大å°ä¸åšé
įæäģļīŧčŋäēæäģļå¯čŊįąæäē API æéæååģēã
+xxhash64 is the current default.: xxhash64 æ¯åŊåįéģ莤åŧã
+Allow sleep during synchronisation: åæĨæé´å
莸įĄį
+Allow the operating system to sleep while finite synchronisation operations are in progress.: å¨ä¸æŦĄæ§åæĨæäŊčŋčĄæé´īŧå
莸æäŊįŗģįģčŋå
ĨįĄį ã
+Allow sleep during synchronisation on the desktop: æĄéĸį̝åæĨæé´å
莸įĄį
+Desktop only. Allow sleep on this device even when the general option is disabled.: äģ
éæĄéĸį̝ãåŗäŊŋéį¨ééĄšåˇ˛įĻį¨īŧäšå
莸æ¤čŽžå¤čŋå
ĨįĄį ã
+Enable advanced mode: å¯į¨éĢįē§æ¨Ąåŧ
+Enable power user mode: å¯į¨ä¸åŽļæ¨Ąåŧ
diff --git a/src/main.onunload.unit.spec.ts b/src/main.onunload.unit.spec.ts
index 3ddb9019..af48724d 100644
--- a/src/main.onunload.unit.spec.ts
+++ b/src/main.onunload.unit.spec.ts
@@ -49,6 +49,8 @@ const unusedStartupDependencies: readonly [string, readonly string[]][] = [
["./serviceFeatures/interactiveConflictResolution", ["useInteractiveConflictResolutionFeature"]],
["./modules/features/InteractiveConflictResolving/ConflictResolveModal.ts", ["ConflictResolveModal"]],
["./serviceFeatures/obsidianReplicationRibbon.ts", ["useObsidianReplicationRibbonFeature"]],
+ ["./serviceFeatures/syncGuard", ["useSyncGuardFeature"]],
+ ["./serviceFeatures/syncGuard/SyncGuardOverlay", ["SyncGuardOverlay"]],
["./serviceFeatures/startupLifecycle", ["useStartupLifecycleFeature"]],
["./serviceFeatures/useJoinedDevices", ["useJoinedDevicesFeature"]],
["./serviceFeatures/useDatabaseDiagnostics", ["useDatabaseDiagnosticsFeature"]],
diff --git a/src/main.ts b/src/main.ts
index 51aba0d1..fb716949 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -52,6 +52,8 @@ import { createFileReflectionProvenance } from "./serviceModules/FileReflectionP
import { useInteractiveConflictResolutionFeature } from "./serviceFeatures/interactiveConflictResolution";
import { ConflictResolveModal } from "./modules/features/InteractiveConflictResolving/ConflictResolveModal.ts";
import { useObsidianReplicationRibbonFeature } from "./serviceFeatures/obsidianReplicationRibbon.ts";
+import { useSyncGuardFeature } from "./serviceFeatures/syncGuard";
+import { SyncGuardOverlay } from "./serviceFeatures/syncGuard/SyncGuardOverlay";
import { useStartupLifecycleFeature } from "./serviceFeatures/startupLifecycle";
import { useJoinedDevicesFeature } from "./serviceFeatures/useJoinedDevices";
import { useDatabaseDiagnosticsFeature } from "./serviceFeatures/useDatabaseDiagnostics";
@@ -199,6 +201,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
useP2PReplicatorCommands(core, replicator);
useP2PReplicatorUI(core, core, replicator, createInteractiveP2PReplication(replicator));
useObsidianReplicationRibbonFeature(core);
+ useSyncGuardFeature(core, (onBypass) => new SyncGuardOverlay(this.app.workspace.containerEl, onBypass));
useDatabaseDiagnosticsFeature(core);
useJoinedDevicesFeature(core, serviceHub.context.noticeGroups);
useRemoteConfiguration(core);
diff --git a/src/modules/features/ReplicationStatusLabel.ts b/src/modules/features/ReplicationStatusLabel.ts
index 8358127c..ef596b56 100644
--- a/src/modules/features/ReplicationStatusLabel.ts
+++ b/src/modules/features/ReplicationStatusLabel.ts
@@ -13,13 +13,25 @@ import {
export const FAST_FORWARD_COUNT_LINGER_MS = 3_000;
-function readPositiveCheckedChanges(source: ReactiveValue): number | undefined {
- const count = source.value.couchDB?.pull?.checkedChanges;
+export type FastForwardCheckedChanges = {
+ push?: number;
+ pull?: number;
+};
+
+function readPositiveCheckedChanges(
+ source: ReactiveValue,
+ direction: keyof FastForwardCheckedChanges
+): number | undefined {
+ const count = source.value.couchDB?.[direction]?.checkedChanges;
return typeof count === "number" && Number.isFinite(count) && count > 0 ? count : undefined;
}
+function copyCheckedChanges(counts: FastForwardCheckedChanges): FastForwardCheckedChanges {
+ return { ...counts };
+}
+
/**
- * Briefly displays changing CouchDB pull-feed comparison counts.
+ * Briefly displays changing CouchDB push- and pull-feed comparison counts.
*
* Unrelated replication-stat changes do not extend the linger or redisplay an
* expired count. A positive initial snapshot is displayed to late subscribers.
@@ -27,46 +39,61 @@ function readPositiveCheckedChanges(source: ReactiveValue):
export function createFastForwardCheckedChangesDisplay(
source: ReactiveValue,
lingerMs = FAST_FORWARD_COUNT_LINGER_MS
-): DisposableReactiveValue {
- const displayed = reactiveSource(undefined);
+): DisposableReactiveValue {
+ const displayed = reactiveSource({});
const linger = Math.max(0, lingerMs);
- let lastObserved = readPositiveCheckedChanges(source);
- let clearTimer: number | undefined;
+ const directions: (keyof FastForwardCheckedChanges)[] = ["push", "pull"];
+ const lastObserved: FastForwardCheckedChanges = {};
+ const clearTimers = new Map();
let disposed = false;
- const cancelClear = () => {
- if (clearTimer === undefined) return;
- compatGlobal.clearTimeout(clearTimer);
- clearTimer = undefined;
+ const cancelClear = (direction: keyof FastForwardCheckedChanges) => {
+ const timer = clearTimers.get(direction);
+ if (timer === undefined) return;
+ compatGlobal.clearTimeout(timer);
+ clearTimers.delete(direction);
};
- const scheduleClear = () => {
- clearTimer = compatGlobal.setTimeout(() => {
- clearTimer = undefined;
+ const scheduleClear = (direction: keyof FastForwardCheckedChanges) => {
+ const timer = compatGlobal.setTimeout(() => {
+ clearTimers.delete(direction);
if (disposed) return;
- if (readPositiveCheckedChanges(source) !== lastObserved) {
+ if (readPositiveCheckedChanges(source, direction) !== lastObserved[direction]) {
update();
return;
}
- displayed.value = undefined;
+ const nextDisplayed = copyCheckedChanges(displayed.value);
+ delete nextDisplayed[direction];
+ displayed.value = nextDisplayed;
}, linger);
+ clearTimers.set(direction, timer);
};
const update = () => {
if (disposed) return;
- const nextCount = readPositiveCheckedChanges(source);
- if (nextCount === lastObserved) return;
- lastObserved = nextCount;
- cancelClear();
- if (nextCount === undefined) {
- displayed.value = undefined;
- return;
+ const nextDisplayed = copyCheckedChanges(displayed.value);
+ let changed = false;
+ for (const direction of directions) {
+ const nextCount = readPositiveCheckedChanges(source, direction);
+ if (nextCount === lastObserved[direction]) continue;
+ lastObserved[direction] = nextCount;
+ cancelClear(direction);
+ if (nextCount === undefined) {
+ delete nextDisplayed[direction];
+ changed = true;
+ continue;
+ }
+ nextDisplayed[direction] = nextCount;
+ scheduleClear(direction);
+ changed = true;
}
- displayed.value = nextCount;
- scheduleClear();
+ if (changed) displayed.value = nextDisplayed;
};
- if (lastObserved !== undefined) {
- displayed.value = lastObserved;
- scheduleClear();
+ for (const direction of directions) {
+ const count = readPositiveCheckedChanges(source, direction);
+ if (count === undefined) continue;
+ lastObserved[direction] = count;
+ displayed.value = { ...displayed.value, [direction]: count };
+ scheduleClear(direction);
}
source.onChanged(update);
@@ -83,7 +110,7 @@ export function createFastForwardCheckedChangesDisplay(
dispose() {
if (disposed) return;
disposed = true;
- cancelClear();
+ for (const direction of directions) cancelClear(direction);
source.offChanged(update);
},
};
@@ -113,7 +140,7 @@ function remainingSequenceLabel(
export function projectReplicationStatusLabel(
stats: ReplicationStatics,
networkStatus: RemoteActivityStatus,
- fastForwardCount?: number
+ fastForwardCounts?: FastForwardCheckedChanges
): ReplicationStatusProjection {
const { sent, arrived, maxPullSeq, maxPushSeq, lastSyncPullSeq, lastSyncPushSeq } = stats;
const networkActivity = formatRemoteActivityStatusLabel(networkStatus);
@@ -154,14 +181,19 @@ export function projectReplicationStatusLabel(
(couchDB.pull?.checking === true || couchDB.push?.checking === true);
if (isFastForwardChecking) icon = "âŠ";
- const visibleFastForwardCount =
- couchDB !== undefined &&
- typeof fastForwardCount === "number" &&
- Number.isFinite(fastForwardCount) &&
- fastForwardCount > 0
- ? fastForwardCount
- : undefined;
- const fastForwardLabel = visibleFastForwardCount === undefined ? "" : " (⊠" + visibleFastForwardCount + ")";
+ const visibleFastForwardCounts: FastForwardCheckedChanges = {};
+ if (couchDB !== undefined) {
+ for (const direction of ["push", "pull"] as const) {
+ const count = fastForwardCounts?.[direction];
+ if (typeof count === "number" && Number.isFinite(count) && count > 0) {
+ visibleFastForwardCounts[direction] = count;
+ }
+ }
+ }
+ const fastForwardLabels = (["push", "pull"] as const)
+ .filter((direction) => visibleFastForwardCounts[direction] !== undefined)
+ .map((direction) => ` (âŠ${direction === "push" ? "â" : "â"} ${visibleFastForwardCounts[direction]})`);
+ const fastForwardLabel = fastForwardLabels.join("");
const parts: StatusBarPart[] = [];
if (networkStatus.trackedRequestCount > 0) {
@@ -177,13 +209,15 @@ export function projectReplicationStatusLabel(
parts.push({ key: "network", text: networkActivity });
}
parts.push({ key: "sync", text: "Sync: " + icon });
- if (visibleFastForwardCount !== undefined) {
+ for (const direction of ["push", "pull"] as const) {
+ const count = visibleFastForwardCounts[direction];
+ if (count === undefined) continue;
parts.push({
- key: "ff",
- text: " (⊠",
- value: visibleFastForwardCount,
+ key: `ff-${direction}`,
+ text: ` (âŠ${direction === "push" ? "â" : "â"} `,
+ value: count,
suffix: ")",
- title: "Compared CouchDB feed entries",
+ title: `Compared CouchDB ${direction} feed entries`,
});
}
parts.push({ key: "sent", text: " â ", value: sent });
diff --git a/src/modules/features/ReplicationStatusLabel.unit.spec.ts b/src/modules/features/ReplicationStatusLabel.unit.spec.ts
index 054d597e..74b63196 100644
--- a/src/modules/features/ReplicationStatusLabel.unit.spec.ts
+++ b/src/modules/features/ReplicationStatusLabel.unit.spec.ts
@@ -90,18 +90,22 @@ describe("projectReplicationStatusLabel", () => {
const idleWithCount = projectReplicationStatusLabel(
stats({ syncStatus, couchDB: couchDBProgress("idle", 19, true) }),
noNetworkActivity,
- 19
+ { push: 19, pull: 19 }
);
expect(idleWithCount.icon).toBe(icon);
- expect(idleWithCount.parts.some((part) => part.key === "ff")).toBe(true);
+ expect(idleWithCount.parts.some((part) => part.key === "ff-push")).toBe(true);
+ expect(idleWithCount.parts.some((part) => part.key === "ff-pull")).toBe(true);
- const withoutCouchDB = projectReplicationStatusLabel(stats({ syncStatus }), noNetworkActivity, 19);
+ const withoutCouchDB = projectReplicationStatusLabel(stats({ syncStatus }), noNetworkActivity, {
+ push: 19,
+ pull: 19,
+ });
expect(withoutCouchDB.icon).toBe(icon);
- expect(withoutCouchDB.parts.some((part) => part.key === "ff")).toBe(false);
+ expect(withoutCouchDB.parts.some((part) => part.key.startsWith("ff-"))).toBe(false);
}
const noFastForward = projectReplicationStatusLabel(stats(), noNetworkActivity);
- expect(noFastForward.parts.some((part) => part.key === "ff")).toBe(false);
+ expect(noFastForward.parts.some((part) => part.key.startsWith("ff-"))).toBe(false);
expect(noFastForward.message).toBe("Sync: ⥠â 0 â 0");
});
@@ -123,18 +127,40 @@ describe("projectReplicationStatusLabel", () => {
couchDB: couchDBProgress("continuous", 1200, false),
}),
noNetworkActivity,
- 1200
+ { pull: 1200 }
);
expect(transferred.icon).toBe("âĄ");
- expect(transferred.message).toBe("Sync: ⥠(⊠1200) â 0 â 7");
- expect(transferred.parts.map((part) => part.key)).toEqual(["sync", "ff", "sent", "arrived"]);
+ expect(transferred.message).toBe("Sync: ⥠(âŠâ 1200) â 0 â 7");
+ expect(transferred.parts.map((part) => part.key)).toEqual(["sync", "ff-pull", "sent", "arrived"]);
expect(transferred.parts[1]).toMatchObject({
- key: "ff",
- text: " (⊠",
+ key: "ff-pull",
+ text: " (âŠâ ",
value: 1200,
suffix: ")",
+ title: "Compared CouchDB pull feed entries",
});
+ const pushChecking = projectReplicationStatusLabel(
+ stats({
+ couchDB: {
+ phase: "continuous",
+ push: { checkedChanges: 71975, completedBatches: 2878, checking: true },
+ },
+ }),
+ noNetworkActivity,
+ { push: 71975 }
+ );
+ expect(pushChecking.icon).toBe("âŠ");
+ expect(pushChecking.message).toBe("Sync: ⊠(âŠâ 71975) â 0 â 0");
+ expect(pushChecking.parts[1]).toMatchObject({ key: "ff-push", value: 71975 });
+
+ const bothChecking = projectReplicationStatusLabel(
+ stats({ couchDB: couchDBProgress("continuous", 10, true) }),
+ noNetworkActivity,
+ { push: 71975, pull: 10 }
+ );
+ expect(bothChecking.message).toBe("Sync: ⊠(âŠâ 71975) (âŠâ 10) â 0 â 0");
+
const idle = projectReplicationStatusLabel(
stats({
syncStatus: "CONNECTED",
@@ -142,10 +168,10 @@ describe("projectReplicationStatusLabel", () => {
couchDB: couchDBProgress("idle", 1200, false),
}),
noNetworkActivity,
- 1200
+ { pull: 1200 }
);
expect(idle.icon).toBe("âĄ");
- expect(idle.message).toBe("Sync: ⥠(⊠1200) â 0 â 7");
+ expect(idle.message).toBe("Sync: ⥠(âŠâ 1200) â 0 â 7");
});
it("keeps the error icon ahead of an active CouchDB checking indicator", () => {
@@ -154,7 +180,7 @@ describe("projectReplicationStatusLabel", () => {
syncStatus: "ERRORED",
couchDB: {
...couchDBProgress("one-shot", 2, true),
- push: { checkedChanges: 0, completedBatches: 0, checking: true },
+ push: { checkedChanges: 2, completedBatches: 1, checking: true },
},
}),
noNetworkActivity
@@ -181,41 +207,41 @@ describe("createFastForwardCheckedChangesDisplay", () => {
);
const display = createFastForwardCheckedChangesDisplay(source);
- expect(display.value).toBe(1200);
+ expect(display.value).toEqual({ pull: 1200 });
vi.advanceTimersByTime(1000);
source.value = stats({
sent: 3,
couchDB: couchDBProgress("idle", 1200, false),
});
- expect(display.value).toBe(1200);
+ expect(display.value).toEqual({ pull: 1200 });
vi.advanceTimersByTime(FAST_FORWARD_COUNT_LINGER_MS - 1001);
- expect(display.value).toBe(1200);
+ expect(display.value).toEqual({ pull: 1200 });
source.value = stats({
sent: 4,
couchDB: couchDBProgress("idle", 1200, false),
});
vi.advanceTimersByTime(1);
- expect(display.value).toBeUndefined();
+ expect(display.value).toEqual({});
source.value = stats({
syncStatus: "PAUSED",
couchDB: couchDBProgress("continuous", 1200, true),
});
- expect(display.value).toBeUndefined();
+ expect(display.value).toEqual({});
source.value = stats({
couchDB: couchDBProgress("continuous", 1201, true),
});
- expect(display.value).toBe(1201);
+ expect(display.value).toEqual({ pull: 1201 });
source.value = stats({
couchDB: couchDBProgress("one-shot", 0, true),
});
- expect(display.value).toBeUndefined();
+ expect(display.value).toEqual({});
source.value = stats();
- expect(display.value).toBeUndefined();
+ expect(display.value).toEqual({});
display.dispose();
});
@@ -227,16 +253,46 @@ describe("createFastForwardCheckedChangesDisplay", () => {
vi.advanceTimersByTime(FAST_FORWARD_COUNT_LINGER_MS);
source.value = stats({ couchDB: couchDBProgress("continuous", 6, true) });
- expect(display.value).toBe(5);
+ expect(display.value).toEqual({ pull: 5 });
});
it("does not display progress for providers without CouchDB replication state", () => {
const source = reactiveSource(stats());
const display = createFastForwardCheckedChangesDisplay(source);
- expect(display.value).toBeUndefined();
+ expect(display.value).toEqual({});
source.value = stats({ couchDB: { phase: "continuous" } });
- expect(display.value).toBeUndefined();
+ expect(display.value).toEqual({});
+ display.dispose();
+ });
+
+ it("tracks push and pull independently and expires only the direction that stopped changing", () => {
+ const source = reactiveSource(
+ stats({
+ couchDB: {
+ phase: "catch-up",
+ pull: { checkedChanges: 4, completedBatches: 1, checking: true },
+ push: { checkedChanges: 40, completedBatches: 2, checking: true },
+ },
+ })
+ );
+ const display = createFastForwardCheckedChangesDisplay(source, 100);
+
+ expect(display.value).toEqual({ push: 40, pull: 4 });
+ vi.advanceTimersByTime(50);
+ source.value = stats({
+ couchDB: {
+ phase: "catch-up",
+ pull: { checkedChanges: 4, completedBatches: 1, checking: true },
+ push: { checkedChanges: 41, completedBatches: 2, checking: true },
+ },
+ });
+ expect(display.value).toEqual({ push: 41, pull: 4 });
+
+ vi.advanceTimersByTime(50);
+ expect(display.value).toEqual({ push: 41 });
+ vi.advanceTimersByTime(50);
+ expect(display.value).toEqual({});
display.dispose();
});
});
diff --git a/src/modules/features/SettingDialogue/GeneralSettingSpecs.ts b/src/modules/features/SettingDialogue/GeneralSettingSpecs.ts
index a839a2e1..e3af3906 100644
--- a/src/modules/features/SettingDialogue/GeneralSettingSpecs.ts
+++ b/src/modules/features/SettingDialogue/GeneralSettingSpecs.ts
@@ -35,6 +35,8 @@ export function createGeneralSettingSpecGroups({
},
{ key: "showStatusOnStatusbar", control: { type: "toggle" } },
{ key: "hideFileWarningNotice", control: { type: "toggle" } },
+ { key: "syncGuardEnabled", control: { type: "toggle" } },
+ { key: "syncGuardMinimumFiles", control: { type: "number", min: 1 } },
{
key: "networkWarningStyle",
control: {
diff --git a/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.declarative.unit.spec.ts b/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.declarative.unit.spec.ts
index da894f79..afe7986f 100644
--- a/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.declarative.unit.spec.ts
+++ b/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.declarative.unit.spec.ts
@@ -1,6 +1,7 @@
import { MemoryValueStore } from "@vrtmrz/value-store";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { DEFAULT_SETTINGS } from "@vrtmrz/livesync-commonlib/compat/common/types";
+import { DEFAULT_SYNC_GUARD_SETTINGS, type SyncGuardSettings } from "@/modules/services/ObsidianSettingService.ts";
import type { SettingDefinitionGroup, SettingDefinitionItem, SettingDefinitionPage } from "obsidian";
import type { PageFunctions } from "./SettingPane.ts";
@@ -152,6 +153,7 @@ type SettingsTabOptions = {
};
function createSettingsTab(options: SettingsTabOptions = {}): ObsidianLiveSyncSettingTab {
+ const syncGuardSettings: SyncGuardSettings = { ...DEFAULT_SYNC_GUARD_SETTINGS };
const core = {
settings: { ...DEFAULT_SETTINGS, useAdvancedMode: true },
confirm: {
@@ -160,6 +162,10 @@ function createSettingsTab(options: SettingsTabOptions = {}): ObsidianLiveSyncSe
services: {
setting: {
deviceValues: new MemoryValueStore(),
+ syncGuardSettings,
+ setSyncGuardSetting: vi.fn(async (key: keyof SyncGuardSettings, value: boolean | number) => {
+ Object.assign(syncGuardSettings, { [key]: value });
+ }),
getDeviceAndVaultName: vi.fn(() => ""),
saveSettingData: vi.fn(async () => undefined),
},
@@ -179,8 +185,8 @@ function createSettingsTab(options: SettingsTabOptions = {}): ObsidianLiveSyncSe
};
const tab = new ObsidianLiveSyncSettingTab({} as never, plugin as never);
Object.assign(tab, {
- _editingSettings: { ...DEFAULT_SETTINGS, useAdvancedMode: true },
- initialSettings: { ...DEFAULT_SETTINGS, useAdvancedMode: true },
+ _editingSettings: { ...DEFAULT_SETTINGS, ...syncGuardSettings, useAdvancedMode: true },
+ initialSettings: { ...DEFAULT_SETTINGS, ...syncGuardSettings, useAdvancedMode: true },
});
return tab;
}
@@ -335,6 +341,8 @@ describe("ObsidianLiveSyncSettingTab native page lifecycle", () => {
"showOnlyIconsOnEditor",
"showStatusOnStatusbar",
"hideFileWarningNotice",
+ "syncGuardEnabled",
+ "syncGuardMinimumFiles",
"networkWarningStyle",
]);
expect(
@@ -345,6 +353,39 @@ describe("ObsidianLiveSyncSettingTab native page lifecycle", () => {
).toEqual(["useAdvancedMode", "usePowerUserMode", "useEdgeCaseMode"]);
});
+ it("persists declarative SyncGuard controls through Obsidian's local settings service", async () => {
+ const tab = createSettingsTab();
+ const setting = tab.services.setting as typeof tab.services.setting & {
+ setSyncGuardSetting: ReturnType;
+ };
+ const initialCoreSettings = { ...tab.core.settings };
+
+ await tab.setControlValue("syncGuardEnabled", true);
+ await tab.setControlValue("syncGuardMinimumFiles", 6);
+
+ expect(setting.setSyncGuardSetting).toHaveBeenNthCalledWith(1, "syncGuardEnabled", true);
+ expect(setting.setSyncGuardSetting).toHaveBeenNthCalledWith(2, "syncGuardMinimumFiles", 6);
+ expect(tab.getControlValue("syncGuardEnabled")).toBe(true);
+ expect(tab.getControlValue("syncGuardMinimumFiles")).toBe(6);
+ expect(tab.core.settings).toEqual(initialCoreSettings);
+ expect(setting.saveSettingData).not.toHaveBeenCalled();
+ });
+
+ it("refreshes active settings when local SyncGuard preferences change elsewhere", async () => {
+ const tab = createSettingsTab();
+ tab.isShown = true;
+ const refresh = vi.spyOn(tab, "requestPageRefresh").mockImplementation(() => undefined);
+ const setting = tab.services.setting as typeof tab.services.setting & { syncGuardSettings: SyncGuardSettings };
+ setting.syncGuardSettings.syncGuardEnabled = true;
+ setting.syncGuardSettings.syncGuardMinimumFiles = 13;
+
+ await tab.requestReload();
+
+ expect(tab.editingSettings.syncGuardEnabled).toBe(true);
+ expect(tab.editingSettings.syncGuardMinimumFiles).toBe(13);
+ expect(refresh).toHaveBeenCalledOnce();
+ });
+
it("omits the old Setup child page and keeps standard General and Advanced pages native", () => {
const tab = createSettingsTab();
const pages = collectPages(tab.getSettingDefinitions());
diff --git a/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.ts b/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.ts
index a3c72a31..e5680d8e 100644
--- a/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.ts
+++ b/src/modules/features/SettingDialogue/ObsidianLiveSyncSettingTab.ts
@@ -77,6 +77,11 @@ import { createExtraMenuSettingSpecGroup, createGeneralSettingSpecGroups } from
import { SetupManager } from "@/modules/features/SetupManager.ts";
import { isP2PMainRemote } from "@/common/remoteConfiguration.ts";
import { withOwnedRemoteResource } from "@/common/ownedRemoteResource.ts";
+import {
+ getSyncGuardSettings,
+ isSyncGuardSettingKey,
+ setSyncGuardSetting,
+} from "@/modules/services/ObsidianSettingService.ts";
// For creating a document
// const toc = new Set();
@@ -138,6 +143,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
if (
this.isReconstructionRequiredSetting(k) ||
this.isConfigurationEncryptionSetting(k) ||
+ isSyncGuardSettingKey(k) ||
k in OnDialogSettingsDefault
) {
// //@ts-ignore
@@ -157,6 +163,10 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
(e) => this.isDirty(e) && !this.isReconstructionRequiredSetting(e)
);
await this.saveConfigurationEncryptionIfDirty(changedKeys);
+ for (const key of changedKeys.filter(isSyncGuardSettingKey)) {
+ await setSyncGuardSetting(this.services.setting, key, this.editingSettings[key]);
+ this.copySettingValue(this.initialSettings, this.editingSettings, key);
+ }
this.applySetting(changedKeys);
this.reloadAllSettings();
}
@@ -177,6 +187,11 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
await this.saveConfigurationEncryptionIfDirty(appliedKeys);
for (const k of appliedKeys) {
if (this.isConfigurationEncryptionSetting(k)) continue;
+ if (isSyncGuardSettingKey(k)) {
+ await setSyncGuardSetting(this.services.setting, k, this.editingSettings[k]);
+ this.copySettingValue(this.initialSettings, this.editingSettings, k);
+ continue;
+ }
if (k in OnDialogSettingsDefault) {
await this.saveLocalSetting(k as keyof OnDialogSettings);
this.copySettingValue(this.initialSettings, this.editingSettings, k);
@@ -307,7 +322,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
ret.configPassphrase = this.localConfigurationPassphrase;
ret.preset = "";
ret.deviceAndVaultName = this.services.setting.getDeviceAndVaultName();
- return ret;
+ return { ...ret, ...getSyncGuardSettings(this.services.setting) };
}
computeAllLocalSettings(): Partial {
const syncMode = this.editingSettings?.liveSync
@@ -375,7 +390,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
return;
}
const localSetting = this.reloadAllLocalSettings();
- if (key in this.core.settings) {
+ if (isSyncGuardSettingKey(key) || key in this.core.settings) {
if (key in localSetting) {
this.copySettingValue(this.initialSettings, localSetting, key);
this.copySettingValue(this.editingSettings, localSetting, key);
@@ -567,25 +582,25 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
const nativeTabIsShown =
this.supportsDeclarativeSettings() && this.containerEl !== undefined && this.containerEl.isShown();
if (this.isShown || nativeTabIsShown) {
- const newConf = this.core.settings;
- const keys = Object.keys(newConf) as (keyof ObsidianLiveSyncSettings)[];
+ const newConf = { ...this.core.settings, ...getSyncGuardSettings(this.services.setting) };
+ const keys = Object.keys(newConf) as AllSettingItemKey[];
let hasLoaded = false;
let catalogueVisibilityChanged = false;
for (const k of keys) {
- if (isObjectDifferent(newConf[k], this.initialSettings?.[k])) {
+ if (isObjectDifferent(Reflect.get(newConf, k), this.initialSettings?.[k])) {
// Something has changed
- if (this.isDirty(k as AllSettingItemKey)) {
+ if (this.isDirty(k)) {
// And modified.
this.core.confirm.askInPopup(
`config-reloaded-${k}`,
$msg("obsidianLiveSyncSettingTab.msgSettingModified", {
- setting: getConfName(k as AllSettingItemKey),
+ setting: getConfName(k),
}),
(anchor) => {
anchor.text = $msg("obsidianLiveSyncSettingTab.optionHere");
anchor.addEventListener("click", () => {
- this.refreshSetting(k as AllSettingItemKey);
- if (this.changesPageCatalogue(k as AllSettingItemKey)) {
+ this.refreshSetting(k);
+ if (this.changesPageCatalogue(k)) {
this.requestCatalogueRefresh();
} else {
this.requestPageRefresh();
@@ -595,12 +610,12 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
);
} else {
// not modified
- this.refreshSetting(k as AllSettingItemKey);
+ this.refreshSetting(k);
if (k in OnDialogSettingsDefault) {
continue;
}
hasLoaded = true;
- if (this.changesPageCatalogue(k as AllSettingItemKey)) {
+ if (this.changesPageCatalogue(k)) {
catalogueVisibilityChanged = true;
}
}
diff --git a/src/modules/features/SettingDialogue/SettingSpec.unit.spec.ts b/src/modules/features/SettingDialogue/SettingSpec.unit.spec.ts
index 7e957aeb..0261a2da 100644
--- a/src/modules/features/SettingDialogue/SettingSpec.unit.spec.ts
+++ b/src/modules/features/SettingDialogue/SettingSpec.unit.spec.ts
@@ -142,6 +142,8 @@ describe("General setting specifications", () => {
"showOnlyIconsOnEditor",
"showStatusOnStatusbar",
"hideFileWarningNotice",
+ "syncGuardEnabled",
+ "syncGuardMinimumFiles",
"networkWarningStyle",
"lessInformationInLog",
"showVerboseLog",
diff --git a/src/modules/features/SettingDialogue/settingConstants.ts b/src/modules/features/SettingDialogue/settingConstants.ts
index 495f8f76..725ae2ea 100644
--- a/src/modules/features/SettingDialogue/settingConstants.ts
+++ b/src/modules/features/SettingDialogue/settingConstants.ts
@@ -1,25 +1,50 @@
-export {
- AllSettingDefault,
- OnDialogSettingsDefault,
- SettingInformation,
-} from "@vrtmrz/livesync-commonlib/compat/common/settingConstants";
-export type {
- AllSettings,
- AllSettingItemKey,
- AllStringItemKey,
- AllNumericItemKey,
- AllBooleanItemKey,
- OnDialogSettings,
- ValueOf,
-} from "@vrtmrz/livesync-commonlib/compat/common/settingConstants";
+export { OnDialogSettingsDefault } from "@vrtmrz/livesync-commonlib/compat/common/settingConstants";
+export type { OnDialogSettings } from "@vrtmrz/livesync-commonlib/compat/common/settingConstants";
import {
+ AllSettingDefault as CommonlibAllSettingDefault,
+ SettingInformation as CommonlibSettingInformation,
getConfig as getCommonlibConfig,
getConfName as getCommonlibConfName,
- type AllSettingItemKey,
+ type AllSettings as CommonlibAllSettings,
+ type AllSettingItemKey as CommonlibAllSettingItemKey,
+ type AllStringItemKey as CommonlibAllStringItemKey,
+ type AllNumericItemKey as CommonlibAllNumericItemKey,
+ type AllBooleanItemKey as CommonlibAllBooleanItemKey,
} from "@vrtmrz/livesync-commonlib/compat/common/settingConstants";
+import type { ConfigurationItem } from "@vrtmrz/livesync-commonlib/compat/common/types";
import type { MessageTranslator } from "@vrtmrz/livesync-commonlib/context";
import { translateLiveSyncMessage } from "@/common/translation";
+import {
+ DEFAULT_SYNC_GUARD_SETTINGS,
+ isSyncGuardSettingKey,
+ type SyncGuardSettingKey,
+ type SyncGuardSettings,
+} from "@/modules/services/ObsidianSettingService";
+
+export type AllSettings = Omit & SyncGuardSettings;
+export type AllStringItemKey = CommonlibAllStringItemKey;
+export type AllNumericItemKey = Exclude | "syncGuardMinimumFiles";
+export type AllBooleanItemKey = Exclude | "syncGuardEnabled";
+export type AllSettingItemKey = Exclude | SyncGuardSettingKey;
+export type ValueOf = AllSettings[T];
+
+const syncGuardSettingInformation: Record = {
+ syncGuardEnabled: {
+ name: "Enable SyncGuard",
+ desc: "Enable device-local SyncGuard protection during synchronisation.",
+ },
+ syncGuardMinimumFiles: {
+ name: "SyncGuard minimum files",
+ desc: "Minimum number of Vault files required for SyncGuard protection to apply.",
+ },
+};
+
+export const AllSettingDefault = { ...CommonlibAllSettingDefault, ...DEFAULT_SYNC_GUARD_SETTINGS };
+export const SettingInformation: Partial> = {
+ ...CommonlibSettingInformation,
+ ...syncGuardSettingInformation,
+};
// Commonlib defaults `translate` to its English-only translator, so every caller which omits
// it silently renders English regardless of `displayLanguage`. Default it to the LiveSync
@@ -28,10 +53,12 @@ import { translateLiveSyncMessage } from "@/common/translation";
/** `getConfig` with the LiveSync catalogue applied by default. */
export function getConfig(key: AllSettingItemKey, translate: MessageTranslator = translateLiveSyncMessage) {
+ if (isSyncGuardSettingKey(key)) return syncGuardSettingInformation[key];
return getCommonlibConfig(key, translate);
}
/** `getConfName` with the LiveSync catalogue applied by default. See `getConfig`. */
export function getConfName(key: AllSettingItemKey, translate: MessageTranslator = translateLiveSyncMessage) {
+ if (isSyncGuardSettingKey(key)) return syncGuardSettingInformation[key].name;
return getCommonlibConfName(key, translate);
}
diff --git a/src/modules/features/SetupWizard/dialogs/SetupRemoteCouchDB.svelte b/src/modules/features/SetupWizard/dialogs/SetupRemoteCouchDB.svelte
index a163ceee..2c640ee0 100644
--- a/src/modules/features/SetupWizard/dialogs/SetupRemoteCouchDB.svelte
+++ b/src/modules/features/SetupWizard/dialogs/SetupRemoteCouchDB.svelte
@@ -314,14 +314,16 @@
{#if processing}
{translateMessage("Checking connection... Please wait.")}
{:else}
+ {#if setupMode === "settings"}
+
+ {translateMessage(
+ "Saving without a successful connection test keeps this profile, but automatic synchronisation may fail until the connection is corrected."
+ )}
+
+ {/if}
checkAndCommit()} />
{#if setupMode === "settings"}
-
- {translateMessage(
- "Saving without a successful connection test keeps this profile, but automatic synchronisation may fail until the connection is corrected."
- )}
-
button) {
+ margin-left: 0;
+ }
+
.dialog-host :global(.button-group) {
display: flex;
flex-direction: column;
diff --git a/src/modules/services/ObsidianSettingService.ts b/src/modules/services/ObsidianSettingService.ts
index 87b93b59..7ef5b2a1 100644
--- a/src/modules/services/ObsidianSettingService.ts
+++ b/src/modules/services/ObsidianSettingService.ts
@@ -8,6 +8,36 @@ import {
type SettingServiceDependencies,
} from "@vrtmrz/livesync-commonlib/compat/services/base/SettingService";
import type { ObsidianServiceContext } from "@/modules/services/ObsidianServiceContext";
+import { EVENT_SYNC_GUARD_PREFERENCES_CHANGED } from "@/common/events.ts";
+
+export type SyncGuardSettings = {
+ syncGuardEnabled: boolean;
+ syncGuardMinimumFiles: number;
+};
+
+export type SyncGuardSettingKey = keyof SyncGuardSettings;
+
+export const DEFAULT_SYNC_GUARD_SETTINGS: Readonly = {
+ syncGuardEnabled: false,
+ syncGuardMinimumFiles: 20,
+};
+
+export function isSyncGuardSettingKey(key: string): key is SyncGuardSettingKey {
+ return key === "syncGuardEnabled" || key === "syncGuardMinimumFiles";
+}
+
+/** Access the Obsidian host preferences from a service exposed through the shared hub type. */
+export function getSyncGuardSettings(setting: SettingService): Readonly {
+ return (setting as ObsidianSettingService).syncGuardSettings;
+}
+
+export async function setSyncGuardSetting(
+ setting: SettingService,
+ key: K,
+ value: SyncGuardSettings[K]
+): Promise {
+ await (setting as ObsidianSettingService).setSyncGuardSetting(key, value);
+}
export function normaliseObsidianSettingsData(data: unknown): ObsidianLiveSyncSettings | undefined {
if (typeof data !== "object" || data === null || Array.isArray(data)) return undefined;
@@ -15,6 +45,13 @@ export function normaliseObsidianSettingsData(data: unknown): ObsidianLiveSyncSe
}
export class ObsidianSettingService extends SettingService {
+ private _syncGuardSettings: SyncGuardSettings = { ...DEFAULT_SYNC_GUARD_SETTINGS };
+ private legacySyncGuardSettings: Partial = {};
+
+ get syncGuardSettings(): Readonly {
+ return this._syncGuardSettings;
+ }
+
constructor(context: T, dependencies: SettingServiceDependencies) {
super(context, dependencies);
this.onSettingSaved.addHandler((settings) => {
@@ -26,10 +63,62 @@ export class ObsidianSettingService extends Se
return Promise.resolve(true);
});
}
+
+ override async loadSettings(): Promise {
+ await super.loadSettings();
+ const storedEnabled = await this.config.getObject("syncGuardEnabled");
+ const storedMinimumFiles = await this.config.getObject("syncGuardMinimumFiles");
+ const enabled = typeof storedEnabled === "boolean" ? storedEnabled : this.legacySyncGuardSettings.syncGuardEnabled;
+ const minimumFiles =
+ typeof storedMinimumFiles === "number" && Number.isFinite(storedMinimumFiles) && storedMinimumFiles >= 1
+ ? storedMinimumFiles
+ : this.legacySyncGuardSettings.syncGuardMinimumFiles;
+
+ this._syncGuardSettings = {
+ syncGuardEnabled: enabled ?? DEFAULT_SYNC_GUARD_SETTINGS.syncGuardEnabled,
+ syncGuardMinimumFiles: minimumFiles ?? DEFAULT_SYNC_GUARD_SETTINGS.syncGuardMinimumFiles,
+ };
+ if (storedEnabled === undefined && enabled !== undefined) {
+ await this.config.setObject("syncGuardEnabled", enabled);
+ }
+ if (storedMinimumFiles === undefined && minimumFiles !== undefined) {
+ await this.config.setObject("syncGuardMinimumFiles", minimumFiles);
+ }
+ }
+
+ async setSyncGuardSetting(key: K, value: SyncGuardSettings[K]): Promise {
+ if (key === "syncGuardMinimumFiles" && (typeof value !== "number" || !Number.isInteger(value) || value < 1)) {
+ throw new RangeError("SyncGuard minimum files must be a positive integer");
+ }
+ if (this._syncGuardSettings[key] === value) return;
+ await this.config.setObject(key, value);
+ this._syncGuardSettings = { ...this._syncGuardSettings, [key]: value };
+ // These preferences change only the workspace overlay. Re-applying all
+ // replication settings would unnecessarily interrupt an active sync.
+ this.context.events.emitEvent(EVENT_SYNC_GUARD_PREFERENCES_CHANGED);
+ }
+
protected override async saveData(data: ObsidianLiveSyncSettings): Promise {
- return await this.context.liveSyncPlugin.saveData(data);
+ const persisted: Record = { ...data };
+ delete persisted.syncGuardEnabled;
+ delete persisted.syncGuardMinimumFiles;
+ return await this.context.liveSyncPlugin.saveData(persisted);
}
protected override async loadData(): Promise {
- return normaliseObsidianSettingsData(await this.context.liveSyncPlugin.loadData());
+ const data = normaliseObsidianSettingsData(await this.context.liveSyncPlugin.loadData());
+ if (!data) return undefined;
+ const persisted: Record = { ...data };
+ const { syncGuardEnabled, syncGuardMinimumFiles } = persisted;
+ this.legacySyncGuardSettings = {
+ ...(typeof syncGuardEnabled === "boolean" ? { syncGuardEnabled } : {}),
+ ...(typeof syncGuardMinimumFiles === "number" &&
+ Number.isFinite(syncGuardMinimumFiles) &&
+ syncGuardMinimumFiles >= 1
+ ? { syncGuardMinimumFiles }
+ : {}),
+ };
+ delete persisted.syncGuardEnabled;
+ delete persisted.syncGuardMinimumFiles;
+ return persisted as unknown as ObsidianLiveSyncSettings;
}
}
diff --git a/src/modules/services/ObsidianSettingService.unit.spec.ts b/src/modules/services/ObsidianSettingService.unit.spec.ts
index 6df8404d..74cd4977 100644
--- a/src/modules/services/ObsidianSettingService.unit.spec.ts
+++ b/src/modules/services/ObsidianSettingService.unit.spec.ts
@@ -1,20 +1,29 @@
-import { describe, expect, it } from "vitest";
+import { describe, expect, it, vi } from "vitest";
import { createServiceContext } from "@vrtmrz/livesync-commonlib/context";
-import type { ObsidianLiveSyncSettings } from "@vrtmrz/livesync-commonlib/compat/common/types";
+import { DEFAULT_SETTINGS, type ObsidianLiveSyncSettings } from "@vrtmrz/livesync-commonlib/compat/common/types";
+import { EVENT_SETTING_SAVED } from "@vrtmrz/livesync-commonlib/compat/events/coreEvents";
+import { EVENT_SYNC_GUARD_PREFERENCES_CHANGED } from "@/common/events.ts";
import type { IAPIService } from "@vrtmrz/livesync-commonlib/compat/services/base/IService";
import { MemoryValueStore } from "@vrtmrz/value-store";
import type { ObsidianServiceContext } from "@/modules/services/ObsidianServiceContext";
import { normaliseObsidianSettingsData } from "./ObsidianSettingService.ts";
-import { ObsidianSettingService } from "./ObsidianSettingService.ts";
+import { getSyncGuardSettings, ObsidianSettingService, setSyncGuardSetting } from "./ObsidianSettingService.ts";
function createSettingService(
vaultName: string,
- deviceValues: MemoryValueStore
+ deviceValues: MemoryValueStore,
+ savedData?: unknown,
+ saveData = vi.fn(async (_settings: unknown) => undefined)
): ObsidianSettingService {
const context = createServiceContext() as unknown as ObsidianServiceContext;
+ context.liveSyncPlugin = {
+ loadData: vi.fn(async () => savedData),
+ saveData,
+ } as unknown as ObsidianServiceContext["liveSyncPlugin"];
const APIService = {
addLog: () => undefined,
getSystemVaultName: () => vaultName,
+ getAppID: () => "test-app",
} as unknown as IAPIService;
return new ObsidianSettingService(context, { APIService, deviceValues });
}
@@ -52,3 +61,102 @@ describe("normaliseObsidianSettingsData", () => {
await expect(secondValues.getValue("second-vault-shared-key")).resolves.toBe("second config");
});
});
+
+describe("SyncGuard device-local settings", () => {
+ const savedSettings = { ...DEFAULT_SETTINGS, isConfigured: true, additionalSuffixOfDatabaseName: "" };
+
+ it("loads safe defaults when no local values were saved", async () => {
+ const setting = createSettingService("first-vault", new MemoryValueStore(), savedSettings);
+
+ await setting.loadSettings();
+
+ expect(getSyncGuardSettings(setting)).toEqual({ syncGuardEnabled: false, syncGuardMinimumFiles: 20 });
+ });
+
+ it("migrates preferences stored by earlier builds and excludes them from plugin data", async () => {
+ const localValues = new MemoryValueStore();
+ const saveData = vi.fn(async (_data: unknown) => undefined);
+ const setting = createSettingService(
+ "first-vault",
+ localValues,
+ { ...savedSettings, syncGuardEnabled: true, syncGuardMinimumFiles: 7 },
+ saveData
+ );
+
+ await setting.loadSettings();
+ await setting.saveSettingData();
+
+ expect(getSyncGuardSettings(setting)).toEqual({ syncGuardEnabled: true, syncGuardMinimumFiles: 7 });
+ await expect(setting.config.getObject("syncGuardEnabled")).resolves.toBe(true);
+ await expect(setting.config.getObject("syncGuardMinimumFiles")).resolves.toBe(7);
+ expect(saveData.mock.lastCall?.[0]).not.toHaveProperty("syncGuardEnabled");
+ expect(saveData.mock.lastCall?.[0]).not.toHaveProperty("syncGuardMinimumFiles");
+ });
+
+ it("keeps existing device-local values authoritative over legacy plugin data", async () => {
+ const localValues = new MemoryValueStore();
+ const setting = createSettingService(
+ "first-vault",
+ localValues,
+ { ...savedSettings, syncGuardEnabled: true, syncGuardMinimumFiles: 5 }
+ );
+ await localValues.setObject("first-vault--syncGuardEnabled", false);
+ await localValues.setObject("first-vault--syncGuardMinimumFiles", 30);
+
+ await setting.loadSettings();
+
+ expect(getSyncGuardSettings(setting)).toEqual({ syncGuardEnabled: false, syncGuardMinimumFiles: 30 });
+ });
+
+ it("restores saved preferences after recreating the service", async () => {
+ const localValues = new MemoryValueStore();
+ const first = createSettingService("first-vault", localValues, savedSettings);
+ await first.loadSettings();
+ await setSyncGuardSetting(first, "syncGuardEnabled", true);
+ await setSyncGuardSetting(first, "syncGuardMinimumFiles", 12);
+
+ const reloaded = createSettingService("first-vault", localValues, savedSettings);
+ await reloaded.loadSettings();
+
+ expect(getSyncGuardSettings(reloaded)).toEqual({ syncGuardEnabled: true, syncGuardMinimumFiles: 12 });
+ });
+
+ it("rejects invalid file thresholds without changing local settings", async () => {
+ const setting = createSettingService("first-vault", new MemoryValueStore(), savedSettings);
+ await setting.loadSettings();
+ const storedBefore = await setting.config.getObject("syncGuardMinimumFiles");
+
+ for (const invalid of [0, -1, 1.5, NaN, Infinity]) {
+ await expect(setSyncGuardSetting(setting, "syncGuardMinimumFiles", invalid)).rejects.toThrow(RangeError);
+ }
+
+ expect(getSyncGuardSettings(setting).syncGuardMinimumFiles).toBe(20);
+ await expect(setting.config.getObject("syncGuardMinimumFiles")).resolves.toBe(storedBefore);
+ });
+
+ it("persists edits only in this Vault's device store and notifies the guard", async () => {
+ const localValues = new MemoryValueStore();
+ const saveData = vi.fn(async (_data: unknown) => undefined);
+ const first = createSettingService("first-vault", localValues, savedSettings, saveData);
+ const second = createSettingService("second-vault", localValues, savedSettings);
+ await first.loadSettings();
+ await second.loadSettings();
+ const initialSaveCount = saveData.mock.calls.length;
+ const changed = vi.fn();
+ const unrelatedReplicationSettingsSaved = vi.fn();
+ first.context.events.onEvent(EVENT_SYNC_GUARD_PREFERENCES_CHANGED, changed);
+ first.context.events.onEvent(EVENT_SETTING_SAVED, unrelatedReplicationSettingsSaved);
+
+ await setSyncGuardSetting(first, "syncGuardEnabled", true);
+ await setSyncGuardSetting(first, "syncGuardMinimumFiles", 8);
+ await setSyncGuardSetting(first, "syncGuardEnabled", true);
+
+ expect(getSyncGuardSettings(first)).toEqual({ syncGuardEnabled: true, syncGuardMinimumFiles: 8 });
+ expect(getSyncGuardSettings(second)).toEqual({ syncGuardEnabled: false, syncGuardMinimumFiles: 20 });
+ await expect(localValues.getObject("first-vault--syncGuardEnabled")).resolves.toBe(true);
+ await expect(localValues.getObject("first-vault--syncGuardMinimumFiles")).resolves.toBe(8);
+ expect(changed).toHaveBeenCalledTimes(2);
+ expect(unrelatedReplicationSettingsSaved).not.toHaveBeenCalled();
+ expect(saveData).toHaveBeenCalledTimes(initialSaveCount);
+ });
+});
diff --git a/src/serviceFeatures/redFlag.fetchState.ts b/src/serviceFeatures/redFlag.fetchState.ts
new file mode 100644
index 00000000..acc3f018
--- /dev/null
+++ b/src/serviceFeatures/redFlag.fetchState.ts
@@ -0,0 +1,109 @@
+import type { NecessaryServices } from "@vrtmrz/livesync-commonlib/compat/interfaces/ServiceModule";
+import type { ObsidianLiveSyncSettings } from "@vrtmrz/livesync-commonlib/settings";
+import {
+ REMOTE_COUCHDB,
+ REMOTE_MINIO,
+ REMOTE_P2P,
+} from "@vrtmrz/livesync-commonlib/compat/common/models/setting.const";
+import {
+ FlagFilesHumanReadable,
+ FlagFilesOriginal,
+} from "@vrtmrz/livesync-commonlib/compat/common/models/redflag.const";
+import { readFetchFlagState, writeFetchFlagState, type FetchFlagState } from "./redFlag.fetchStateMarkdown";
+
+type FetchStateHost = NecessaryServices;
+
+function endpointDescription(value: string): string {
+ try {
+ const url = new URL(value);
+ url.username = "";
+ url.password = "";
+ url.search = "";
+ url.hash = "";
+ return url.toString().replace(/\/+$/, "");
+ } catch {
+ return "";
+ }
+}
+
+/** Describe the current destination for confirmation, without connection credentials. */
+export function describeFetchRemote(settings: ObsidianLiveSyncSettings): string {
+ switch (settings.remoteType) {
+ case REMOTE_COUCHDB:
+ return ["CouchDB", endpointDescription(settings.couchDB_URI), settings.couchDB_DBNAME].join(" / ");
+ case REMOTE_MINIO:
+ return [
+ "Object Storage",
+ endpointDescription(settings.endpoint),
+ settings.region,
+ settings.bucket,
+ settings.bucketPrefix,
+ ]
+ .filter(Boolean)
+ .join(" / ");
+ case REMOTE_P2P:
+ return [
+ "P2P",
+ settings.P2P_AppID,
+ settings.P2P_roomID,
+ ...settings.P2P_relays.split(",").map((relay) => endpointDescription(relay.trim())),
+ ]
+ .filter(Boolean)
+ .join(" / ");
+ default:
+ throw new Error("The selected remote type cannot use Fast Setup.");
+ }
+}
+
+async function readFetchFlag(host: FetchStateHost) {
+ const storage = host.serviceModules.storageAccess;
+ // A newly scheduled fetch writes the human-readable flag, even if an older alias remains.
+ for (const name of [FlagFilesHumanReadable.FETCH_ALL, FlagFilesOriginal.FETCH_ALL]) {
+ const path = storage.normalisePath(name);
+ if (await storage.isExists(path)) {
+ return { path, markdown: await storage.readFileText(path) };
+ }
+ }
+ throw new Error("The scheduled fetch flag is missing.");
+}
+
+async function writeVerified(host: FetchStateHost, path: string, markdown: string) {
+ const storage = host.serviceModules.storageAccess;
+ if (!(await storage.writeFileAuto(path, markdown)) || (await storage.readFileText(path)) !== markdown) {
+ throw new Error("The Fast Setup choices could not be saved in the fetch flag.");
+ }
+}
+
+export async function readFetchSelection(host: FetchStateHost): Promise {
+ const { markdown } = await readFetchFlag(host);
+ if (!markdown.trim()) return "empty";
+ return readFetchFlagState(markdown) ?? "unreadable";
+}
+
+export async function saveFetchSelection(host: FetchStateHost, stage1: string, stage2: string): Promise {
+ const { path, markdown } = await readFetchFlag(host);
+ const state: FetchFlagState = {
+ type: "livesync-fast-setup",
+ version: 1,
+ stage1,
+ stage2,
+ };
+ const updated = writeFetchFlagState(markdown, state);
+ if (updated !== markdown) await writeVerified(host, path, updated);
+}
+
+export async function clearFetchSelection(host: FetchStateHost): Promise {
+ const { path, markdown } = await readFetchFlag(host);
+ const cleared = writeFetchFlagState(markdown, undefined);
+ if (cleared !== markdown) await writeVerified(host, path, cleared);
+}
+
+/** Remove both aliases, retaining the authoritative flag until the other alias is gone. */
+export async function deleteFetchFlags(host: NecessaryServices): Promise {
+ const storage = host.serviceModules.storageAccess;
+ for (const name of [FlagFilesOriginal.FETCH_ALL, FlagFilesHumanReadable.FETCH_ALL]) {
+ const path = storage.normalisePath(name);
+ if (await storage.isExists(path)) await storage.delete(path, true);
+ if (await storage.isExists(path)) throw new Error(`Could not remove ${name}.`);
+ }
+}
diff --git a/src/serviceFeatures/redFlag.fetchState.unit.spec.ts b/src/serviceFeatures/redFlag.fetchState.unit.spec.ts
new file mode 100644
index 00000000..9414f767
--- /dev/null
+++ b/src/serviceFeatures/redFlag.fetchState.unit.spec.ts
@@ -0,0 +1,202 @@
+import { describe, expect, it, vi } from "vitest";
+import { DEFAULT_SETTINGS } from "@vrtmrz/livesync-commonlib/settings";
+import { REMOTE_MINIO, REMOTE_P2P } from "@vrtmrz/livesync-commonlib/compat/common/models/setting.const";
+import {
+ FlagFilesHumanReadable,
+ FlagFilesOriginal,
+} from "@vrtmrz/livesync-commonlib/compat/common/models/redflag.const";
+import {
+ clearFetchSelection,
+ deleteFetchFlags,
+ describeFetchRemote,
+ readFetchSelection,
+ saveFetchSelection,
+} from "./redFlag.fetchState";
+import { readFetchFlagState, writeFetchFlagState } from "./redFlag.fetchStateMarkdown";
+import {
+ askSimpleFetchMode,
+ SIMPLE_FETCH_STAGE1_REMOTE_WINS,
+ SIMPLE_FETCH_STAGE2_REMOTE_DELETE_NONE,
+} from "./redFlag.simpleFetch";
+
+const modern = FlagFilesHumanReadable.FETCH_ALL;
+const legacy = FlagFilesOriginal.FETCH_ALL;
+const stage1 = SIMPLE_FETCH_STAGE1_REMOTE_WINS;
+const stage2 = SIMPLE_FETCH_STAGE2_REMOTE_DELETE_NONE;
+
+function fixture() {
+ const files = new Map([[modern, ""]]);
+ const settings = { ...DEFAULT_SETTINGS, couchDB_URI: "https://example.invalid", couchDB_DBNAME: "vault" };
+ const storage = {
+ normalisePath: (path: string) => path,
+ isExists: vi.fn(async (path: string) => files.has(path)),
+ readFileText: vi.fn(async (path: string) => {
+ if (!files.has(path)) throw new Error("missing file");
+ return files.get(path)!;
+ }),
+ writeFileAuto: vi.fn(async (path: string, value: string) => {
+ files.set(path, value);
+ return true;
+ }),
+ delete: vi.fn(async (path: string) => {
+ files.delete(path);
+ }),
+ };
+ const confirm = vi.fn(async (_title: string, _message: string, buttons: string[]) => buttons[1]);
+ const host = {
+ services: { setting: { currentSettings: () => settings }, UI: { confirm: { confirmWithMessage: confirm } } },
+ serviceModules: { storageAccess: storage },
+ } as any;
+ return { host, files, settings, storage, confirm };
+}
+
+describe("Fetch flag state", () => {
+ it("preserves Markdown and reuses the selection independently of the database suffix", async () => {
+ const { host, files, settings } = fixture();
+ await saveFetchSelection(host, stage1, stage2);
+ const prose = "# My recovery notes\n\nKeep this explanation.\n\n";
+ files.set(modern, prose + files.get(modern)!);
+ await saveFetchSelection(host, stage1, stage2);
+ expect(files.get(modern)).toContain(prose);
+ settings.additionalSuffixOfDatabaseName = "changed-by-rebuild";
+ await expect(readFetchSelection(host)).resolves.toMatchObject({ stage1, stage2 });
+ await clearFetchSelection(host);
+ expect(files.get(modern)).toContain(prose);
+ expect(readFetchFlagState(files.get(modern)!)).toBeUndefined();
+ });
+
+ it("reads and updates the legacy filename when it is the only flag", async () => {
+ const { host, files } = fixture();
+ files.delete(modern);
+ files.set(legacy, "");
+ await saveFetchSelection(host, stage1, stage2);
+ await expect(readFetchSelection(host)).resolves.toMatchObject({ stage1, stage2 });
+ expect(files.has(modern)).toBe(false);
+ await deleteFetchFlags(host);
+ expect(files.size).toBe(0);
+ });
+
+ it("treats a new empty canonical flag as a new operation even when a legacy selection remains", async () => {
+ const { host, files, confirm } = fixture();
+ await saveFetchSelection(host, stage1, stage2);
+ files.set(legacy, files.get(modern)!);
+ files.set(modern, "");
+ await askSimpleFetchMode(host);
+ expect(confirm).toHaveBeenCalledTimes(2);
+ await deleteFetchFlags(host);
+ expect(files.size).toBe(0);
+ });
+
+ it("does not fall back to the legacy flag when reading the canonical flag fails", async () => {
+ const { host, files, storage } = fixture();
+ files.set(legacy, "");
+ storage.readFileText.mockRejectedValueOnce(new Error("read failed"));
+ await expect(readFetchSelection(host)).rejects.toThrow("read failed");
+ expect(storage.readFileText).toHaveBeenCalledOnce();
+ });
+
+ it("does not create an unsolicited fetch flag when none exists", async () => {
+ const { host, files, storage } = fixture();
+ files.clear();
+ await expect(saveFetchSelection(host, stage1, stage2)).rejects.toThrow("flag is missing");
+ expect(storage.writeFileAuto).not.toHaveBeenCalled();
+ });
+
+ it("rejects a save when the storage silently retains different content", async () => {
+ const { host, storage } = fixture();
+ storage.writeFileAuto.mockResolvedValueOnce(true);
+ await expect(saveFetchSelection(host, stage1, stage2)).rejects.toThrow("could not be saved");
+ });
+
+ it("retains the authoritative flag when removing the legacy alias fails", async () => {
+ const { host, files, storage } = fixture();
+ files.set(legacy, "");
+ storage.delete.mockRejectedValueOnce(new Error("delete failed"));
+ await expect(deleteFetchFlags(host)).rejects.toThrow("delete failed");
+ expect(files.has(modern)).toBe(true);
+ expect(storage.delete).toHaveBeenCalledOnce();
+ });
+
+ it("detects a deletion which returned without removing the flag", async () => {
+ const { host, files, storage } = fixture();
+ storage.delete.mockResolvedValueOnce(undefined);
+ await expect(deleteFetchFlags(host)).rejects.toThrow("Could not remove");
+ expect(files.has(modern)).toBe(true);
+ });
+
+ it.each([
+ { stage1: "unknown", stage2 },
+ { stage1, stage2: "Delete local files if deleted on remote" },
+ { stage1: "Use the detailed flow", stage2 },
+ ])("asks again for an invalid combination: %j", async (choices) => {
+ const { host, files, storage, confirm } = fixture();
+ files.set(
+ modern,
+ writeFetchFlagState("", {
+ type: "livesync-fast-setup",
+ version: 1,
+ ...choices,
+ })
+ );
+ const original = files.get(modern);
+ confirm.mockImplementationOnce(async (_title, _message, buttons) => buttons[0]);
+ await askSimpleFetchMode(host);
+ expect(confirm).toHaveBeenCalledTimes(3);
+ expect(files.get(modern)).toBe(original);
+ expect(storage.writeFileAuto).not.toHaveBeenCalled();
+ });
+
+ it.each([
+ { remoteType: "" as const, couchDB_DBNAME: "different-vault" },
+ { remoteType: REMOTE_MINIO, endpoint: "https://s3.invalid", bucket: "other-bucket" },
+ { remoteType: REMOTE_P2P, P2P_roomID: "other-room" },
+ ])("requires explicit confirmation for the currently selected remote: %j", async (remote) => {
+ const { host, settings, confirm } = fixture();
+ await saveFetchSelection(host, stage1, stage2);
+ Object.assign(settings, remote);
+ await expect(readFetchSelection(host)).resolves.toMatchObject({ stage1, stage2 });
+ confirm.mockImplementationOnce(async (_title, _message, buttons) => buttons[0]);
+ await askSimpleFetchMode(host);
+ expect(confirm).toHaveBeenCalledOnce();
+ expect(confirm.mock.calls[0][1]).toContain("Current remote:");
+ });
+});
+
+describe("Fetch destination description", () => {
+ it("omits URL credentials, query parameters, fragments, and connection secrets", () => {
+ const { settings } = fixture();
+ Object.assign(settings, {
+ couchDB_URI: "https://alice:secret@example.invalid/path?token=secret#secret",
+ couchDB_USER: "alice",
+ couchDB_PASSWORD: "secret",
+ jwtKey: "secret",
+ passphrase: "secret",
+ additionalSuffixOfDatabaseName: "another-device",
+ });
+ expect(describeFetchRemote(settings)).toBe("CouchDB / https://example.invalid/path / vault");
+ });
+
+ it("shows Object Storage prefixes and P2P rooms without relay credentials", () => {
+ const { settings } = fixture();
+ Object.assign(settings, {
+ remoteType: REMOTE_MINIO,
+ endpoint: "https://s3.invalid",
+ region: "region",
+ bucket: "vault",
+ bucketPrefix: "one/",
+ });
+ expect(describeFetchRemote(settings)).toContain("vault / one/");
+ Object.assign(settings, {
+ remoteType: REMOTE_P2P,
+ P2P_roomID: "group",
+ P2P_passphrase: "secret",
+ P2P_relays: "wss://alice:secret@relay.invalid?token=secret,wss://other.invalid",
+ });
+ const description = describeFetchRemote(settings);
+ expect(description).toContain("group");
+ expect(description).toContain("wss://relay.invalid");
+ expect(description).toContain("wss://other.invalid");
+ expect(description).not.toContain("secret");
+ expect(description).not.toContain("alice");
+ });
+});
diff --git a/src/serviceFeatures/redFlag.fetchStateMarkdown.ts b/src/serviceFeatures/redFlag.fetchStateMarkdown.ts
new file mode 100644
index 00000000..5d7e9400
--- /dev/null
+++ b/src/serviceFeatures/redFlag.fetchStateMarkdown.ts
@@ -0,0 +1,131 @@
+import MarkdownIt from "markdown-it";
+
+export interface FetchFlagState {
+ type: "livesync-fast-setup";
+ version: 1;
+ stage1: string;
+ stage2: string;
+}
+
+const STATE_TYPE = "livesync-fast-setup";
+const markdownParser = new MarkdownIt({ html: true });
+
+type MarkdownStateBlock = {
+ start: number;
+ end: number;
+ state: FetchFlagState | undefined;
+};
+
+function parseFetchFlagState(value: unknown): FetchFlagState | undefined {
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return undefined;
+ const record = value as Record;
+ if (
+ record.type !== STATE_TYPE ||
+ record.version !== 1 ||
+ typeof record.stage1 !== "string" ||
+ typeof record.stage2 !== "string"
+ ) {
+ return undefined;
+ }
+ return {
+ type: STATE_TYPE,
+ version: 1,
+ stage1: record.stage1,
+ stage2: record.stage2,
+ };
+}
+
+function getLineStartOffsets(markdown: string): number[] {
+ const starts = [0];
+ for (let index = 0; index < markdown.length; index++) {
+ if (markdown[index] === "\r") {
+ if (markdown[index + 1] === "\n") index++;
+ starts.push(index + 1);
+ } else if (markdown[index] === "\n") {
+ starts.push(index + 1);
+ }
+ }
+ return starts;
+}
+
+function isFenceClosed(markdown: string, map: [number, number] | null, markup: string): boolean {
+ if (!map) return false;
+ const closingLine = markdown.split(/\r\n|\n|\r/)[map[1] - 1] ?? "";
+ const closing = /^ {0,3}(`+|~+)[\t ]*$/.exec(closingLine);
+ return closing !== null && closing[1][0] === markup[0] && closing[1].length >= markup.length;
+}
+
+function getStateBlocks(markdown: string): MarkdownStateBlock[] | undefined {
+ const tokens = markdownParser.parse(markdown, {});
+ const topLevelFences = tokens.filter((token) => token.type === "fence" && token.level === 0);
+ if (topLevelFences.some((token) => !isFenceClosed(markdown, token.map, token.markup))) return undefined;
+
+ const starts = getLineStartOffsets(markdown);
+ const blocks: MarkdownStateBlock[] = [];
+ for (const token of topLevelFences) {
+ if (token.info.trim() !== "json") continue;
+ let parsed: unknown;
+ try {
+ parsed = JSON.parse(token.content);
+ } catch {
+ return undefined;
+ }
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) continue;
+ const record = parsed as Record;
+ if (record.type !== STATE_TYPE) continue;
+
+ if (!token.map) return undefined;
+ const start = starts[token.map[0]];
+ const end = starts[token.map[1]] ?? markdown.length;
+ if (start === undefined || end < start) return undefined;
+ blocks.push({ start, end, state: parseFetchFlagState(parsed) });
+ }
+ return blocks;
+}
+
+function getLineEnding(markdown: string): string {
+ return markdown.match(/\r\n|\n|\r/)?.[0] ?? "\n";
+}
+
+function formatStateBlock(state: FetchFlagState, lineEnding: string): string {
+ const json = JSON.stringify(
+ {
+ type: state.type,
+ version: state.version,
+ stage1: state.stage1,
+ stage2: state.stage2,
+ },
+ null,
+ 2
+ );
+ return `\`\`\`json${lineEnding}${json}${lineEnding}\`\`\``;
+}
+
+function createFastSetupNote(state: FetchFlagState): string {
+ return [
+ "# Fast Setup",
+ "",
+ "This file keeps the Fast Setup choices while the fetch is incomplete. It is removed when the fetch completes.",
+ "",
+ formatStateBlock(state, "\n"),
+ ].join("\n");
+}
+
+export function readFetchFlagState(markdown: string): FetchFlagState | undefined {
+ const blocks = getStateBlocks(markdown);
+ if (!blocks || blocks.length !== 1) return undefined;
+ return blocks[0].state;
+}
+
+export function writeFetchFlagState(markdown: string, state: FetchFlagState | undefined): string {
+ if (markdown.trim().length === 0) return state ? createFastSetupNote(state) : markdown;
+
+ const blocks = getStateBlocks(markdown);
+ if (!blocks || blocks.length !== 1 || !blocks[0].state) return markdown;
+
+ const block = blocks[0];
+ const originalBlock = markdown.slice(block.start, block.end);
+ const trailingLineEnding = originalBlock.match(/(?:\r\n|\n|\r)$/)?.[0] ?? "";
+ const replacement = state ? formatStateBlock(state, getLineEnding(markdown)) + trailingLineEnding : "";
+ return markdown.slice(0, block.start) + replacement + markdown.slice(block.end);
+}
diff --git a/src/serviceFeatures/redFlag.fetchStateMarkdown.unit.spec.ts b/src/serviceFeatures/redFlag.fetchStateMarkdown.unit.spec.ts
new file mode 100644
index 00000000..05beee10
--- /dev/null
+++ b/src/serviceFeatures/redFlag.fetchStateMarkdown.unit.spec.ts
@@ -0,0 +1,147 @@
+import { describe, expect, it } from "vitest";
+import { readFetchFlagState, writeFetchFlagState, type FetchFlagState } from "./redFlag.fetchStateMarkdown";
+
+const state: FetchFlagState = {
+ type: "livesync-fast-setup",
+ version: 1,
+ stage1: "remote-only",
+ stage2: "keep-local",
+};
+
+function stateBlock(value: unknown = state): string {
+ return `\`\`\`json\n${JSON.stringify(value, null, 2)}\n\`\`\``;
+}
+
+describe("Fast Setup Markdown state codec", () => {
+ it("reads a valid top-level JSON state fence", () => {
+ expect(readFetchFlagState(stateBlock())).toEqual(state);
+ });
+
+ it("ignores a legacy remote field", () => {
+ expect(readFetchFlagState(stateBlock({ ...state, remote: "old-remote-hash" }))).toEqual(state);
+ });
+
+ it("creates a Fast Setup note only for an empty or whitespace-only document", () => {
+ const markdown = writeFetchFlagState("", state);
+
+ expect(markdown).toContain("# Fast Setup");
+ expect(markdown).toContain("This file keeps the Fast Setup choices while the fetch is incomplete.");
+ expect(markdown).toContain("It is removed when the fetch completes.");
+ expect(readFetchFlagState(markdown)).toEqual(state);
+
+ const whitespace = " \n\t";
+ expect(readFetchFlagState(writeFetchFlagState(whitespace, state))).toEqual(state);
+ expect(writeFetchFlagState(whitespace, undefined)).toBe(whitespace);
+ });
+
+ it("updates one readable state while preserving prose and unrelated code fences", () => {
+ const unrelatedFence = '```json\n{\n "type": "example"\n}\n```';
+ const markdown = `Opening note.\n\n${unrelatedFence}\n\n${stateBlock()}\n\nClosing note.`;
+ const updated = { ...state, stage1: "new-choice" };
+
+ const result = writeFetchFlagState(markdown, updated);
+
+ expect(result).toContain("Opening note.");
+ expect(result).toContain(unrelatedFence);
+ expect(result).toContain("Closing note.");
+ expect(readFetchFlagState(result)).toEqual(updated);
+ });
+
+ it("clears one readable state while preserving surrounding prose", () => {
+ const markdown = `Before.\n${stateBlock()}\nAfter.`;
+
+ expect(writeFetchFlagState(markdown, undefined)).toBe("Before.\nAfter.");
+ });
+
+ it("does not read or write nested code examples", () => {
+ const example = [
+ "> Example state:",
+ "> ```json",
+ `> ${JSON.stringify(state)}`,
+ "> ```",
+ "",
+ "````markdown",
+ "```json",
+ JSON.stringify(state),
+ "```",
+ "````",
+ ].join("\n");
+
+ expect(readFetchFlagState(example)).toBeUndefined();
+ expect(writeFetchFlagState(example, state)).toBe(example);
+ expect(writeFetchFlagState(example, undefined)).toBe(example);
+ });
+
+ it("does not change plain prose, malformed JSON, or an unsupported version", () => {
+ const malformed = '```json\n{"type":"livesync-fast-setup",\n```';
+ const unsupportedVersion = stateBlock({ ...state, version: 2 });
+
+ for (const markdown of ["Ordinary prose only.", malformed, unsupportedVersion]) {
+ expect(readFetchFlagState(markdown)).toBeUndefined();
+ expect(writeFetchFlagState(markdown, state)).toBe(markdown);
+ expect(writeFetchFlagState(markdown, undefined)).toBe(markdown);
+ }
+ });
+
+ it("does not change duplicate state candidates", () => {
+ const markdown = `Start.\n\n${stateBlock()}\n\n${stateBlock({ ...state, stage1: "second" })}\n\nEnd.`;
+ expect(readFetchFlagState(markdown)).toBeUndefined();
+ expect(writeFetchFlagState(markdown, state)).toBe(markdown);
+ expect(writeFetchFlagState(markdown, undefined)).toBe(markdown);
+ });
+
+ it("does not repair malformed marker blocks", () => {
+ const malformed = [
+ "Before.",
+ "",
+ "```json",
+ "{",
+ ' "type": "livesync-fast-setup",',
+ ' "version": 1,',
+ ' "remote":',
+ "}",
+ "```",
+ "",
+ "After.",
+ ].join("\n");
+
+ expect(readFetchFlagState(malformed)).toBeUndefined();
+ expect(writeFetchFlagState(malformed, state)).toBe(malformed);
+ expect(writeFetchFlagState(malformed, undefined)).toBe(malformed);
+ });
+
+ it("does not write state into malformed Markdown without a readable state block", () => {
+ const markdown = "Opening note.\n\n```text\nThis note remains inside an unclosed fence.";
+
+ expect(readFetchFlagState(markdown)).toBeUndefined();
+ expect(writeFetchFlagState(markdown, state)).toBe(markdown);
+ });
+
+ it("does not write when a state block is inside an unclosed fence", () => {
+ const markdown = `Opening note.\n\n\`\`\`json\n${JSON.stringify(state)}`;
+
+ expect(readFetchFlagState(markdown)).toBeUndefined();
+ expect(writeFetchFlagState(markdown, state)).toBe(markdown);
+ });
+
+ it("does not write into unclosed raw HTML comments and blocks", () => {
+ const markdownExamples = [
+ "Opening note.\n\n